Back-office
Information architecture, page templates, global elements and the index of every page of the HOCO back-office dashboard.
The back-office (@hoco/dashboard, Next.js) is where HOCO staff run the business: CRM, field operations, catalog and stock, orders and delivery, finance, promotions, support, reports and administration (cahier §4). Anis builds it: DSH-01 to DSH-13 for V1, with returns and after-sales in DSH-13; V2 pages in DSH-20 and DSH-21. Akram designs it on days 15 to 19 (DES-15 to DES-19), and the V2 surfaces later in DES-30.
Most of the 71 pages are built from five page templates that Akram defines on day 4 (DES-04). Only 21 pages are bespoke: maps, boards, planners, the 360° sheet, editors with a live preview, and the permissions matrix. This page holds what all pages share. Each section page then specifies its screens one by one: layout, data, actions, rules, real-time behaviour, states and acceptance criteria.
Product decisions of 17 Oct 2026 that shape these pages
- No order is confirmed automatically. Every order (app, web, assisted, phone) waits in Commandes à confirmer (BO-41) until a person confirms it with
orders.validate(ORD-14). The queue has a sidebar badge and a dashboard card, both updated live. - Three selling prices per variant, one per customer type (Super-gros, Demi-gros, Détail). They replace V1 price lists and quantity tiers. A reseller's customer type is declared at sign-up, confirmed at validation and only changed by the back-office (PRC-01, AUTH-11).
- Stock quantities exist only in the back-office. Resellers and guests only ever see En stock or En rupture (STK-07).
- After-sales (SAV) and returns are V1. Claims are tickets resolved by replacement, return, credit note, repair or rejection. Return reception restocks automatically and issues a credit note (SUP-04, ORD-13).
- Guests browse the reseller app and web without prices. Promotions and banners targeted to « Tous » are shown to them; targeted ones are not (AUTH-10, BO-50).
- Per-category attributes feed the reseller filters (BO-71). Support regions carry the WhatsApp numbers (BO-70).
How to read the page specs
Every screen has a fields table (template, release, design day, build task, requirements, permissions), then Purpose, Who uses it, Entry points, Layout, Data, Actions, Rules, Real-time, States and Acceptance criteria. UI copy is French, as shipped in V1. Status labels and tones come from enums.json. Permission keys follow <module>.<action> (permissions matrix). Anything marked Assumption still has to be confirmed.
Who uses the back-office
Six internal roles use it (cahier §3, ACL-01). Delegates and resellers have no back-office access. Sign-in (BO-01) refuses them and points them to their app. A staff user can hold several roles, and their effective permissions are the union of those roles.
| Role | Key | Back-office scope (cahier §3) | Usually starts the day on |
|---|---|---|---|
| Super administrateur | SUPER_ADMIN | Everything, plus global settings, roles, permissions and audit | Tableau de bord, Administration |
| Administrateur commercial | COMMERCIAL_ADMIN | CRM, resellers, delegates, tours, promotions, orders, reporting | Commandes à confirmer, Comptes à valider, Suivi terrain |
| Responsable stock | STOCK_MANAGER | Products, categories, variants, quantities, inventories, alerts | Tableau de bord stock, État du stock |
| Responsable finance | FINANCE_MANAGER | Payments, credit, invoices, due dates, financial reports | Tableau de bord finance, Paiements à vérifier |
| Responsable livraison | DELIVERY_MANAGER | Preparation, shipping, carrier assignment, proof of delivery | Livraisons (board) |
| Direction | DIRECTION | Read-only dashboards, KPIs and consolidated reports | Tableau de bord, Rapports |
Information architecture
Sidebar
Navigation comes from permissions, not from roles (ACL-05). An item appears when the user holds the permission of its main read operation. A group appears when at least one of its items does. If HOCO edits the matrix (BO-62), the navigation follows without any code change. V2 items stay hidden until their release flag is on (they are never shown greyed out). Routes are shown without the locale segment.
| Group | Item | Screen | Route | Shown with | Badge |
|---|---|---|---|---|---|
| Pilotage | Tableau de bord | BO-04 | / | dashboard.view | — |
| Ventes | BO-05 | /dashboards/sales | dashboard.view + orders.view | — | |
| Terrain | BO-06 | /dashboards/field | dashboard.view + field.view | Delegates on tour now (4/6) | |
| Stock | BO-07 | /dashboards/stock | dashboard.view + stock.view | Out-of-stock variants | |
| Finance | BO-08 | /dashboards/finance | dashboard.view + finance.view | — | |
| CRM | Revendeurs | BO-09 (map: BO-10) | /crm/resellers | crm.view | — |
| Comptes à valider | BO-13 | /crm/validation | crm.validate | Pending accounts and points of sale | |
| Relances et tâches | BO-14 | /crm/tasks | crm.view | My overdue tasks | |
| Segments | BO-15 | /crm/segments | crm.administer | — | |
| Terrain | Délégués | BO-16 | /field/delegates | field.view | — |
| Tournées | BO-21 | /field/tours | field.view | — | |
| Visites | BO-24 | /field/visits | field.view | — | |
| Objectifs | BO-20 | /field/objectives | field.view | — | |
| Zones | BO-19 | /field/zones | field.administer | — | |
| Types de visite | BO-26 | /field/visit-types | field.administer | — | |
| Catalogue | Produits | BO-27 | /catalog/products | catalog.view | — |
| Marques | BO-29 | /catalog/brands | catalog.view | — | |
| Catégories | BO-30 | /catalog/categories | catalog.view | — | |
| Attributs et filtres | BO-71 | /catalog/attributes | catalog.view | Variants with missing required values | |
| Tarifs | BO-31 | /catalog/prices | catalog.view | — | |
| Stock | État du stock | BO-33 | /stock | stock.view | — |
| Mouvements | BO-34 | /stock/movements | stock.view | — | |
| Réception / ajustement | BO-35 | /stock/operations/new | stock.create or stock.update | — | |
| Inventaires | BO-36 | /stock/inventories | stock.update | Open sessions | |
| Entrepôts | BO-37 | /stock/warehouses | stock.administer | — | |
| Commandes | Commandes à confirmer | BO-41 | /orders/to-confirm | orders.validate | Orders SUBMITTED + PENDING, live; red once the oldest has waited over 2 h |
| Commandes | BO-38 | /orders | orders.view | — | |
| Livraisons | BO-42 | /delivery | delivery.view | To prepare | |
| Retours et SAV | BO-69 | /orders/returns | orders.view | Returns REQUESTED + returns to receive | |
| Transporteurs | BO-43 | /delivery/carriers | delivery.administer | — | |
| Finance | Factures | BO-44 | /finance/invoices | finance.view | — |
| Paiements | BO-45 | /finance/payments | finance.view | Declarations to verify | |
| Crédit revendeurs | BO-46 | /finance/credit | finance.view | — | |
| Impayés et relances (V2) | BO-47 | /finance/overdue | finance.view | — | |
| Exports comptables (V2) | BO-48 | /finance/exports | finance.export | — | |
| Promotions | Promotions | BO-49 | /promotions | promotions.view | — |
| Bannières et offres du jour | BO-51 | /promotions/banners | promotions.view | — | |
| Packs (V2) | BO-52 | /promotions/packs | promotions.view | — | |
| Fidélité (V2) | BO-53 | /loyalty/settings | loyalty.administer | — | |
| Récompenses (V2) | BO-54 | /loyalty/rewards | loyalty.view | Redemptions to fulfil | |
| Support | Tickets et SAV | BO-55 | /support/tickets | support.view | Unassigned open tickets, after-sales claims included |
| Annonces | BO-57 | /communication/announcements | communication.view | — | |
| Notifications | BO-58 | /communication/notifications | communication.view | — | |
| Rapports | Rapports | BO-59 | /reports | reports.view | — |
| Exports | BO-60 | /exports | reports.export | Running exports | |
| Administration | Utilisateurs | BO-61 | /admin/users | users.view | — |
| Rôles et permissions | BO-62 | /admin/roles | roles.view | — | |
| Journal d'activité | BO-63 | /admin/audit | audit.view | — | |
| Paramètres | BO-64 | /admin/settings | settings.view | — | |
| Listes de référence | BO-65 | /admin/reference-lists | settings.administer | — | |
| Versions des applications | BO-66 | /admin/app-versions | settings.administer | — | |
| Imports de données | BO-67 | /admin/imports | settings.administer | — | |
| Régions support et WhatsApp | BO-70 | /admin/support-regions | settings.administer | Wilayas without a region | |
| User menu | Mon profil et sessions | BO-68 | /me | any session | — |
The remaining pages have no sidebar item. They are reached from a list or a record: sign-in and reset (BO-01, BO-02), the reseller 360° and its form (BO-11, BO-12), delegate detail and form (BO-17, BO-18), the tour editor and live detail (BO-22, BO-23), the visit detail (BO-25), the product editor and import wizard (BO-28, BO-32), the order detail and order on behalf (BO-39, BO-40), the promotion editor (BO-50), the ticket detail (BO-56) and each report (BO-59 runner).
Badges read the total of the matching list call with pageSize=1. The Commandes à confirmer badge reads admin.orders.toConfirm. Badges refresh on page load, on window focus, every 60 s, and after the live events that concern them. The to-confirm badge updates within seconds of order.created and order.status_changed. Counts above 99 show 99+.
Sections visible by role
These visibilities follow from the default permission seed below. The seed is derived from cahier §3 and the role descriptions in RoleKey. The authoritative matrix is SRS › Permissions matrix, and HOCO can change it in BO-62.
| Section | Super admin | Commercial admin | Stock manager | Finance manager | Delivery manager | Direction |
|---|---|---|---|---|---|---|
| Pilotage | All five dashboards | General, sales, field, stock | General, sales, stock | General, sales, finance | General, sales, stock | All five (read-only) |
| CRM | Manage | Manage, incl. validation and segments | — | View (resellers, 360°) | — | View |
| Terrain | Manage | Manage, incl. zones and visit types | — | — | — | View |
| Catalogue | Manage | View, incl. the three selling prices (no purchase price) | Manage, incl. the three prices, purchase prices, attributes and import | View, incl. purchase prices | View | View (no purchase price) |
| Stock | Manage | View | Manage, incl. inventory validation, warehouses and return reception | — | View | View |
| Commandes | Manage | Confirm orders (to-confirm queue), refuse, modify, override; decide returns; view deliveries | View; receive returns | View | View orders; fulfilment transitions; manage deliveries and carriers | View |
| Finance | Manage | — | — | Manage, incl. credit limits and credit notes | — | View |
| Promotions | Manage | Manage, incl. activation | View | View | — | View |
| Support | Manage | Handle tickets; resolve after-sales claims; manage and send announcements | — | Handle tickets | Handle tickets | — |
| Rapports | All reports and exports | Reports of the modules it can view; exports | Same rule | Same rule | Same rule | All reports and exports |
| Administration | Manage | — | — | — | — | — |
Navigation map
Page templates
| Template | Template-kind screens | Bespoke screens built on its skeleton |
|---|---|---|
| List | BO-14, 15, 16, 20, 24, 27, 29, 30, 31, 33, 34, 36, 37, 41, 43, 44, 45, 46, 47, 48, 49, 51, 52, 54, 55, 57, 60, 61, 63, 67, 69, 70, 71 | BO-09, BO-13, BO-38 |
| Detail with tabs | BO-25, BO-56 | BO-11, BO-17, BO-23, BO-39 |
| Form / editor | BO-02, BO-12, BO-18, BO-35, BO-40 | BO-22, BO-28, BO-32, BO-50 |
| Settings | BO-26, 53, 58, 64, 65, 66, 68 | BO-62 |
| Dashboard | BO-05, BO-07, BO-08 | BO-04, BO-06, BO-59 |
The remaining bespoke screens (BO-01, BO-03, BO-10, BO-19, BO-21, BO-42) have their own layouts but reuse the shared header, filter bar and states.
Shared page anatomy
- Breadcrumb (
Commandes › CMD-2026-000158) above the page title. Next to the title: the record count for lists, status pills for records. - One primary action on the right of the header (« Nouveau produit », « Valider la commande »). Secondary actions sit in a « Plus » menu. Destructive actions always sit in the menu, never next to the primary button.
- Content width: lists and dashboards are fluid up to 1 680 px. Forms are capped at 960 px. Detail pages use a 1 fr / 360 px split with a side rail on desktop.
- Each page sets the browser title to
<page> · HOCOand has a stabledata-page="bo-38"attribute for end-to-end tests (QA-01).
List
- Header: title and
total(« 312 commandes »), the primary action, and « Exporter » / « Importer » when the module allows them. - Status tabs (saved views) when the record has a lifecycle. Each tab is a preset filter with a counter, for example « En préparation » =
status=PREPARING. The active tab is?view=<key>. - Filter bar:
- A search input (
q, debounced 300 ms, at least 2 characters) and up to four inline filters (select, multi-select, date range). - « Plus de filtres » opens a side sheet with the rest. Active filters show as removable chips, plus « Réinitialiser ».
- Every filter lives in the URL under the API filter name (
status,wilaya,commune,delegate,from,to,q; repeated parameters for multi-values). The view is shareable, back/forward works and a reload restores it. Each list's last URL is kept in session storage, so the sidebar link brings the user back to the same view. - A filter change resets
pageto 1.
- A search input (
- Table (TanStack Table):
- Sorting is server-side and on a single column (
sortandorderparameters), with an arrow on the sorted header. - « Colonnes » toggles column visibility. The choice is stored per user and per table in local storage (
bo.table.<tableId>.columns). Each spec lists the default-visible columns. - The identifier column is pinned. Numbers are right-aligned in tabular figures. Statuses render as pills.
- A row click opens the record (Ctrl/Cmd-click opens a new tab). A kebab menu at the end of the row holds row actions.
- A density toggle (« Confortable » / « Compacte ») is stored like the column choice.
- Sorting is server-side and on a single column (
- Selection and bulk actions: a checkbox column. Once rows are selected, a sticky bar appears (« 12 sélectionnés » plus actions).
- Selection is limited to the current page.
- When no bulk operation exists, the action runs item by item with an
Idempotency-Keyeach. A progress dialog ends with a per-item report (done / failed with the error message). A partial result is never silent.
- Pagination:
pageandpageSize(25 by default; 25, 50 or 100), « 1–25 sur 312 », plus first, previous, next and last. - Export: « Exporter » offers XLSX, CSV and PDF. It exports the current filters and visible columns as an async job (BO-60), with the toast « Export en préparation — il apparaîtra dans le centre d'exports ». The button only shows with the module's
exportpermission (ANA-05). - States: see States.
Drawer or page?
- A drawer (right sheet, 560 px; full width on tablet portrait) edits small records without sub-collections (about 12 fields or fewer): brands, categories, attribute definitions, warehouses, carriers, segments, tasks, users, reference items, support regions, banners, announcements, payment recording, credit-limit changes. The drawer is addressable (
?drawer=newor?drawer=<id>), so links and the back button work, and it guards unsaved changes on close. - A page edits records with sub-collections, tabs, maps or long forms: reseller, delegate, product, tour, promotion, order on behalf, stock operation.
- A read-only quick view drawer previews a record without leaving the list (stock movements of a variant, an invoice, a delivery card).
Detail with tabs
- Header summary:
- The business number in monospace (copy on click), the name, and one status pill per lifecycle (for example the order status plus the assisted-confirmation status).
- A row of five to seven key facts.
- Primary actions computed by the server: detail responses carry
allowedActions, and the UI renders exactly those, never re-deriving transitions. - On scroll, the header condenses and stays sticky.
- Tabs: addressable with
?tab=<key>. Each tab loads lazily, has its own loading, empty and error states, and shows a counter when the detail response provides one. - Side rail (desktop at 1 280 px and wider): summary cards. Below that width they move under the main content.
- History tab: who, when, what (before → after), reason. System actions read « Système ».
- Concurrency: records carry a
versionthat mutations send back. A409 VERSION_CONFLICTopens « Cette fiche a été modifiée par Karim B. à 10:42 » with « Recharger », and the user's unsaved input stays in the form.
Form / editor
- Sections with a title and a two-column field grid on desktop (one column on tablet). Labels sit above fields; a required field is marked with « * ». Help text sits under the field, and the error replaces it in the danger tone.
- Validation:
- The client validates with the same Zod schemas as the contract (
packages/contracts), on blur and on submit. - Server
VALIDATION_FAILEDerrors map onto fields throughdetails.fields[].path. - The first error gets focus and is scrolled into view. With more than three errors, a summary banner lists them.
- The client validates with the same Zod schemas as the contract (
- Save bar: a sticky bottom bar appears as soon as the form is dirty (« Modifications non enregistrées » · « Annuler » · « Enregistrer »). Ctrl/Cmd+S saves.
- Leaving a dirty form asks « Quitter sans enregistrer ? ».
- While a save is pending, the button shows a spinner and is disabled. Form saves are never optimistic.
- Inputs:
- Money is typed in DA with two decimals (
1 250,00) and sent as integer centimes. - Percentages are typed in % and sent as basis points.
- Phones accept the national format (
0555 12 34 56) and are sent in E.164. - Dates are entered and shown in
Africa/Algiers.
- Money is typed in DA with two decimals (
- Files:
- Drag-and-drop zones check type and size per
FilePurposebefore upload. - Upload runs
files.createUpload, thenPUT, thenfiles.completeUpload, with per-file progress and retry.
- Drag-and-drop zones check type and size per
- Permission-gated fields:
- Without the view permission, the field is not rendered.
- With view but not update, it is read-only, with a lock icon and the tooltip « Modification réservée aux profils autorisés ».
Settings
- An anchor navigation on the left lists the groups. Each group is a card with its own « Enregistrer », so saves are independent and a mistake stays contained. Each card shows « Modifié par … le … ».
- Every change is validated by the server and audited (
UPDATEonSettingwith before/after). - A change with operational impact (disabling a payment method, raising a minimum app version, changing the GPS tolerance) asks for confirmation. The dialog states the impact in one sentence.
- A user with only the view permission sees the same page, read-only.
Dashboard
- Filter bar at the top (period plus dimensions), synced to the URL. « Mis à jour à 09:42 » sits next to a refresh button.
- KPI cards on a 12-column grid: four per row on desktop, two on tablet, six on large screens.
- A card shows a label, the value, its unit, a comparison (Δ % with an arrow, toned by meaning: up is good for revenue, bad for unpaid amounts), an optional sparkline, and an ⓘ tooltip with the exact definition.
- A click opens the matching list with the same filters.
- Charts: line, bar or donut, with legend and tooltips. A « Voir les données » toggle swaps the chart for a table (accessibility, NFR-07).
- Card states:
- Loading: a skeleton.
- Empty: « Aucune donnée sur la période ».
- Error: the card alone shows the error; the page keeps working.
- Not permitted: the card is not rendered.
- Layout comes from
admin.dashboard.layout.get(DashboardLayout, per role). Holders ofsettings.administercan edit it. - Refresh: on relevant live events (debounced 10 s), every 5 minutes and on window focus (ANA-02).
States
| State | Pattern |
|---|---|
| Loading | First load: skeletons that match the final layout. Refetch (filters, pagination): the previous data stays and a thin progress bar runs at the top. No full-page spinners. |
| Empty, first use | Illustration, one sentence on what the page is for, the primary action (« Créer la première promotion »). |
| Empty, no result | « Aucun résultat pour ces filtres » with « Réinitialiser les filtres ». |
| Error | Inline panel in place of the content: the localised message, « Réessayer » and the copiable requestId. A failed mutation shows an error toast, and the form keeps its input. |
| Permission denied | A direct URL without the permission shows a full-page « Accès refusé » with a link back to the dashboard. A 403 PERMISSION_DENIED on a mutation shows a toast, then me.get is refreshed and the action disappears. |
| Not found | « Introuvable » page, also for records outside the user's scope (the API answers 404 rather than leak that they exist). |
| Session expired | 401 UNAUTHENTICATED redirects to BO-01 with ?next= (the current path). |
| Maintenance | 503 MAINTENANCE shows a full-page message with details.until. |
| Offline | Browser offline: the banner « Connexion perdue — les actions sont désactivées » appears and mutations are disabled. |
| Real-time lost | Live stream down for more than 30 s: the status dot turns amber with « Mises à jour en direct interrompues ». Data still refreshes on focus. |
Global elements
App shell and top bar
The shell (BO-03) has:
- A sidebar: 256 px wide, collapsible to a 72 px icon rail, with the HOCO logo, groups, items and badges.
- A 56 px top bar with:
- the global search field (Ctrl K);
- the live-status dot;
- the notifications bell with its unread badge;
- the user menu.
Global search
- One field, « Rechercher un revendeur, une commande, un produit, une facture… », opened with Ctrl K (⌘K on macOS), calling
admin.search. - Results are grouped (Revendeurs, Commandes, Produits, Factures), with at most five per group, and filtered by permission on the server: a user without
finance.viewnever gets the Factures group. « Voir tous les résultats » opens the group's list withq=. - Matching:
- An exact business number (
CMD-2026-000158,REV-000123,FAC-2026-000123) is the first result, and Enter opens it. - Phones are normalised (
0555123456finds+213555123456). - Products match by name, reference or barcode.
- An exact business number (
- Mechanics: at least 2 characters, debounced 250 ms. Keyboard: ↑ ↓ Enter Esc. The last five searches are stored locally.
Notifications drawer
- The bell opens a 400 px right drawer (NOT-01), fed by
notifications.listwith cursor pagination. - Items: tabs « Toutes » / « Non lues ». Each item has an icon for its
NotificationCategory, a title, two lines of body, a relative time and an unread dot.- Clicking an item marks it read (
notifications.markRead, optimistic) and opens its deep link in the back-office. - « Tout marquer comme lu » calls
notifications.markAllRead.
- Clicking an item marks it read (
- Badge (
notifications.unreadCount) refreshes on focus, every 60 s, and after each live event. - Staff typically receive assigned tasks and tickets, accounts to validate, declarations to verify and stock alerts. The notifications matrix is authoritative.
Live events and toasts
- Connection: the shell opens one Server-Sent Events connection per tab, after sign-in, to
admin.events.stream(SYN-07). It closes on sign-out. - Reconnection: exponential backoff (1 s up to 30 s) with
Last-Event-ID. When the server can no longer replay the gap, every visible query is invalidated. - Filtering: the server filters events by permission and scope. The client maps each event to TanStack Query invalidations in one central handler table.
| Event | Received with | Toast | Refreshes |
|---|---|---|---|
order.created | orders.view | With orders.validate: « Nouvelle commande à confirmer · CMD-2026-000158 · Boutique El Amel · 45 600,00 DA » with « Confirmer » (opens it in BO-41) | The to-confirm queue (BO-41) and its badge, order lists, BO-04 Commandes à confirmer card, BO-04 / BO-05 order cards |
order.status_changed | orders.view or delivery.view | Only on the record the user has open, as a banner: « Cette commande vient de passer à Validée (par Samir K.) » | BO-41 (the order leaves the queue once confirmed, refused or cancelled), BO-38, BO-39, BO-42, BO-69, dashboards, the reseller 360° orders tab |
account.pending | crm.validate | « Nouveau compte à valider : Boutique Nour (Oran) » | BO-13 queue, sidebar badge |
stock.alert | stock.view | « Stock bas : Câble X14 Type-C 1 m — 8 disponibles » | BO-07, BO-33, BO-04 stock cards |
tour.started, tour.closed | field.view | tour.closed only: « Tournée TRN-2026-000045 clôturée par Yacine M. » | BO-06, BO-21, BO-23, BO-04 field cards |
visit.checked_in, visit.completed | field.view | None | BO-06, BO-23, BO-24, BO-25, reseller 360° visits tab |
delegate.position | field.view | None | BO-06 map markers, BO-23 marker and trail |
Retours et SAV (BO-69) is live: it listens to return.requested and return.status_changed (contract 1.1.0). The ticket conversation (BO-56) still polls every 60 s while visible: the stream has ticket.created but no reply event. On the reseller side, every status change the back-office makes reaches the reseller live through events.stream and as a push and in-app notification (ORD-15).
Toast rules:
- At most three toasts stack. They dismiss after 6 s (error toasts stay until closed) and announce through
aria-live="polite". - Bursts are grouped: « 5 nouvelles commandes ».
- No toast for the user's own actions (
actor= me). - Each event category can be muted from the user menu (« Alertes en direct »). The choice is stored in the browser.
User menu
- Header: name, email and role labels.
- « Mon profil et sessions » (BO-68).
- « Alertes en direct » (per-category toggles).
- « Langue »: Français. العربية and English appear once enabled (NFR-06).
- « Se déconnecter » (
auth.logout): it closes the event stream and returns to BO-01.
Confirmations, reasons and audit
Dialog anatomy:
- The title is a question naming the record: « Refuser la commande CMD-2026-000158 ? ».
- The body states the consequences (stock, notification, irreversibility).
- Required inputs follow (reason, comment).
- The primary button repeats the verb (« Refuser la commande », never « OK »), in the danger tone when destructive. The secondary button is « Annuler ».
- Enter never confirms a destructive dialog.
Re-authentication. Sensitive actions ask for the password again when the last authentication is more than 15 minutes old. They are: role permissions, user status, credit limits, business settings, app-version policy. Assumption — the list and the delay are a decision to confirm (open decisions).
Reasons come from the reference lists (ReferenceItem.listKey) when one exists. Otherwise they are free text of at least 10 characters. An item whose label is « Autre » requires a comment.
| Action | Reason input | Source | Audit action |
|---|---|---|---|
| Refuse an order | Select + comment | ORDER_REFUSAL_REASON | REJECT |
| Cancel an order | Select + comment | ORDER_CANCEL_REASON | STATUS_CHANGE |
| Validate an order despite a failed control | Free text, required | — | VALIDATE + OVERRIDE |
| Modify order lines | Free text, required | — | UPDATE |
| Reject a payment declaration | Select + comment | PAYMENT_REJECT_REASON | REJECT |
| Reject an account or a new point of sale | Free text, required; « Doublon de REV-… » shortcut | — (Assumption: no reference list) | REJECT |
| Change a reseller's customer type after validation | Free text, required | — | UPDATE (before → after) |
| Reject a return request | Free text, required (shown to the reseller) | — | REJECT |
| Reject an after-sales claim | Free text, required (shown to the reseller) | — | REJECT |
| Refuse quantities at return reception | Free text per line | — | UPDATE |
| Change a reseller's status | Free text, required | — | STATUS_CHANGE |
| Cancel a tour, reopen a tour, unlock a visit | Free text, required | — | STATUS_CHANGE / UPDATE |
| Adjust stock | Free text, required per operation | — | CREATE (movement) |
| Change a credit limit | Free text, required | — | UPDATE |
| Change role permissions | Free text, optional | — | PERMISSION_CHANGE |
Visit not-done reasons (VISIT_NOT_DONE_REASON) and tour suspension reasons (TOUR_SUSPEND_REASON) are entered in the delegate app. The back-office only displays them and filters on them.
Breakpoints
The back-office must work on desktop, tablet and large screens (cahier §9, NFR-05). Akram designs at 1 440 px and checks 1 024 px, 768 px and 1 920 px.
| Width | Target | Behaviour |
|---|---|---|
| 1 920 px and wider | Large screen | Sidebar expanded. Six KPI cards per row. Tables show a few more default columns. BO-06 (live map) and BO-42 (delivery board) offer a full-screen « Mode écran » for a wall display. |
| 1 280 – 1 919 px | Desktop | Sidebar expanded (256 px). Side rails visible. Four KPI cards per row. |
| 768 – 1 279 px | Tablet (landscape and portrait) | Sidebar collapsed to icons; it overlays the content when opened. Side rails move under the content. Tables scroll horizontally with the first column pinned. Drawers are full-width in portrait. Touch targets are at least 44 px. Every drag-and-drop has a menu alternative (« Déplacer vers… »). |
| Under 768 px | Not a design target | Single column. The banner « Le back-office est conçu pour tablette et ordinateur » appears. Nothing is blocked. |
Formatting and language
- Money:
1 250,00 DA(narrow no-break space for thousands, comma for decimals). Show HT / TTC explicitly wherever both exist. - Dates and times:
- Shown as
03/11/2026and09:12inAfrica/Algiers. A tooltip gives the full timestamp. - Times within the last 24 h are relative (« il y a 5 min »).
- Periods such as « aujourd'hui » and « ce mois » are business days in Algiers.
- Shown as
- Phones: shown in national format (
0555 12 34 56), linked astel:and as WhatsApp (https://wa.me/213555123456). - Measures: distances
850 m/12,4 km; percentages12,5 %. - Status labels: the label is the enum's
frand the pill colour is itstone. - Languages: French in V1. Layouts use logical CSS properties (
ms-*,pe-*,start/end) so Arabic (RTL) can be switched on later. No text is baked into images (NFR-06). - Accessibility: WCAG 2.1 AA contrast. Focus stays visible. Every action is reachable by keyboard, including table rows, dialogs and drag-and-drop alternatives. Charts have a data-table view (NFR-07).
Contract and data notes raised by these specs
Writing the page specs turned up gaps in the endpoint catalogue. These go to the API contract in SRV-03 and to the module tasks:
- Missing list operations:
- Credit accounts (BO-46) need
GET /admin/credit-accounts(finance.view). - Import history (BO-32, BO-67) needs
GET /admin/imports(job type permission).
- Credit accounts (BO-46) need
- Import job permission:
admin.imports.getis declared withsettings.administer, but product imports are started withcatalog.administer. The job should be readable with the permission of its type. - Option lists for pickers: the CRUD groups for segments, zones, warehouses, carriers and visit types gate
GETwith*.administer. Pickers on other pages (filters, forms) must be able to read them with the module'sview. - List exports:
admin.exports.createis declared withreports.export. List exports must also check the module'sexportpermission (orders.export,stock.export…). - Delivery failure:
DeliveryStatus.FAILEDhas no operation. Proposal:admin.deliveries.updateacceptsstatus: FAILEDwith a reason, plus a retry. - Fulfilment rights:
admin.orders.transitionrequiresorders.update, the same permission as line modification and cancellation. Proposal: also acceptdelivery.updatefor the PREPARING → READY → SHIPPED → DELIVERED / RETURNED transitions, and droporders.updatefrom the delivery manager. - Order list filters:
admin.orders.listneedscontrols(blocking / warning / ok) andfirstOrderfilters (BO-38).admin.orders.toConfirmneedsstatus,channel,assistedConfirmationandcontrolsfilters (BO-41). - KPI inputs:
- Orders need
validatedAt,validatedByand a snapshot of the attributed delegate (the source delegate for assisted orders, else the reseller's assigned delegate at checkout), so that revenue by delegate stays stable after a reassignment (BO-04). - The confirmation delay (
validatedAt − submittedAt) feeds the Commandes à confirmer card.
- Orders need
- Staff-facing label:
TicketStatus.WAITING_RESELLERis labelled « En attente de votre réponse » (reseller wording). The back-office shows « En attente du revendeur ». - Promotion preview:
admin.promotions.previewshould return the competing promotions and why each one was excluded (BO-50). - Pricing operations to retire:
admin.variants.tiersandadmin.priceLists.crud/admin.priceLists.itemsare still listed as V1. Under §1b, quantity tiers are dropped and price lists move to V2 (PRC-03).- BO-31 in V1 is the three-price grid. It saves through
admin.variants.crud. - A bulk price operation (
PUT /admin/variants/prices,catalog.update) would save a whole grid in one call.
- BO-31 in V1 is the three-price grid. It saves through
- Support and returns events: the admin stream has no
ticket.created,ticket.repliedorreturn.requestedevent. Adding them would make the Tickets et SAV and Retours et SAV badges live like the orders queue. - After-sales claim items:
admin.tickets.getmust return the claimed lines (TicketItem: order line, quantity,AfterSalesIssue, photos) and the resulting replacement order, return or credit note (BO-56). - Putting an order on hold: BO-39 and BO-41 offer « Mettre en attente » (
SUBMITTED→PENDING, with a reason).admin.orders.transitionlists only fulfilment targets, so the transition table and the operation must acceptPENDING. - Refused at the door: moving a failed delivery to
RETURNED(BO-42) should create theReturnRequestserver-side (IN_TRANSIT), so that reception in BO-69 restocks like any return. - Credit notes and receipts (V1, FIN-08): the admin finance group has no credit-note list or detail. BO-44 needs
GET /admin/credit-notes(or atypefilter on invoices), and confirmed payments need a receipt PDF URL (BO-45). - Import templates: a
GET /admin/imports/{type}/template?category=operation would generate templates whose attribute columns match the category (BO-32, BO-67).
Screen index
| ID | Screen | Section | Template | Release | Design | Build |
|---|---|---|---|---|---|---|
BO-01 | Sign in | Access | bespoke | V1 | Day 15 | DSH-01 |
BO-02 | Forgot / reset password | Access | template | V1 | Day 15 | DSH-01 |
BO-03 | App shell: navigation by role, global search, notifications | Access | bespoke | V1 | Day 15 | DSH-01 |
BO-04 | General dashboard | Dashboards | bespoke | V1 | Day 15 | DSH-02 |
BO-05 | Sales dashboard | Dashboards | template | V1 | Day 15 | DSH-02 |
BO-06 | Field dashboard & live map | Dashboards | bespoke | V1 | Day 15 | DSH-08 |
BO-07 | Stock dashboard | Dashboards | template | V1 | Day 15 | DSH-05 |
BO-08 | Finance dashboard | Dashboards | template | V1 | Day 15 | DSH-07 |
BO-09 | Resellers & prospects list | CRM | bespoke | V1 | Day 16 | DSH-03 |
BO-10 | Resellers map | CRM | bespoke | V1 | Day 16 | DSH-03 |
BO-11 | Reseller 360° | CRM | bespoke | V1 | Day 16 | DSH-03 |
BO-12 | Reseller create / edit | CRM | template | V1 | Day 16 | DSH-03 |
BO-13 | Account validation queue | CRM | bespoke | V1 | Day 16 | DSH-03 |
BO-14 | Follow-ups & tasks | CRM | template | V1 | Day 16 | DSH-03 |
BO-15 | Segments | CRM | template | V1 | Day 16 | DSH-03 |
BO-16 | Delegates list | Field | template | V1 | Day 19 | DSH-08 |
BO-17 | Delegate detail & performance | Field | bespoke | V1 | Day 19 | DSH-08 |
BO-18 | Delegate create / edit | Field | template | V1 | Day 19 | DSH-08 |
BO-19 | Zones (list + map editor) | Field | bespoke | V1 | Day 19 | DSH-08 |
BO-20 | Objectives | Field | template | V1 | Day 19 | DSH-08 |
BO-21 | Tour planner (calendar) | Field | bespoke | V1 | Day 19 | DSH-08 |
BO-22 | Tour editor (stops, order, recommendation) | Field | bespoke | V1 | Day 19 | DSH-08 |
BO-23 | Tour live detail | Field | bespoke | V1 | Day 19 | DSH-08 |
BO-24 | Visits list | Field | template | V1 | Day 19 | DSH-08 |
BO-25 | Visit detail | Field | template | V1 | Day 19 | DSH-08 |
BO-26 | Visit types & checklist templates | Field | template | V1 | Day 19 | DSH-08 |
BO-27 | Products list | Catalog | template | V1 | Day 17 | DSH-04 |
BO-28 | Product editor | Catalog | bespoke | V1 | Day 17 | DSH-04 |
BO-29 | Brands | Catalog | template | V1 | Day 17 | DSH-04 |
BO-30 | Categories | Catalog | template | V1 | Day 17 | DSH-04 |
BO-31 | Price grid — three customer-type prices (bulk edit) | Catalog | template | V1 | Day 17 | DSH-04 |
BO-32 | Product import wizard | Catalog | bespoke | V1 | Day 17 | DSH-04 |
BO-33 | Stock overview | Stock | template | V1 | Day 17 | DSH-05 |
BO-34 | Stock movements journal | Stock | template | V1 | Day 17 | DSH-05 |
BO-35 | Stock receipt / adjustment | Stock | template | V1 | Day 17 | DSH-05 |
BO-36 | Inventory sessions | Stock | template | V1 | Day 17 | DSH-05 |
BO-37 | Warehouses | Stock | template | V1 | Day 17 | DSH-05 |
BO-38 | Orders list | Orders | bespoke | V1 | Day 18 | DSH-06 |
BO-39 | Order detail (controls, actions, history) | Orders | bespoke | V1 | Day 18 | DSH-06 |
BO-40 | New order on behalf of a reseller | Orders | template | V1 | Day 18 | DSH-06 |
BO-41 | Orders to confirm (new, on hold, assisted) | Orders | template | V1 | Day 18 | DSH-06 |
BO-42 | Delivery board | Orders | bespoke | V1 | Day 18 | DSH-06 |
BO-43 | Carriers | Orders | template | V1 | Day 18 | DSH-06 |
BO-44 | Invoices | Finance | template | V1 | Day 18 | DSH-07 |
BO-45 | Payments & declarations | Finance | template | V1 | Day 18 | DSH-07 |
BO-46 | Credit accounts | Finance | template | V1 | Day 18 | DSH-07 |
BO-47 | Overdue & reminders | Finance | template | V2 | DES-30 | DSH-20 |
BO-48 | Accounting exports | Finance | template | V2 | DES-30 | DSH-20 |
BO-49 | Promotions list | Promotions | template | V1 | Day 19 | DSH-09 |
BO-50 | Promotion editor | Promotions | bespoke | V1 | Day 19 | DSH-09 |
BO-51 | Banners & daily offers | Promotions | template | V1 | Day 19 | DSH-09 |
BO-52 | Packs | Promotions | template | V2 | DES-30 | DSH-21 |
BO-53 | Loyalty settings & levels | Promotions | template | V2 | DES-30 | DSH-21 |
BO-54 | Rewards & redemptions | Promotions | template | V2 | DES-30 | DSH-21 |
BO-55 | Tickets list | Support | template | V1 | Day 19 | DSH-10 |
BO-56 | Ticket detail | Support | template | V1 | Day 19 | DSH-10 |
BO-57 | Announcements | Communication | template | V1 | Day 19 | DSH-10 |
BO-58 | Notification templates & rules | Communication | template | V1 | Day 19 | DSH-10 |
BO-59 | Reports library | Reports | bespoke | V1 | Day 19 | DSH-11 |
BO-60 | Export centre | Reports | template | V1 | Day 19 | DSH-11 |
BO-61 | Users | Administration | template | V1 | Day 19 | DSH-12 |
BO-62 | Roles & permissions matrix | Administration | bespoke | V1 | Day 19 | DSH-12 |
BO-63 | Activity log | Administration | template | V1 | Day 19 | DSH-12 |
BO-64 | Business settings | Administration | template | V1 | Day 19 | DSH-12 |
BO-65 | Reference lists | Administration | template | V1 | Day 19 | DSH-12 |
BO-66 | Mobile app versions | Administration | template | V1 | Day 19 | DSH-12 |
BO-67 | Data imports | Administration | template | V1 | Day 19 | DSH-12 |
BO-68 | My profile & sessions | Administration | template | V1 | Day 19 | DSH-12 |
BO-69 | Returns & after-sales claims | Orders | template | V1 | Day 18 | DSH-13 |
BO-70 | Support regions & WhatsApp numbers | Administration | template | V1 | Day 19 | DSH-12 |
BO-71 | Product attributes (filters) per category | Catalog | template | V1 | Day 17 | DSH-04 |