Files
orion/app/modules/loyalty/docs/pages-and-navigation.md
Samir Boulahtit 6161d69ba2 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>
2026-03-22 19:28:07 +01:00

8.6 KiB

Pages & Navigation

Cross-persona page architecture for the loyalty module. Every persona sees the same set of pages but scoped to their access level.

Page Matrix

Page Admin (on behalf) Merchant Store Storefront
Program Any merchant Own Own (owner edits) Public info
Analytics Any merchant Own (multi-store) Store-scoped --
Cards/Members Any merchant Own (all stores) Store-scoped Own card
Card Detail Any card Own merchant Store-scoped Own card
Transactions Any merchant Own (store filter) Store-scoped Own history
Staff PINs Read-only Full CRUD Full CRUD --
Settings Edit Read-only -- --
Terminal -- -- POS interface --
Wallet Debug Super admin only -- -- --
Enrollment -- -- Staff-initiated Self-service

URL Patterns

Store Pages

/store/{store_code}/loyalty/terminal      # POS terminal
/store/{store_code}/loyalty/cards          # Customer cards list
/store/{store_code}/loyalty/cards/{id}     # Card detail
/store/{store_code}/loyalty/pins           # Staff PIN management
/store/{store_code}/loyalty/program        # Program view (owner: edit)
/store/{store_code}/loyalty/analytics      # Store analytics
/store/{store_code}/loyalty/enroll         # Staff-initiated enrollment

Merchant Pages

/merchants/loyalty/program                 # Program view/edit
/merchants/loyalty/cards                   # Cards across all stores
/merchants/loyalty/cards/{id}              # Card detail
/merchants/loyalty/analytics               # Merchant-wide analytics
/merchants/loyalty/transactions            # Transactions (store filter)
/merchants/loyalty/pins                    # PINs across all stores (CRUD)
/merchants/loyalty/settings                # Loyalty settings (read-only)

Admin Pages (On Behalf)

/admin/loyalty/programs                    # All programs list
/admin/loyalty/analytics                   # Platform-wide analytics
/admin/loyalty/wallet-debug                # Wallet diagnostics (super admin)
/admin/loyalty/merchants/{mid}             # Merchant detail hub
/admin/loyalty/merchants/{mid}/program     # Program edit
/admin/loyalty/merchants/{mid}/settings    # Merchant settings (edit)
/admin/loyalty/merchants/{mid}/cards       # Merchant's cards
/admin/loyalty/merchants/{mid}/cards/{cid} # Card detail
/admin/loyalty/merchants/{mid}/transactions # Merchant's transactions
/admin/loyalty/merchants/{mid}/pins        # Merchant's PINs (read-only)

Storefront Pages

/platforms/loyalty/storefront/{store_code}/loyalty/join           # Self-enrollment
/platforms/loyalty/storefront/{store_code}/loyalty/join/success   # Enrollment success
/platforms/loyalty/storefront/{store_code}/account/loyalty        # Dashboard
/platforms/loyalty/storefront/{store_code}/account/loyalty/history # History

Sidebar Navigation

Store Menu

Order Item Icon Route
10 Terminal gift /store/{code}/loyalty/terminal
20 Customer Cards identification /store/{code}/loyalty/cards
22 Staff PINs key /store/{code}/loyalty/pins
25 Program cog /store/{code}/loyalty/program
30 Analytics chart-bar /store/{code}/loyalty/analytics

All require loyalty.view_programs permission.

Merchant Menu

Order Item Icon Route
10 Program gift /merchants/loyalty/program
15 Customer Cards identification /merchants/loyalty/cards
20 Analytics chart-bar /merchants/loyalty/analytics
25 Transactions clock /merchants/loyalty/transactions
30 Staff PINs key /merchants/loyalty/pins
35 Settings cog /merchants/loyalty/settings

Admin Menu

Order Item Icon Route
10 Loyalty Programs gift /admin/loyalty/programs
20 Loyalty Analytics chart-bar /admin/loyalty/analytics
30 Wallet Debug beaker /admin/loyalty/wallet-debug (super admin)

Admin sub-pages (cards, transactions, PINs) are accessed via the merchant detail hub — no separate menu items.

API Endpoints by Persona

Store API (/api/v1/store/loyalty/)

