feat(admin): full CRUD + enable/disable for locations, categories, subcategories
- Migration 011: add is_active INTEGER NOT NULL DEFAULT 1 to locations, categories, subcategories (idempotent) - Admin routes: locations edit + toggle, categories toggle, subcategories edit + toggle (parallel endpoints on /catalog) - Bot-side services: filter is_active=1 on locationService/categoryService read methods (getLocationById/getCategoryById left unfiltered for purchase display) - Views: edit forms, toggle buttons, Active/Disabled badges, disabled row styling in locations.ejs, categories.ejs, catalog tree - Add Categories nav item in sidebar (folder icon) - Tests: 14 new tests for migration idempotency + service is_active filtering (23 total pass)
This commit is contained in:
@@ -5,7 +5,7 @@ class CategoryService {
|
||||
static async getCategoriesByLocationId(locationId) {
|
||||
try {
|
||||
const categories = await db.allAsync(
|
||||
'SELECT * FROM categories WHERE location_id = ?',
|
||||
'SELECT * FROM categories WHERE location_id = ? AND is_active = 1',
|
||||
[locationId]
|
||||
);
|
||||
return categories;
|
||||
@@ -17,7 +17,7 @@ class CategoryService {
|
||||
|
||||
static async getSubcategoriesByCategoryId(categoryId) {
|
||||
return await db.allAsync(
|
||||
'SELECT id, name FROM subcategories WHERE category_id = ? ORDER BY name',
|
||||
'SELECT id, name FROM subcategories WHERE category_id = ? AND is_active = 1 ORDER BY name',
|
||||
[categoryId]
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,26 +3,26 @@ import logger from "../utils/logger.js";
|
||||
|
||||
class LocationService {
|
||||
static async getCountries() {
|
||||
return await db.allAsync('SELECT DISTINCT country FROM locations ORDER BY country');
|
||||
return await db.allAsync('SELECT DISTINCT country FROM locations WHERE is_active = 1 ORDER BY country');
|
||||
}
|
||||
|
||||
static async getCitiesByCountry(country) {
|
||||
return await db.allAsync(
|
||||
'SELECT DISTINCT city FROM locations WHERE country = ? ORDER BY city',
|
||||
'SELECT DISTINCT city FROM locations WHERE country = ? AND is_active = 1 ORDER BY city',
|
||||
[country]
|
||||
);
|
||||
}
|
||||
|
||||
static async getDistrictsByCountryAndCity(country, city) {
|
||||
return await db.allAsync(
|
||||
'SELECT id, district FROM locations WHERE country = ? AND city = ? ORDER BY district',
|
||||
'SELECT id, district FROM locations WHERE country = ? AND city = ? AND is_active = 1 ORDER BY district',
|
||||
[country, city]
|
||||
);
|
||||
}
|
||||
|
||||
static async getLocationsByCountryAndCity(country, city) {
|
||||
return await db.allAsync(
|
||||
'SELECT id, country, city, district FROM locations WHERE country = ? AND city = ? ORDER BY district',
|
||||
'SELECT id, country, city, district FROM locations WHERE country = ? AND city = ? AND is_active = 1 ORDER BY district',
|
||||
[country, city]
|
||||
);
|
||||
}
|
||||
@@ -30,7 +30,7 @@ class LocationService {
|
||||
static async getLocation(country, city, district) {
|
||||
try {
|
||||
const location = await db.getAsync(
|
||||
'SELECT * FROM locations WHERE country = ? AND city = ? AND district = ?',
|
||||
'SELECT * FROM locations WHERE country = ? AND city = ? AND district = ? AND is_active = 1',
|
||||
[country, city, district]
|
||||
);
|
||||
return location;
|
||||
|
||||
Reference in New Issue
Block a user