feat(loyalty): cross-persona page alignment with shared components

Align loyalty pages across admin, merchant, and store personas so each
sees the same page set scoped to their access level. Admin acts as a
superset of merchant with "on behalf" capabilities.

New pages:
- Store: Staff PINs management (CRUD)
- Merchant: Cards, Card Detail, Transactions, Staff PINs (CRUD), Settings (read-only)
- Admin: Merchant Cards, Card Detail, Transactions, PINs (read-only)

Architecture:
- 4 shared Jinja2 partials (cards-list, card-detail, transactions, pins)
- 4 shared JS factory modules parameterized by apiPrefix/scope
- Persona templates are thin wrappers including shared partials
- PinDetailResponse schema for cross-store PIN listings

API: 17 new endpoints (11 merchant, 6 admin on-behalf)
Tests: 38 new integration tests, arch-check green
i18n: ~130 new keys across en/fr/de/lb
Docs: pages-and-navigation.md with full page matrix

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-22 19:28:07 +01:00
parent f41f72b86f
commit 6161d69ba2
49 changed files with 4385 additions and 14 deletions

View File

@@ -137,7 +137,11 @@ loyalty_module = ModuleDefinition(
],
FrontendType.MERCHANT: [
"loyalty-program", # Merchant loyalty program
"loyalty-cards", # Customer cards
"loyalty-analytics", # Merchant loyalty analytics
"loyalty-transactions", # Transaction feed
"loyalty-pins", # Staff PINs
"loyalty-settings", # Settings (read-only)
],
},
# New module-driven menu definitions
@@ -197,6 +201,14 @@ loyalty_module = ModuleDefinition(
order=20,
requires_permission="loyalty.view_programs",
),
MenuItemDefinition(
id="pins",
label_key="loyalty.menu.staff_pins",
icon="key",
route="/store/{store_code}/loyalty/pins",
order=22,
requires_permission="loyalty.view_programs",
),
MenuItemDefinition(
id="loyalty-program",
label_key="loyalty.menu.program",
@@ -230,6 +242,13 @@ loyalty_module = ModuleDefinition(
route="/merchants/loyalty/program",
order=10,
),
MenuItemDefinition(
id="loyalty-cards",
label_key="loyalty.menu.customer_cards",
icon="identification",
route="/merchants/loyalty/cards",
order=15,
),
MenuItemDefinition(
id="loyalty-analytics",
label_key="loyalty.menu.analytics",
@@ -237,6 +256,27 @@ loyalty_module = ModuleDefinition(
route="/merchants/loyalty/analytics",
order=20,
),
MenuItemDefinition(
id="loyalty-transactions",
label_key="loyalty.menu.transactions",
icon="clock",
route="/merchants/loyalty/transactions",
order=25,
),
MenuItemDefinition(
id="loyalty-pins",
label_key="loyalty.menu.staff_pins",
icon="key",
route="/merchants/loyalty/pins",
order=30,
),
MenuItemDefinition(
id="loyalty-settings",
label_key="loyalty.menu.settings",
icon="cog",
route="/merchants/loyalty/settings",
order=35,
),
],
),
],