Community Store Category Issue
May 15, 2025 · AI Conversations
"No pages have been used to categorise products"
> **"No pages have been used to categorise products"**
in **Community Store for Concrete CMS** typically means that no product categories have been set up using **Concrete pages**, which Community Store uses to organize products (each category is a **Page** under a designated "Product Parent Page").
### ✅ Here's how to fix it:
—
### **Step 1: Create a Category Page Structure**
1. Go to your **Sitemap** (`/dashboard/sitemap/full`).
2. Find or create a parent page like:
– `/products`
– `/shop`
– or `/store`
3. Under that parent, **add subpages** for each category (e.g., “Tiles”, “Accessories”).
– Use the **Page Type**: `store_product_list` (or similar depending on your theme/setup).
– These subpages will be used as categories.
—
### **Step 2: Assign the Category Page Path in Settings**
1. Go to **Dashboard > Store > Settings**.
2. Under **Product List Page**, select the **parent page** you created (`/products`, etc.).
– This tells Community Store where to look for category subpages.
3. Save settings.
—
### **Step 3: Verify Page Type of Subpages**
Ensure that your category pages are:
– Not of type “External Link”
– Not system pages
– Have proper permissions
If unsure:
1. Go to **each category page** in sitemap.
2. Click **Edit Page**, then **Page Settings**.
3. Confirm page type is a standard or `store_product_list`.
—
### ✅ After this:
When editing a **product**, you should now see those category pages listed under the "Product Category" dropdown or multiselect in the product form.
—
Let me know if you want to auto-create some category pages via a script or dashboard page.
Assuming you're using the default `CommunityStoreProduct` entity or table, here's how you can fetch the min and max prices.
—
### ✅ **Method 1: Direct SQL Query via DB**
“`php
use Core
col-pPrice
then add product range slider in add.php bootstrap
—
## ✅ 1. **Get Min and Max from `CommunityStoreProducts.pPrice`**
use Database;
$db = Database::connection();
$row = $db->fetchAssociative("SELECT MIN(pPrice) AS min_price, MAX(pPrice) AS max_price FROM CommunityStoreProducts");
$minPrice = $row['min_price'] ?? 0;
$maxPrice = $row['max_price'] ?? 0;
You can now use `$minPrice` and `$maxPrice` to set up a range slider.
—
## ✅ 2. **Add Range Slider in `add.php` using Bootstrap**
<div class="form-group">
<label for="priceRange">Price Range</label>
<div>
<input type="range" class="form-range" min="<?= $minPrice ?>" max="<?= $maxPrice ?>" id="priceRange" name="settings[price_range]" step="1"
oninput="document.getElementById('priceRangeValue').innerText = this.value">
<small>Selected: <span id="priceRangeValue"><?= $minPrice ?></span></small>
</div>
</div>
### 🔁 To preserve value when editing:
If you're passing `settings['price_range']`:
$value = $settings['price_range'] ?? $minPrice;
Then update the slider like:
<input type="range" class="form-range" min="<?= $minPrice ?>" max="<?= $maxPrice ?>"
id="priceRange" name="settings[price_range]" value="<?= $value ?>"
oninput="document.getElementById('priceRangeValue').innerText = this.value">
<small>Selected: <span id="priceRangeValue"><?= $value ?></span></small>
—
Let me know if you'd like to allow **both min and max range selection** (dual-range slider) instead of a single-value slider.
—
### ✅ Full Code for Single Range Slider with Min & Max Display
<?php
use Database;
$db = Database::connection();
$row = $db->fetchAssociative("SELECT MIN(pPrice) AS min_price, MAX(pPrice) AS max_price FROM CommunityStoreProducts");
$minPrice = $row['min_price'] ?? 0;
$maxPrice = $row['max_price'] ?? 0;
$value = $settings['price_range'] ?? $minPrice;
?>
<div class="form-group">
<label for="priceRange">Select Price</label>
<div class="d-flex justify-content-between mb-1">
<span>Min: ₹<?= number_format($minPrice, 2) ?></span>
<span>Max: ₹<?= number_format($maxPrice, 2) ?></span>
</div>
<input type="range" class="form-range" id="priceRange" name="settings[price_range]"
min="<?= $minPrice ?>" max="<?= $maxPrice ?>" step="1" value="<?= $value ?>"
oninput="document.getElementById('priceRangeValue').innerText = this.value">
<small>Selected Price: ₹<span id="priceRangeValue"><?= $value ?></span></small>
</div>
—
### 🧪 Output Example
– Min: ₹100
– Max: ₹1000
– Slider with selected value: ₹300
—
Let me know if you want a **dual-range slider** (for both min & max inputs), or to **save both min and max price range settings**.
—
### ✅ Location of Currency Setting:
– **Table**: `Config`
– **Key**: `community_store.currency`
—
### 🧾 Example SQL to Get Currency:
SELECT value FROM Config WHERE configNamespace = 'community_store' AND configKey = 'currency';
Or in PHP:
use ConcreteCoreSupportFacadeConfig;
$currency = Config::get('community_store.currency'); // e.g., "USD", "EUR", "INR"
—
### 🪙 Related Currency Display Settings:
You might also want:
– `community_store.currency_symbol`
– `community_store.currency_symbol_position` (e.g., "before", "after")
These are also stored in the `Config` table.
$symbol = Config::get('community_store.currency_symbol');
$position = Config::get('community_store.currency_symbol_position'); // before or after
—
Let me know if you want to display prices with symbol formatting based on those settings.
and in db save both min and max based on the slider
—
### ✅ 1. **Store both Min and Max in DB**
You can save in `settings['min_price']` and `settings['max_price']` when saving the form:
$minSelected = $this->post('settings')['min_price'];
$maxSelected = $this->post('settings')['max_price'];
Store them as JSON or in separate DB columns, depending on your setup.
—
### ✅ 2. **HTML & JS for Dual Range Slider in `add.php` (Bootstrap Compatible)**
<?php
use Database;
$db = Database::connection();
$row = $db->fetchAssociative("SELECT MIN(pPrice) AS min_price, MAX(pPrice) AS max_price FROM CommunityStoreProducts");
$minPrice = $row['min_price'] ?? 0;
$maxPrice = $row['max_price'] ?? 1000;
$selectedMin = $settings['min_price'] ?? $minPrice;
$selectedMax = $settings['max_price'] ?? $maxPrice;
?>
<div class="form-group">
<label>Price Range</label>
<div class="d-flex justify-content-between mb-1">
<span>Min: ₹<?= number_format($minPrice, 2) ?></span>
<span>Max: ₹<?= number_format($maxPrice, 2) ?></span>
</div>
<div class="position-relative" style="height: 40px;">
<input type="range" name="settings[min_price]" class="form-range position-absolute w-100"
min="<?= $minPrice ?>" max="<?= $maxPrice ?>" step="1"
value="<?= $selectedMin ?>" id="minRange"
oninput="updateSliderValues()">
<input type="range" name="settings[max_price]" class="form-range position-absolute w-100"
min="<?= $minPrice ?>" max="<?= $maxPrice ?>" step="1"
value="<?= $selectedMax ?>" id="maxRange"
oninput="updateSliderValues()">
</div>
<div class="mt-2">
<small>Selected Range: ₹<span id="minValue"><?= $selectedMin ?></span> – ₹<span id="maxValue"><?= $selectedMax ?></span></small>
</div>
</div>
<script>
function updateSliderValues() {
const minSlider = document.getElementById('minRange');
const maxSlider = document.getElementById('maxRange');
let min = parseInt(minSlider.value);
let max = parseInt(maxSlider.value);
if (min > max) {
// swap values to prevent cross-over
[min, max] = [max, min];
minSlider.value = min;
maxSlider.value = max;
}
document.getElementById('minValue').innerText = min;
document.getElementById('maxValue').innerText = max;
}
document.addEventListener('DOMContentLoaded', updateSliderValues);
</script>
—
### ✅ 3. **On Save (Controller Logic)**
$minSelected = $this->post('settings')['min_price'] ?? 0;
$maxSelected = $this->post('settings')['max_price'] ?? 0;
$db->insert('shoppingfeedconfiguration', [
'url' => $url,
'name' => $name,
'settings' => json_encode([
'min_price' => $minSelected,
'max_price' => $maxSelected
])
]);
You can also store them in two separate columns if needed.
—
Let me know if you want a prettier UI (like jQuery UI slider or noUiSlider), or price formatting with currency symbol too.
– Two `input type="range"` sliders are stacked **exactly on top of each other**.
– Without custom styling, one overlaps the other, so only the topmost one (e.g. `maxRange`) is visible/interactable.
—
### ✅ Fix: Style them so both thumbs are visible
Here’s a **working and styled solution** to make both range thumbs visible:
—
### ✅ Updated Dual Range Slider Code (Pure HTML + CSS + JS):
<?php
use Database;
$db = Database::connection();
$row = $db->fetchAssociative("SELECT MIN(pPrice) AS min_price, MAX(pPrice) AS max_price FROM CommunityStoreProducts");
$minPrice = $row['min_price'] ?? 0;
$maxPrice = $row['max_price'] ?? 1000;
$selectedMin = $settings['min_price'] ?? $minPrice;
$selectedMax = $settings['max_price'] ?? $maxPrice;
?>
<style>
.range-container {
position: relative;
width: 100%;
height: 50px;
}
.range-container input[type=range] {
position: absolute;
width: 100%;
pointer-events: none; /* disable default drag */
-webkit-appearance: none;
background: transparent;
}
.range-container input[type=range]::-webkit-slider-thumb {
pointer-events: all;
width: 20px;
height: 20px;
background: #007bff;
border-radius: 50%;
border: none;
-webkit-appearance: none;
}
.range-container input[type=range]::-moz-range-thumb {
pointer-events: all;
width: 20px;
height: 20px;
background: #007bff;
border-radius: 50%;
border: none;
}
</style>
<div class="form-group">
<label>Price Range</label>
<div class="d-flex justify-content-between mb-1">
<span>Min: ₹<?= number_format($minPrice, 2) ?></span>
<span>Max: ₹<?= number_format($maxPrice, 2) ?></span>
</div>
<div class="range-container">
<input type="range" id="minRange" name="settings[min_price]"
min="<?= $minPrice ?>" max="<?= $maxPrice ?>" value="<?= $selectedMin ?>" step="1"
oninput="handleRangeSlide()">
<input type="range" id="maxRange" name="settings[max_price]"
min="<?= $minPrice ?>" max="<?= $maxPrice ?>" value="<?= $selectedMax ?>" step="1"
oninput="handleRangeSlide()">
</div>
<div class="mt-2">
<small>Selected: ₹<span id="minValue"><?= $selectedMin ?></span> – ₹<span id="maxValue"><?= $selectedMax ?></span></small>
</div>
</div>
<script>
function handleRangeSlide() {
const minSlider = document.getElementById('minRange');
const maxSlider = document.getElementById('maxRange');
let minVal = parseInt(minSlider.value);
let maxVal = parseInt(maxSlider.value);
if (minVal > maxVal) {
[minVal, maxVal] = [maxVal, minVal];
minSlider.value = minVal;
maxSlider.value = maxVal;
}
document.getElementById('minValue').textContent = minVal;
document.getElementById('maxValue').textContent = maxVal;
}
document.addEventListener('DOMContentLoaded', handleRangeSlide);
</script>
—
### ✅ Notes:
– **`pointer-events: none`** disables default interaction on the range bar.
– **Thumbs are restyled** and **pointer-events re-enabled** on them, so both can be dragged.
– This solution works without any external library.
—
Let me know if you want a **more interactive visual bar fill** (e.g., colored progress between the thumbs).
> You're using two overlapping “ elements, and by default each range slider renders its own track, but they overlap and don't "merge" into one visual track.
—
### ✅ Solution: Add a **custom background track** that visually fills between the min and max sliders.
We’ll create a fake "track fill" using a `div` positioned underneath the thumbs, then dynamically set its left and width based on the slider values.
—
### ✅ Full Working Code with Track Fill (Grey Line)
<?php
use Database;
$db = Database::connection();
$row = $db->fetchAssociative("SELECT MIN(pPrice) AS min_price, MAX(pPrice) AS max_price FROM CommunityStoreProducts");
$minPrice = $row['min_price'] ?? 0;
$maxPrice = $row['max_price'] ?? 1000;
$selectedMin = $settings['min_price'] ?? $minPrice;
$selectedMax = $settings['max_price'] ?? $maxPrice;
$range = $maxPrice - $minPrice;
?>
<style>
.range-container {
position: relative;
width: 100%;
height: 50px;
}
.range-track {
position: absolute;
height: 6px;
background: #ccc;
top: 50%;
transform: translateY(-50%);
width: 100%;
border-radius: 3px;
z-index: 1;
}
.range-fill {
position: absolute;
height: 6px;
background: #007bff;
top: 50%;
transform: translateY(-50%);
border-radius: 3px;
z-index: 2;
}
.range-container input[type=range] {
position: absolute;
width: 100%;
pointer-events: none;
-webkit-appearance: none;
background: transparent;
z-index: 3;
}
.range-container input[type=range]::-webkit-slider-thumb {
pointer-events: all;
width: 20px;
height: 20px;
background: #007bff;
border-radius: 50%;
border: none;
-webkit-appearance: none;
}
.range-container input[type=range]::-moz-range-thumb {
pointer-events: all;
width: 20px;
height: 20px;
background: #007bff;
border-radius: 50%;
border: none;
}
</style>
<div class="form-group">
<label>Price Range</label>
<div class="d-flex justify-content-between mb-1">
<span>Min: ₹<?= number_format($minPrice, 2) ?></span>
<span>Max: ₹<?= number_format($maxPrice, 2) ?></span>
</div>
<div class="range-container">
<div class="range-track"></div>
<div class="range-fill" id="rangeFill"></div>
<input type="range" id="minRange" name="settings[min_price]"
min="<?= $minPrice ?>" max="<?= $maxPrice ?>" value="<?= $selectedMin ?>" step="1"
oninput="handleRangeSlide()">
<input type="range" id="maxRange" name="settings[max_price]"
min="<?= $minPrice ?>" max="<?= $maxPrice ?>" value="<?= $selectedMax ?>" step="1"
oninput="handleRangeSlide()">
</div>
<div class="mt-2">
<small>Selected: ₹<span id="minValue"><?= $selectedMin ?></span> – ₹<span id="maxValue"><?= $selectedMax ?></span></small>
</div>
</div>
<script>
function handleRangeSlide() {
const minSlider = document.getElementById('minRange');
const maxSlider = document.getElementById('maxRange');
const min = parseInt(minSlider.value);
const max = parseInt(maxSlider.value);
const fill = document.getElementById('rangeFill');
const minValue = Math.min(min, max);
const maxValue = Math.max(min, max);
const total = <?= $range ?>;
const minPercent = ((minValue - <?= $minPrice ?>) / total) * 100;
const maxPercent = ((maxValue - <?= $minPrice ?>) / total) * 100;
fill.style.left = minPercent + "%";
fill.style.width = (maxPercent - minPercent) + "%";
document.getElementById('minValue').textContent = minValue;
document.getElementById('maxValue').textContent = maxValue;
}
document.addEventListener('DOMContentLoaded', handleRangeSlide);
</script>
—
### ✅ What This Does:
– Adds a `range-track` (gray line behind sliders)
– Adds a `range-fill` (blue line between thumbs)
– Dynamically adjusts its position/width based on current slider values
—
Let me know if you want to add:
– Tooltips above thumbs
– Touch/mobile support improvements
– jQuery/nouislider version with drag-combined behavior
– **Left slider thumb** (Min) to be locked to the **minimum price**.
– **Right slider thumb** (Max) to be locked to the **maximum price**.
In other words:
– **Min slider default position** → at `minPrice`
– **Max slider default position** → at `maxPrice`
—
### ✅ Code Update
In the PHP section of your slider:
$selectedMin = $minPrice;
$selectedMax = $maxPrice;
So full code update (only this section changes):
$minPrice = $row['min_price'] ?? 0;
$maxPrice = $row['max_price'] ?? 1000;
$selectedMin = $minPrice; // always start at min
$selectedMax = $maxPrice; // always start at max
—
Now your sliders will load like this:
– 👈 Left thumb starts at `minPrice`
– 👉 Right thumb starts at `maxPrice`
—
✅ This will reflect in:
– Slider positions
– The blue fill (`range-fill`) line
– Displayed prices below the slider
Let me know if you want:
– Limits (e.g., min must always be ≤ max)
– Tooltips above the thumbs
– Save as default values in DB when editing feed again