feat(roles): add admin store roles page, permission i18n, and menu integration
Some checks failed
CI / ruff (push) Successful in 9s
CI / validate (push) Has been cancelled
CI / dependency-scanning (push) Has been cancelled
CI / docs (push) Has been cancelled
CI / deploy (push) Has been cancelled
CI / pytest (push) Has started running

- Add admin store roles page with merchant→store cascading for superadmin
  and store-only selection for platform admin
- Add permission catalog API with translated labels/descriptions (en/fr/de/lb)
- Add permission translations to all 15 module locale files (60 files total)
- Add info icon tooltips for permission descriptions in role editor
- Add store roles menu item and admin menu item in module definition
- Fix store-selector.js URL construction bug when apiEndpoint has query params
- Add admin store roles API (CRUD + platform scoping)
- Add integration tests for admin store roles and permission catalog

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-26 23:31:27 +01:00
parent 2b55e7458b
commit f95db7c0b1
83 changed files with 3491 additions and 513 deletions

View File

@@ -27,7 +27,7 @@ function storeRoles() {
showRoleModal: false,
editingRole: null,
roleForm: { name: '', permissions: [] },
permissionsByCategory: {},
permissionCategories: [],
presetRoles: ['manager', 'staff', 'support', 'viewer', 'marketing'],
async init() {
@@ -50,40 +50,14 @@ function storeRoles() {
async loadPermissions() {
try {
// Group known permissions by category prefix
const allPerms = window.USER_PERMISSIONS || [];
this.permissionsByCategory = this.groupPermissions(allPerms);
const response = await apiClient.get('/store/team/permissions/catalog');
this.permissionCategories = response.categories || [];
storeRolesLog.info('Loaded permission catalog:', this.permissionCategories.length, 'categories');
} catch (e) {
storeRolesLog.warn('Could not load permission categories:', e);
storeRolesLog.warn('Could not load permission catalog:', e);
}
},
groupPermissions(permIds) {
// Known permission categories from the codebase
const knownPerms = [
'dashboard.view',
'settings.view', 'settings.edit', 'settings.theme', 'settings.domains',
'products.view', 'products.create', 'products.edit', 'products.delete', 'products.import', 'products.export',
'orders.view', 'orders.edit', 'orders.cancel', 'orders.refund',
'customers.view', 'customers.edit', 'customers.delete', 'customers.export',
'stock.view', 'stock.edit', 'stock.transfer',
'team.view', 'team.invite', 'team.edit', 'team.remove',
'analytics.view', 'analytics.export',
'messaging.view_messages', 'messaging.send_messages', 'messaging.manage_templates',
'billing.view_tiers', 'billing.manage_tiers', 'billing.view_subscriptions', 'billing.manage_subscriptions', 'billing.view_invoices',
'cms.view_pages', 'cms.manage_pages', 'cms.view_media', 'cms.manage_media', 'cms.manage_themes',
'loyalty.view_programs', 'loyalty.manage_programs', 'loyalty.view_rewards', 'loyalty.manage_rewards',
'cart.view', 'cart.manage',
];
const groups = {};
for (const perm of knownPerms) {
const cat = perm.split('.')[0];
if (!groups[cat]) groups[cat] = [];
groups[cat].push({ id: perm });
}
return groups;
},
async loadRoles() {
this.loading = true;
this.error = false;
@@ -127,7 +101,7 @@ function storeRoles() {
},
toggleCategory(category) {
const perms = this.permissionsByCategory[category] || [];
const perms = category.permissions || [];
const permIds = perms.map(p => p.id);
const allSelected = permIds.every(id => this.roleForm.permissions.includes(id));
if (allSelected) {
@@ -142,7 +116,7 @@ function storeRoles() {
},
isCategoryFullySelected(category) {
const perms = this.permissionsByCategory[category] || [];
const perms = category.permissions || [];
return perms.length > 0 && perms.every(p => this.roleForm.permissions.includes(p.id));
},