HOCO B2B
DesignScreen specificationsBack-office

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

  1. 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.
  2. 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).
  3. Stock quantities exist only in the back-office. Resellers and guests only ever see En stock or En rupture (STK-07).
  4. 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).
  5. 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).
  6. 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.

RoleKeyBack-office scope (cahier §3)Usually starts the day on
Super administrateurSUPER_ADMINEverything, plus global settings, roles, permissions and auditTableau de bord, Administration
Administrateur commercialCOMMERCIAL_ADMINCRM, resellers, delegates, tours, promotions, orders, reportingCommandes à confirmer, Comptes à valider, Suivi terrain
Responsable stockSTOCK_MANAGERProducts, categories, variants, quantities, inventories, alertsTableau de bord stock, État du stock
Responsable financeFINANCE_MANAGERPayments, credit, invoices, due dates, financial reportsTableau de bord finance, Paiements à vérifier
Responsable livraisonDELIVERY_MANAGERPreparation, shipping, carrier assignment, proof of deliveryLivraisons (board)
DirectionDIRECTIONRead-only dashboards, KPIs and consolidated reportsTableau de bord, Rapports

Information architecture

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.

GroupItemScreenRouteShown withBadge
PilotageTableau de bordBO-04/dashboard.view—
VentesBO-05/dashboards/salesdashboard.view + orders.view—
TerrainBO-06/dashboards/fielddashboard.view + field.viewDelegates on tour now (4/6)
StockBO-07/dashboards/stockdashboard.view + stock.viewOut-of-stock variants
FinanceBO-08/dashboards/financedashboard.view + finance.view—
CRMRevendeursBO-09 (map: BO-10)/crm/resellerscrm.view—
Comptes à validerBO-13/crm/validationcrm.validatePending accounts and points of sale
Relances et tâchesBO-14/crm/taskscrm.viewMy overdue tasks
SegmentsBO-15/crm/segmentscrm.administer—
TerrainDéléguésBO-16/field/delegatesfield.view—
TournéesBO-21/field/toursfield.view—
VisitesBO-24/field/visitsfield.view—
ObjectifsBO-20/field/objectivesfield.view—
ZonesBO-19/field/zonesfield.administer—
Types de visiteBO-26/field/visit-typesfield.administer—
CatalogueProduitsBO-27/catalog/productscatalog.view—
MarquesBO-29/catalog/brandscatalog.view—
CatégoriesBO-30/catalog/categoriescatalog.view—
Attributs et filtresBO-71/catalog/attributescatalog.viewVariants with missing required values
TarifsBO-31/catalog/pricescatalog.view—
StockÉtat du stockBO-33/stockstock.view—
MouvementsBO-34/stock/movementsstock.view—
Réception / ajustementBO-35/stock/operations/newstock.create or stock.update—
InventairesBO-36/stock/inventoriesstock.updateOpen sessions
EntrepôtsBO-37/stock/warehousesstock.administer—
CommandesCommandes à confirmerBO-41/orders/to-confirmorders.validateOrders SUBMITTED + PENDING, live; red once the oldest has waited over 2 h
CommandesBO-38/ordersorders.view—
LivraisonsBO-42/deliverydelivery.viewTo prepare
Retours et SAVBO-69/orders/returnsorders.viewReturns REQUESTED + returns to receive
TransporteursBO-43/delivery/carriersdelivery.administer—
FinanceFacturesBO-44/finance/invoicesfinance.view—
PaiementsBO-45/finance/paymentsfinance.viewDeclarations to verify
Crédit revendeursBO-46/finance/creditfinance.view—
Impayés et relances (V2)BO-47/finance/overduefinance.view—
Exports comptables (V2)BO-48/finance/exportsfinance.export—
PromotionsPromotionsBO-49/promotionspromotions.view—
Bannières et offres du jourBO-51/promotions/bannerspromotions.view—
Packs (V2)BO-52/promotions/packspromotions.view—
Fidélité (V2)BO-53/loyalty/settingsloyalty.administer—
Récompenses (V2)BO-54/loyalty/rewardsloyalty.viewRedemptions to fulfil
SupportTickets et SAVBO-55/support/ticketssupport.viewUnassigned open tickets, after-sales claims included
AnnoncesBO-57/communication/announcementscommunication.view—
NotificationsBO-58/communication/notificationscommunication.view—
RapportsRapportsBO-59/reportsreports.view—
ExportsBO-60/exportsreports.exportRunning exports
AdministrationUtilisateursBO-61/admin/usersusers.view—
Rôles et permissionsBO-62/admin/rolesroles.view—
Journal d'activitéBO-63/admin/auditaudit.view—
ParamètresBO-64/admin/settingssettings.view—
Listes de référenceBO-65/admin/reference-listssettings.administer—
Versions des applicationsBO-66/admin/app-versionssettings.administer—
Imports de donnéesBO-67/admin/importssettings.administer—
Régions support et WhatsAppBO-70/admin/support-regionssettings.administerWilayas without a region
User menuMon profil et sessionsBO-68/meany 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.