Group Endpoints
Program GET/POST/PUT/DELETE /program, GET /stats, GET /stats/merchant
Cards GET /cards, GET/POST /cards/lookup, GET /cards/{id}, POST /cards/enroll, GET /cards/{id}/transactions, GET /transactions
Stamps POST /stamp, POST /stamp/redeem, POST /stamp/void
Points POST /points/earn, POST /points/redeem, POST /points/void, POST /cards/{id}/points/adjust
PINs GET /pins, POST /pins, PATCH /pins/{id}, DELETE /pins/{id}, POST /pins/{id}/unlock

Merchant API (/api/v1/merchants/loyalty/)

Group Endpoints
Program GET/POST/PATCH/DELETE /program, GET /stats
Cards GET /cards, GET /cards/{id}, GET /cards/{id}/transactions
Transactions GET /transactions
PINs GET /pins, POST /pins, PATCH /pins/{id}, DELETE /pins/{id}, POST /pins/{id}/unlock
Settings GET /settings
Locations GET /locations

Admin API (/api/v1/admin/loyalty/)

Group Endpoints
Programs GET /programs, GET /programs/{id}, GET /programs/{id}/stats, POST /merchants/{mid}/program, PATCH /programs/{id}, DELETE /programs/{id}, POST /programs/{id}/activate, POST /programs/{id}/deactivate
Merchant Mgmt GET /merchants/{mid}/stats, GET /merchants/{mid}/settings, PATCH /merchants/{mid}/settings
On-Behalf GET /merchants/{mid}/cards, GET /merchants/{mid}/cards/{cid}, GET /merchants/{mid}/cards/{cid}/transactions, GET /merchants/{mid}/transactions, GET /merchants/{mid}/pins (read-only), GET /merchants/{mid}/locations
Platform GET /stats, GET /wallet-status
Wallet Debug GET /debug/config, GET /debug/classes, POST /debug/classes/{pid}/create, GET /debug/cards/{cid}, POST /debug/cards/{cid}/generate-url, GET /debug/recent-enrollments (super admin)

Architecture: Shared Components

All persona pages reuse the same shared partials and JS modules to ensure consistent behavior:

Shared Templates (templates/loyalty/shared/)

Partial Variables Used By
cards-list.html cards_api_prefix, cards_base_url, show_store_filter, show_enroll_button Store, Merchant, Admin
card-detail-view.html card_detail_api_prefix, card_detail_back_url Store, Merchant, Admin
transactions-list.html transactions_api_prefix, show_store_filter Merchant, Admin
pins-list.html pins_api_prefix, show_store_filter, show_crud Store, Merchant, Admin
analytics-stats.html show_programs_card, show_locations, show_merchants_metric Store, Merchant, Admin
program-form.html show_delete, show_status Store, Merchant, Admin
program-view.html -- Store, Merchant, Admin

Shared JS Modules (static/shared/js/)

Module Factory Function Config
loyalty-cards-list.js loyaltyCardsList(config) apiPrefix, baseUrl, showStoreFilter, currentPage
loyalty-card-detail-view.js loyaltyCardDetailView(config) apiPrefix, backUrl, currentPage
loyalty-transactions-list.js loyaltyTransactionsList(config) apiPrefix, showStoreFilter, currentPage
loyalty-pins-list.js loyaltyPinsList(config) apiPrefix, showStoreFilter, showCrud, currentPage

Each persona has thin wrapper JS files that call the shared factory with persona-specific config (API prefix, base URL, currentPage).

Scoping Rules

Concern Admin Merchant Store
Data scope Any merchant (via merchant_id path param) Own merchant (from auth token) Own store (from auth token), cards visible merchant-wide
PINs Read-only across merchant CRUD across all stores CRUD for own store only
Settings Full edit Read-only view Not visible
Card operations View only View only Stamp/points/enroll/void
Program edit Any merchant Own program Owner role only

Schemas

PinDetailResponse (new)

Extends PinResponse with store context for cross-store listings:

class PinDetailResponse(PinResponse):
    store_id: int | None
    store_name: str | None

class PinDetailListResponse(BaseModel):
    pins: list[PinDetailResponse]
    total: int

Used by merchant GET /pins and admin GET /merchants/{mid}/pins endpoints. Store endpoints continue using PinResponse / PinListResponse (single-store context).