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:
@@ -30,13 +30,13 @@
|
||||
<div class="panel-container show">
|
||||
<div class="panel-content">
|
||||
<table class="table table-striped table-bordered table-hover mb-0">
|
||||
<thead><tr><th>ID</th><th>Country</th><th>City</th><th>District</th><th>Categories</th><th>Products</th><th>Actions</th></tr></thead>
|
||||
<thead><tr><th>ID</th><th>Country</th><th>City</th><th>District</th><th>Categories</th><th>Products</th><th>Status</th><th>Actions</th></tr></thead>
|
||||
<tbody>
|
||||
<% if (locations.length === 0) { %>
|
||||
<tr><td colspan="7" class="text-muted">No locations</td></tr>
|
||||
<tr><td colspan="8" class="text-muted">No locations</td></tr>
|
||||
<% } else { %>
|
||||
<% locations.forEach(function(l) { %>
|
||||
<tr>
|
||||
<tr<%= l.is_active === 0 ? ' class="table-secondary"' : '' %>>
|
||||
<td><%= l.id %></td>
|
||||
<td><%= l.country %></td>
|
||||
<td><%= l.city %></td>
|
||||
@@ -44,6 +44,28 @@
|
||||
<td><%= l.category_count || 0 %></td>
|
||||
<td><%= l.product_count || 0 %></td>
|
||||
<td>
|
||||
<% if (l.is_active === 1) { %>
|
||||
<span class="badge bg-success">Active</span>
|
||||
<% } else { %>
|
||||
<span class="badge bg-secondary">Disabled</span>
|
||||
<% } %>
|
||||
</td>
|
||||
<td>
|
||||
<form method="POST" action="/locations/<%= l.id %>/update" class="d-inline-flex gap-1 align-items-center mb-1">
|
||||
<input type="hidden" name="_csrf" value="<%= csrfToken %>">
|
||||
<input name="country" class="form-control form-control-sm" value="<%= l.country %>" required size="8" style="width:80px">
|
||||
<input name="city" class="form-control form-control-sm" value="<%= l.city %>" required size="8" style="width:80px">
|
||||
<input name="district" class="form-control form-control-sm" value="<%= l.district || '' %>" size="8" style="width:80px">
|
||||
<button class="btn btn-sm btn-primary">Save</button>
|
||||
</form>
|
||||
<form method="POST" action="/locations/<%= l.id %>/toggle" class="d-inline" onsubmit="<%= l.is_active === 1 ? "return confirm('Disable this location?')" : '' %>">
|
||||
<input type="hidden" name="_csrf" value="<%= csrfToken %>">
|
||||
<% if (l.is_active === 1) { %>
|
||||
<button class="btn btn-sm btn-warning">Disable</button>
|
||||
<% } else { %>
|
||||
<button class="btn btn-sm btn-success">Enable</button>
|
||||
<% } %>
|
||||
</form>
|
||||
<form method="POST" action="/locations/<%= l.id %>/delete" class="d-inline" onsubmit="return confirm('Delete location?')">
|
||||
<input type="hidden" name="_csrf" value="<%= csrfToken %>">
|
||||
<button class="btn btn-sm btn-danger">Delete</button>
|
||||
|
||||
Reference in New Issue
Block a user