SectionSuper adminCommercial adminStock managerFinance managerDelivery managerDirection
PilotageAll five dashboardsGeneral, sales, field, stockGeneral, sales, stockGeneral, sales, financeGeneral, sales, stockAll five (read-only)
CRMManageManage, incl. validation and segments—View (resellers, 360°)—View
TerrainManageManage, incl. zones and visit types———View
CatalogueManageView, incl. the three selling prices (no purchase price)Manage, incl. the three prices, purchase prices, attributes and importView, incl. purchase pricesViewView (no purchase price)
StockManageViewManage, incl. inventory validation, warehouses and return reception—ViewView
CommandesManageConfirm orders (to-confirm queue), refuse, modify, override; decide returns; view deliveriesView; receive returnsViewView orders; fulfilment transitions; manage deliveries and carriersView
FinanceManage——Manage, incl. credit limits and credit notes—View
PromotionsManageManage, incl. activationViewView—View
SupportManageHandle tickets; resolve after-sales claims; manage and send announcements—Handle ticketsHandle tickets—
RapportsAll reports and exportsReports of the modules it can view; exportsSame ruleSame ruleSame ruleAll reports and exports
AdministrationManage—————

Page templates

TemplateTemplate-kind screensBespoke screens built on its skeleton
ListBO-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, 71BO-09, BO-13, BO-38
Detail with tabsBO-25, BO-56BO-11, BO-17, BO-23, BO-39
Form / editorBO-02, BO-12, BO-18, BO-35, BO-40BO-22, BO-28, BO-32, BO-50
SettingsBO-26, 53, 58, 64, 65, 66, 68BO-62
DashboardBO-05, BO-07, BO-08BO-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> · HOCO and has a stable data-page="bo-38" attribute for end-to-end tests (QA-01).

List

  1. Header: title and total (« 312 commandes »), the primary action, and « Exporter » / « Importer » when the module allows them.
  2. 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>.
  3. 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 page to 1.
  4. Table (TanStack Table):
    • Sorting is server-side and on a single column (sort and order parameters), 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.
  5. 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-Key each. A progress dialog ends with a per-item report (done / failed with the error message). A partial result is never silent.
  6. Pagination: page and pageSize (25 by default; 25, 50 or 100), « 1–25 sur 312 », plus first, previous, next and last.
  7. 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 export permission (ANA-05).
  8. 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=new or ?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 version that mutations send back. A 409 VERSION_CONFLICT opens « 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_FAILED errors map onto fields through details.fields[].path.
    • The first error gets focus and is scrolled into view. With more than three errors, a summary banner lists them.
  • 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.
  • Files:
  • 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 (UPDATE on Setting with 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 of settings.administer can edit it.
  • Refresh: on relevant live events (debounced 10 s), every 5 minutes and on window focus (ANA-02).

States

StatePattern
LoadingFirst 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 useIllustration, 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 ».
ErrorInline 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 deniedA 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 expired401 UNAUTHENTICATED redirects to BO-01 with ?next= (the current path).
Maintenance503 MAINTENANCE shows a full-page message with details.until.
OfflineBrowser offline: the banner « Connexion perdue — les actions sont désactivées » appears and mutations are disabled.
Real-time lostLive 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.
  • 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.view never gets the Factures group. « Voir tous les résultats » opens the group's list with q=.
  • Matching:
    • An exact business number (CMD-2026-000158, REV-000123, FAC-2026-000123) is the first result, and Enter opens it.
    • Phones are normalised (0555123456 finds +213555123456).
    • Products match by name, reference or barcode.
  • 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.list with 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.
  • 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.
EventReceived withToastRefreshes
order.createdorders.viewWith 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_changedorders.view or delivery.viewOnly 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.pendingcrm.validate« Nouveau compte à valider : Boutique Nour (Oran) »BO-13 queue, sidebar badge
stock.alertstock.view« Stock bas : Câble X14 Type-C 1 m — 8 disponibles »BO-07, BO-33, BO-04 stock cards
tour.started, tour.closedfield.viewtour.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.completedfield.viewNoneBO-06, BO-23, BO-24, BO-25, reseller 360° visits tab
delegate.positionfield.viewNoneBO-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.

ActionReason inputSourceAudit action
Refuse an orderSelect + commentORDER_REFUSAL_REASONREJECT
Cancel an orderSelect + commentORDER_CANCEL_REASONSTATUS_CHANGE
Validate an order despite a failed controlFree text, required—VALIDATE + OVERRIDE
Modify order linesFree text, required—UPDATE
Reject a payment declarationSelect + commentPAYMENT_REJECT_REASONREJECT
Reject an account or a new point of saleFree text, required; « Doublon de REV-… » shortcut— (Assumption: no reference list)REJECT
Change a reseller's customer type after validationFree text, required—UPDATE (before → after)
Reject a return requestFree text, required (shown to the reseller)—REJECT
Reject an after-sales claimFree text, required (shown to the reseller)—REJECT
Refuse quantities at return receptionFree text per line—UPDATE
Change a reseller's statusFree text, required—STATUS_CHANGE
Cancel a tour, reopen a tour, unlock a visitFree text, required—STATUS_CHANGE / UPDATE
Adjust stockFree text, required per operation—CREATE (movement)
Change a credit limitFree text, required—UPDATE
Change role permissionsFree 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.

WidthTargetBehaviour
1 920 px and widerLarge screenSidebar 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 pxDesktopSidebar expanded (256 px). Side rails visible. Four KPI cards per row.
768 – 1 279 pxTablet (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 pxNot a design targetSingle 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/2026 and 09:12 in Africa/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.
  • Phones: shown in national format (0555 12 34 56), linked as tel: and as WhatsApp (https://wa.me/213555123456).
  • Measures: distances 850 m / 12,4 km; percentages 12,5 %.
  • Status labels: the label is the enum's fr and the pill colour is its tone.
  • 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:

  1. 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).
  2. Import job permission: admin.imports.get is declared with settings.administer, but product imports are started with catalog.administer. The job should be readable with the permission of its type.
  3. Option lists for pickers: the CRUD groups for segments, zones, warehouses, carriers and visit types gate GET with *.administer. Pickers on other pages (filters, forms) must be able to read them with the module's view.
  4. List exports: admin.exports.create is declared with reports.export. List exports must also check the module's export permission (orders.export, stock.export…).
  5. Delivery failure: DeliveryStatus.FAILED has no operation. Proposal: admin.deliveries.update accepts status: FAILED with a reason, plus a retry.
  6. Fulfilment rights: admin.orders.transition requires orders.update, the same permission as line modification and cancellation. Proposal: also accept delivery.update for the PREPARING → READY → SHIPPED → DELIVERED / RETURNED transitions, and drop orders.update from the delivery manager.
  7. Order list filters: admin.orders.list needs controls (blocking / warning / ok) and firstOrder filters (BO-38). admin.orders.toConfirm needs status, channel, assistedConfirmation and controls filters (BO-41).
  8. KPI inputs:
    • Orders need validatedAt, validatedBy and 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.
  9. Staff-facing label: TicketStatus.WAITING_RESELLER is labelled « En attente de votre réponse » (reseller wording). The back-office shows « En attente du revendeur ».
  10. Promotion preview: admin.promotions.preview should return the competing promotions and why each one was excluded (BO-50).
  11. Pricing operations to retire: admin.variants.tiers and admin.priceLists.crud / admin.priceLists.items are 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.
  12. Support and returns events: the admin stream has no ticket.created, ticket.replied or return.requested event. Adding them would make the Tickets et SAV and Retours et SAV badges live like the orders queue.
  13. After-sales claim items: admin.tickets.get must return the claimed lines (TicketItem: order line, quantity, AfterSalesIssue, photos) and the resulting replacement order, return or credit note (BO-56).
  14. Putting an order on hold: BO-39 and BO-41 offer « Mettre en attente » (SUBMITTED → PENDING, with a reason). admin.orders.transition lists only fulfilment targets, so the transition table and the operation must accept PENDING.
  15. Refused at the door: moving a failed delivery to RETURNED (BO-42) should create the ReturnRequest server-side (IN_TRANSIT), so that reception in BO-69 restocks like any return.
  16. 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 a type filter on invoices), and confirmed payments need a receipt PDF URL (BO-45).
  17. 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

IDScreenSectionTemplateReleaseDesignBuild
BO-01Sign inAccessbespokeV1Day 15DSH-01
BO-02Forgot / reset passwordAccesstemplateV1Day 15DSH-01
BO-03App shell: navigation by role, global search, notificationsAccessbespokeV1Day 15DSH-01
BO-04General dashboardDashboardsbespokeV1Day 15DSH-02
BO-05Sales dashboardDashboardstemplateV1Day 15DSH-02
BO-06Field dashboard & live mapDashboardsbespokeV1Day 15DSH-08
BO-07Stock dashboardDashboardstemplateV1Day 15DSH-05
BO-08Finance dashboardDashboardstemplateV1Day 15DSH-07
BO-09Resellers & prospects listCRMbespokeV1Day 16DSH-03
BO-10Resellers mapCRMbespokeV1Day 16DSH-03
BO-11Reseller 360°CRMbespokeV1Day 16DSH-03
BO-12Reseller create / editCRMtemplateV1Day 16DSH-03
BO-13Account validation queueCRMbespokeV1Day 16DSH-03
BO-14Follow-ups & tasksCRMtemplateV1Day 16DSH-03
BO-15SegmentsCRMtemplateV1Day 16DSH-03
BO-16Delegates listFieldtemplateV1Day 19DSH-08
BO-17Delegate detail & performanceFieldbespokeV1Day 19DSH-08
BO-18Delegate create / editFieldtemplateV1Day 19DSH-08
BO-19Zones (list + map editor)FieldbespokeV1Day 19DSH-08
BO-20ObjectivesFieldtemplateV1Day 19DSH-08
BO-21Tour planner (calendar)FieldbespokeV1Day 19DSH-08
BO-22Tour editor (stops, order, recommendation)FieldbespokeV1Day 19DSH-08
BO-23Tour live detailFieldbespokeV1Day 19DSH-08
BO-24Visits listFieldtemplateV1Day 19DSH-08
BO-25Visit detailFieldtemplateV1Day 19DSH-08
BO-26Visit types & checklist templatesFieldtemplateV1Day 19DSH-08
BO-27Products listCatalogtemplateV1Day 17DSH-04
BO-28Product editorCatalogbespokeV1Day 17DSH-04
BO-29BrandsCatalogtemplateV1Day 17DSH-04
BO-30CategoriesCatalogtemplateV1Day 17DSH-04
BO-31Price grid — three customer-type prices (bulk edit)CatalogtemplateV1Day 17DSH-04
BO-32Product import wizardCatalogbespokeV1Day 17DSH-04
BO-33Stock overviewStocktemplateV1Day 17DSH-05
BO-34Stock movements journalStocktemplateV1Day 17DSH-05
BO-35Stock receipt / adjustmentStocktemplateV1Day 17DSH-05
BO-36Inventory sessionsStocktemplateV1Day 17DSH-05
BO-37WarehousesStocktemplateV1Day 17DSH-05
BO-38Orders listOrdersbespokeV1Day 18DSH-06
BO-39Order detail (controls, actions, history)OrdersbespokeV1Day 18DSH-06
BO-40New order on behalf of a resellerOrderstemplateV1Day 18DSH-06
BO-41Orders to confirm (new, on hold, assisted)OrderstemplateV1Day 18DSH-06
BO-42Delivery boardOrdersbespokeV1Day 18DSH-06
BO-43CarriersOrderstemplateV1Day 18DSH-06
BO-44InvoicesFinancetemplateV1Day 18DSH-07
BO-45Payments & declarationsFinancetemplateV1Day 18DSH-07
BO-46Credit accountsFinancetemplateV1Day 18DSH-07
BO-47Overdue & remindersFinancetemplateV2DES-30DSH-20
BO-48Accounting exportsFinancetemplateV2DES-30DSH-20
BO-49Promotions listPromotionstemplateV1Day 19DSH-09
BO-50Promotion editorPromotionsbespokeV1Day 19DSH-09
BO-51Banners & daily offersPromotionstemplateV1Day 19DSH-09
BO-52PacksPromotionstemplateV2DES-30DSH-21
BO-53Loyalty settings & levelsPromotionstemplateV2DES-30DSH-21
BO-54Rewards & redemptionsPromotionstemplateV2DES-30DSH-21
BO-55Tickets listSupporttemplateV1Day 19DSH-10
BO-56Ticket detailSupporttemplateV1Day 19DSH-10
BO-57AnnouncementsCommunicationtemplateV1Day 19DSH-10
BO-58Notification templates & rulesCommunicationtemplateV1Day 19DSH-10
BO-59Reports libraryReportsbespokeV1Day 19DSH-11
BO-60Export centreReportstemplateV1Day 19DSH-11
BO-61UsersAdministrationtemplateV1Day 19DSH-12
BO-62Roles & permissions matrixAdministrationbespokeV1Day 19DSH-12
BO-63Activity logAdministrationtemplateV1Day 19DSH-12
BO-64Business settingsAdministrationtemplateV1Day 19DSH-12
BO-65Reference listsAdministrationtemplateV1Day 19DSH-12
BO-66Mobile app versionsAdministrationtemplateV1Day 19DSH-12
BO-67Data importsAdministrationtemplateV1Day 19DSH-12
BO-68My profile & sessionsAdministrationtemplateV1Day 19DSH-12
BO-69Returns & after-sales claimsOrderstemplateV1Day 18DSH-13
BO-70Support regions & WhatsApp numbersAdministrationtemplateV1Day 19DSH-12
BO-71Product attributes (filters) per categoryCatalogtemplateV1Day 17DSH-04

On this page