Onboarding & account
Screen specifications RA-01 to RA-08 of the reseller app — launch and version check, welcome with guest entry, sign-up with OTP, customer type and account claim, pending validation, sign-in and password reset.
These eight screens take a visitor from the first launch to a usable account. They are designed on day 6 (with guest mode, RA-41) and built in MOB-05 (launch) and MOB-06 (authentication and guest mode); invitation attribution is MRA-09. Shared rules — formatting, price lock, gating, errors, deep links — are on the reseller app overview. Every operation used here is detailed on the auth API page.
| ID | Screen | UI title | Section | Release | Design | Build |
|---|---|---|---|---|---|---|
RA-01 | Splash & version check | Démarrage | Onboarding & account | V1 | Day 6 | MOB-05 |
RA-02 | Welcome (sign in, sign up, browse as guest) | Bienvenue | Onboarding & account | V1 | Day 6 | MOB-06 |
RA-03 | Sign-up — phone & invitation code | Inscription — téléphone | Onboarding & account | V1 | Day 6 | MOB-06 |
RA-04 | OTP verification | Vérification du code | Onboarding & account | V1 | Day 6 | MOB-06 |
RA-05 | Sign-up — store details, customer type & password | Inscription — magasin | Onboarding & account | V1 | Day 6 | MOB-06 |
RA-06 | Account pending validation / rejected | Compte en attente | Onboarding & account | V1 | Day 6 | MOB-06 |
RA-07 | Sign in | Connexion | Onboarding & account | V1 | Day 6 | MOB-06 |
RA-08 | Forgot password | Mot de passe oublié | Onboarding & account | V1 | Day 6 | MOB-06 |
The client supplied no reference screen for onboarding. The delegate sign-in reference (delegate-01-login-profile) sets the visual tone; both apps share one design system.
Sign-up at a glance. Phone → SMS code → store details, customer type and password → account created (or claimed) → pending validation, with the prices of the declared type visible right away. The account claim (AUTH-05) matters: many resellers already exist as prospects created by a delegate in the field (DA-14); signing up with the same phone number — or with the invitation code — activates that record instead of creating a duplicate.
RA-01 — Splash & version check
| Field | Value |
|---|---|
| Screen | Splash & version check · Onboarding & account |
| French title | Démarrage |
| Release | V1 |
| Design day | Day 6 — DES-06 |
| Build task | MOB-05 |
| Requirements | AUTH-09 · ADM-07 |
| Reference screen | None — follow the design system |
Purpose — Start the app in under 3 seconds, apply the version and maintenance policy, restore the session (or the guest mode) and open the right first screen.
Entry points & exits — Entered on every cold start: launcher, tap on a push while the app is not running, App Link. Exits to RA-02 (first launch without session), RA-09 in guest mode (no session, guest mode chosen before), RA-09 or the pending deep-link target (signed in), RA-06 (account just created, still pending), RA-36 (forced update, maintenance, offline at first launch).
Layout
- Full-bleed brand background with the centred HOCO logo; under it the three brand marks HOCO · ACEFAST · BOROFONE, small and monochrome.
- The Android 12+ native splash hands over to this Flutter screen with the identical visual — no visible jump.
- A small progress indicator under the logo, shown only if the work takes more than 1 second.
- Caption at the bottom: « Version 1.0.0 (42) ».
Data
| Shown or used | Source |
|---|---|
| Version policy | appConfig.get → forceUpdate, updateAvailable, minVersion, latestVersion, storeUrl, releaseNotes |
| Maintenance | appConfig.get → maintenance.enabled, maintenance.message, maintenance.until |
| Public settings, server clock | appConfig.get → settings (pricing, orders, catalog, returns, afterSales, signup, support, legal), serverTime |
| Session, status, terms | me.get → type, status, mustChangePassword, consents.termsVersion, reseller.validationStatus, reseller.customerType, supportRegion |
| Guest flag, stored invitation code | Device storage |
| Installed version | Package info, sent on every call as X-App-Version and to appConfig.get as version |
Actions — No user action. The sequence:
- Read the session token, the guest flag and the cached config from storage.
- In parallel:
appConfig.get?app=RESELLER_APP&platform=android&version=…(timeout 5 s) and, when a token exists,me.get(timeout 8 s). - Apply the version policy, then maintenance, then route (overview › launch and session restore).
- First launch only: read the Play install referrer; if it holds
inv=AB12CD34(and the signed tokent), store them and callinvitations.attributewith anIdempotency-Key(fire and forget, retried at the next launch on failure). - Launched by an App Link
/i/{code}: store the code the same way. - Signed in: refresh the push token with
me.devices.registerif the app version changed, then openevents.stream.
Rules
- Target: first useful screen in under 3 seconds from process start on a mid-range Android with a normal connection (NFR-01).
- The policy is per app and platform (ADM-07), edited in BO-66;
appConfig.getitself never answers426or503. - If
appConfig.getfails, the cached config (at most 7 days old) applies; launch is blocked only when there is neither network nor cache. - A pending deep link (push or App Link) is kept in memory and opened once routing is done; it survives a detour through sign-in.
- A product or promotion link opened without a session opens in guest mode — guests may see them.
States
| State | Behaviour |
|---|---|
| Normal | Automatic routing, no interaction |
| Slow network (over 1 s) | Progress indicator under the logo |
| Offline, session token and cache present | RA-09 on cached data with the offline banner |
| Offline at the very first launch | RA-36 offline state with « Réessayer » |
forceUpdate, or 426 UPGRADE_REQUIRED | RA-36 forced update |
Maintenance, or 503 MAINTENANCE | RA-36 maintenance |
401 UNAUTHENTICATED on me.get | Token cleared → RA-09 in guest mode with the banner « Votre session a expiré. Reconnectez-vous. » |
403 ACCOUNT_SUSPENDED | RA-07 with the suspension banner |
5xx on me.get | Two retries, then RA-09 from cache if any, else ErrorState |
Copy
| Key | French |
|---|---|
| Version caption | « Version {version} ({build}) » |
| Session expired | « Votre session a expiré. Reconnectez-vous. » |
Analytics — app_open with coldStart, launchMs, routedTo, source (launcher, push, link), userKind; install_attributed with hasCode.
Acceptance criteria
- With a valid session and network, the first useful screen appears in under 3 s after a cold start on the reference mid-range Android.
forceUpdatetrue shows only the forced-update screen; no other screen is reachable.- With maintenance on, the maintenance state shows the message typed in BO-66.
- After installing from an invitation link,
invitations.attributeis called once on first open and the code is prefilled on RA-03. - A visitor who chose guest browsing once lands directly on the guest home at the next launch.
RA-02 — Welcome and guest entry
| Field | Value |
|---|---|
| Screen | Welcome (sign in, sign up, browse as guest) · Onboarding & account |
| French title | Bienvenue |
| Release | V1 |
| Design day | Day 6 — DES-06 |
| Build task | MOB-06 |
| Requirements | AUTH-03 · AUTH-04 · IVT-04 · AUTH-10 |
| Reference screen | None — follow the design system |
Purpose — Present the app to a visitor without session and offer the three ways in: create an account, sign in, or browse the catalog without an account (AUTH-10); show who invited them when the app was opened from an invitation.
Entry points & exits — From RA-01 at the first launch without session, from an App Link /i/{code} while signed out, from « Se connecter » in guest headers. Exits to RA-03 (« Créer mon compte »), RA-07 (« J'ai déjà un compte »), RA-09 in guest mode (« Découvrir le catalogue sans compte »), WhatsApp.
Layout
- HOCO logo at the top.
Carouselof three value slides (illustration, title, one line), auto-advance every 5 s, dots:- « Commandez en quelques secondes » — « Tout le catalogue HOCO, ACEFAST et BOROFONE à votre prix revendeur. »
- « Suivez vos commandes en direct » — « De la confirmation à la livraison, vous êtes notifié à chaque étape. »
- « Profitez des offres revendeurs » — « Offres du jour et promotions réservées aux revendeurs. »
- Invitation card, only with a valid code: delegate's initial in an avatar, « Invitation de Yacine, délégué HOCO », « Pour : Magasin El Wafa », code « AB12-CD34 ».
- Primary
Button« Créer mon compte »; secondary « J'ai déjà un compte »; text button « Découvrir le catalogue sans compte » with the helper « Les prix s'affichent après la création de votre compte. » WhatsAppButton(guest resolution, see the overview).- No language selector in V1 (French only); it appears when
settings.i18n.enabledLocalesholds more than one language (NFR-06).
Data
| Shown | Source |
|---|---|
| Invitation card | invitations.resolve → valid, delegateFirstName, storeName, expiresAt — nothing else personal (IVT-04) |
| WhatsApp number | support.whatsapp (guest), else settings.support.whatsapp |
| Stored invitation code | Device storage, written by RA-01 from the App Link or install referrer |
Actions
- On open with a stored code:
invitations.resolve(public, 30 calls per minute per IP); the card appears whenvalidis true. The buttons are usable immediately — the card never blocks them. - « Créer mon compte » → RA-03 with the code prefilled. « J'ai déjà un compte » → RA-07.
- « Découvrir le catalogue sans compte » → stores the guest flag and opens RA-09 in guest mode; the next launches skip this screen.
Rules
- An invitation links the delegate and the point of sale (cahier §6.6, IVT-01); the code is kept on the device until a sign-up uses it or its
expiresAtpasses. A guest who later signs up still gets the code prefilled. - An invalid or expired code is dropped silently here; RA-03 lets the visitor type another one.
- A signed-in user opening
/i/{code}never sees this screen: the app stays where it is and shows « Vous êtes déjà connecté ».
States — Invitation loading: card skeleton. Invalid invitation: no card. Offline: no card, buttons work (the next step needs the network and says so). Slide illustrations ship in the app bundle.
Copy
| Key | French |
|---|---|
| Invitation card | « Invitation de {delegateFirstName}, délégué HOCO » · « Pour : {storeName} » |
| Buttons | « Créer mon compte » · « J'ai déjà un compte » · « Découvrir le catalogue sans compte » |
| Guest helper | « Les prix s'affichent après la création de votre compte. » |
Analytics — welcome_viewed with hasInvitation; signup_started with hasInvitation; signin_started; guest_mode_started.
Acceptance criteria
- Opening
https://app.hoco.dz/i/AB12CD34with a valid code shows the delegate's first name and the store name, and the code is prefilled on RA-03. - « Découvrir le catalogue sans compte » opens the catalog with every price locked and no cart or favorite action.
- The screen renders and the three buttons work without network.
RA-03 — Sign-up — phone & invitation code
| Field | Value |
|---|---|
| Screen | Sign-up — phone & invitation code · Onboarding & account |
| French title | Inscription — téléphone |
| Release | V1 |
| Design day | Day 6 — DES-06 |
| Build task | MOB-06 |
| Requirements | AUTH-03 · AUTH-05 · IVT-04 |
| Reference screen | None — follow the design system |
Purpose — Start the sign-up by proving ownership of the shop's main mobile number, and capture the invitation (referral) code that attributes the sign-up to a delegate.
Entry points & exits — From RA-02, from the guest prompts of RA-41, from « Créer un compte » on RA-07, from /signup. Exits to RA-04 (code sent), RA-07 or RA-08 (number already has an account), back.
Layout
AppBarwith back and the step indicator « Étape 1 sur 3 ».- Title « Créez votre compte revendeur », helper « Utilisez le numéro principal de votre magasin. Il vous servira à vous connecter. »
PhoneField« Numéro de mobile »: fixed+213prefix with the flag, national input formatted as typed (« 0555 12 34 56 »), numeric keyboard.TextField« Code d'invitation (facultatif) »: maskXXXX-XXXX, upper-case, helper « Donné par votre délégué HOCO »; when resolved, a success line « Invitation de Yacine ».StickyFooterwith the primary button « Recevoir le code par SMS ».
Data
| Shown or used | Source |
|---|---|
| Prefilled code | Device storage (App Link or install referrer) |
| Code validity and delegate | invitations.resolve → valid, delegateFirstName |
| Code sent | auth.otp.send → destination (masked), expiresAt, resendAvailableAt, codeLength — passed to RA-04 |
Actions
- « Recevoir le code par SMS » →
auth.otp.sendwith{ purpose: "SIGNUP", phone }→ RA-04. Disabled until the number is valid and the code field is empty or valid; spinner while sending; a second tap is ignored. - The code is resolved 400 ms after the eighth character or when the field loses focus.
Rules
- Accepted numbers: Algerian mobiles —
+213followed by 9 digits starting with 5, 6 or 7 after normalisation; « 0555 12 34 56 », « 555123456 », « 00213555123456 » are all accepted. Landlines are refused because they cannot receive the SMS. - The main phone is unique (CRM-03): a number that already has a user account gets
409 PHONE_ALREADY_EXISTSand no SMS. This screen therefore reveals that a number has an account; RA-08 does not. - A point of sale created in the field with this number, without a user account, is not a conflict — the sign-up will claim it (AUTH-05).
- Invitation code: 8 characters from an alphabet without ambiguous letters, shown
AB12-CD34; typed lower-case is upper-cased, the dash is optional. An invalid code blocks the button until it is corrected or cleared, so attribution is never lost silently (IVT-04). - SMS limits (AUTH-07): one code per 60 seconds and five per hour per number.
States
| Situation | Message or behaviour |
|---|---|
Invalid number (client check, or 400 VALIDATION_FAILED with field code INVALID_PHONE) | « Numéro de mobile invalide. Exemple : 0555 12 34 56. » |
409 PHONE_ALREADY_EXISTS (details.canSignIn) | « Ce numéro a déjà un compte HOCO. » with « Se connecter » and « Mot de passe oublié ? » |
429 OTP_RESEND_TOO_SOON (details.retryAfterSeconds) | « Un code vient d'être envoyé. Réessayez dans {seconds} s. » |
429 RATE_LIMITED | « Trop de demandes pour ce numéro. Réessayez dans une heure. » |
Invitation not valid (valid: false or 404) | « Code d'invitation invalide ou expiré. » under the field |
503 OTP_DELIVERY_FAILED | « L'envoi du SMS a échoué. Réessayez dans quelques instants. » |
| Offline | Button disabled, helper « Connexion requise pour recevoir le code » |
Copy — title, helper and errors above; button « Recevoir le code par SMS »; step « Étape 1 sur 3 ».
Analytics — signup_phone_submitted with hasInvitation, invitationValid; signup_phone_conflict.
Acceptance criteria
- « 0555 12 34 56 », « 555123456 » and « +213555123456 » are all accepted and sent as
+213555123456. - A number that already has an account gets no SMS and is offered sign-in and password reset.
- A code arriving from the invitation link is prefilled and validated; an invalid code blocks the button until fixed or cleared.
- A second request within 60 s does not send a second SMS.
RA-04 — OTP verification
| Field | Value |
|---|---|
| Screen | OTP verification · Onboarding & account |
| French title | Vérification du code |
| Release | V1 |
| Design day | Day 6 — DES-06 |
| Build task | MOB-06 |
| Requirements | AUTH-03 · AUTH-04 · AUTH-07 |
| Reference screen | None — follow the design system |
Purpose — Verify the 6-digit SMS code. The same screen serves sign-up (from RA-03) and password reset (from RA-08).
Entry points & exits — From RA-03 or RA-08. Exits to RA-05 (sign-up) or the new-password step of RA-08 (reset); « Modifier » goes back to the number.
Layout
AppBarwith back; « Étape 2 sur 3 » during sign-up only.- Title « Entrez le code reçu par SMS »; helper « Code envoyé au +213 555 •• •• 56 » with the link « Modifier ».
OtpFieldwith six boxes, numeric keyboard, paste supported, filled automatically from the SMS on Android (SMS Retriever API).- Resend line: « Renvoyer le code dans 0:45 », then the link « Renvoyer le code ».
- Expiry hint in caption style: « Le code expire dans 4:12 ».
- Primary « Vérifier » — also submitted automatically when the sixth digit is entered.
Data — auth.otp.send → destination, resendAvailableAt, expiresAt, codeLength; auth.otp.verify → verificationToken, expiresAt and, during sign-up, signup.claimable and signup.prospect (storeName, ownerName, businessTypeCode, customerType, wilayaCode, communeId, communeName, addressLine, delegateFirstName).
Actions
- Sixth digit or « Vérifier » →
auth.otp.verify{ purpose, phone, code }. Success: sign-up → RA-05 with the token and the prospect; reset → new-password step of RA-08. - « Renvoyer le code » →
auth.otp.sendwith the same purpose; boxes cleared, countdowns reset, snackbar « Nouveau code envoyé ». A new code invalidates the previous one.
Rules
- Code: 6 digits (
settings.auth.otpLength), valid 5 minutes, 5 attempts; then a new code is required (AUTH-07). Resend: one per 60 s, five per hour. - The verification token is single-use and valid 10 minutes: RA-05 must be completed within that time.
- Auto-fill needs the app signature hash in the SMS template: the app sends its
androidAppHashwithauth.otp.send.
States
| Situation | Message or behaviour |
|---|---|
422 OTP_INVALID (details.attemptsRemaining) | « Code incorrect. Il vous reste {attemptsRemaining} essai(s). » — boxes shake and clear |
422 OTP_EXPIRED | « Ce code a expiré ou a été remplacé. Demandez un nouveau code. » |
429 TOO_MANY_ATTEMPTS | « Trop d'essais. Demandez un nouveau code. » — « Vérifier » disabled until a resend |
429 OTP_RESEND_TOO_SOON | Countdown keeps running |
| Offline | « Connexion requise pour vérifier le code. » |
Copy — « Entrez le code reçu par SMS » · « Code envoyé au {destination} » · « Renvoyer le code dans {mm:ss} » · « Renvoyer le code » · « Le code expire dans {mm:ss} » · « Vérifier ».
Analytics — otp_verified with purpose, attempts, autofilled; otp_failed with code; otp_resent.
Acceptance criteria
- On Android, the code fills itself from the SMS.
- After five wrong codes the code can no longer be verified; a newly sent code works.
- « Renvoyer le code » is active only at
resendAvailableAt.
RA-05 — Sign-up — store details, customer type & password
| Field | Value |
|---|---|
| Screen | Sign-up — store details, customer type & password · Onboarding & account |
| French title | Inscription — magasin |
| Release | V1 |
| Design day | Day 6 — DES-06 |
| Build task | MOB-06 |
| Requirements | AUTH-03 · AUTH-05 · CRM-01 · GEO-01 · AUTH-11 |
| Reference screen | None — follow the design system |
Purpose — Collect the store data, the customer type that will select the reseller's prices (AUTH-11) and a password; create the account — or claim the record a delegate created — and open the session.
Entry points & exits — From RA-04 during sign-up only. Exits to RA-06 (account PENDING_VALIDATION, the normal case) or RA-09 (claimed point of sale already approved by HOCO, account ACTIVE at once), back to RA-03 when the verification expired.
Layout — one scrolling form, « Étape 3 sur 3 »:
- Claim banner, only when
signup.claimable:Bannerinfo « Nous avons trouvé votre magasin, enregistré par Yacine. Vérifiez vos informations. » - Section « Votre magasin »:
TextFieldNom du magasin,TextFieldNom du responsable,SelectType de commerce (« Boutique de téléphonie », « Accessoires mobiles », « Multiservices / taxiphone », « Électronique / électroménager », « Grossiste », « Autre »). - Section « Vous êtes » — the customer type, three
RadioCard, none preselected unless the prospect has one:- Super-gros — « Grossiste : vous revendez à d'autres magasins, par grandes quantités. »
- Demi-gros — « Vous achetez des quantités intermédiaires pour votre magasin et quelques revendeurs. »
- Détail — « Magasin de détail : vous vendez au client final. » Under the cards: « Votre type de client détermine vos prix. HOCO le vérifie à la validation de votre compte ; ensuite, seul HOCO peut le modifier. »
- Section « Adresse du magasin »:
SelectWilaya (searchable, « 31 — Oran »),SelectCommune (enabled once a wilaya is chosen),TextFieldAdresse (« Cité USTO, local n° 14 »),TextFieldRepère (facultatif) (« En face de la pharmacie »), button « Ajouter la position du magasin » (optional). - Section « Contact »: switch « Même numéro pour WhatsApp » (on by default; off reveals a
PhoneField),TextFieldEmail (facultatif). - Section « Mot de passe »:
PasswordFieldMot de passe with the policy hint,PasswordFieldConfirmer le mot de passe. - Checkbox « J'accepte les conditions d'utilisation et la politique de confidentialité » — both links open an in-app browser.
StickyFooterwith the primary button « Créer mon compte ».
Documents and photos are not asked here: fewer fields means more completed sign-ups. They are added later on RA-28.
Data
| Shown or used | Source |
|---|---|
| Prefilled values (claim) | auth.otp.verify → signup.prospect.storeName, ownerName, businessTypeCode, customerType, wilayaCode, communeId, addressLine, delegateFirstName |
| Business types | appConfig.get → settings.signup.businessTypes[] (code, label, sortOrder) — sign-up happens before any session, so not reference.lists |
| Customer types offered | appConfig.get → settings.signup.customerTypes; labels from the app's l10n (Super-gros, Demi-gros, Détail) |
| Wilayas, communes | reference.wilayas → items[].code, nameFr; reference.communes → items[].id, nameFr — public, ETag-cached; a copy ships in the app bundle as fallback |
| Terms | appConfig.get → settings.legal.termsUrl, privacyUrl, termsVersion |
| Result | auth.signup.reseller → token, claimed, invitation.delegateFirstName, user.status, user.reseller.customerType, user.supportRegion |
Actions
- « Créer mon compte » →
auth.signup.resellerwith{ verificationToken, password, ownerName, customerType, store: { name, businessTypeCode, wilayaCode, communeId, addressLine, landmark?, location?, whatsapp?, email? }, invitationCode?, acceptedTermsVersion, deviceName }. On success: store the token, callme.devices.register, openevents.stream, route onuser.status. Disabled until required fields are valid and the box is ticked; spinner while sending. The single-use token makes a double submit harmless. - « Ajouter la position du magasin »: asks the location permission (while in use) with the rationale « Votre position aide le livreur et votre délégué à trouver le magasin. », captures
{ lat, lng, accuracy, capturedAt }and shows « Position enregistrée (précision 12 m) » with « Supprimer ». Above 100 m of accuracy: « Position imprécise — réessayez près de l'entrée ou passez cette étape. »
Rules
- One account per verified number. The server attaches the account to an existing point of sale by phone, then by invitation code, else creates a new
SELF_SIGNUPrecord (AUTH-05, CRM-02, CRM-03); a claimed record keeps its delegate, GPS and photos. - The invitation code attributes the sign-up to the inviting delegate; the funnel steps Compte créé and Téléphone vérifié are recorded (IVT-03, IVT-04).
- Customer type is required. It is stored as declared, highlighted to the validator in BO-13 when it differs from what the delegate recorded, confirmed or changed at validation, and afterwards changed only by the back-office (AUTH-11). The reseller sees the prices of the declared type right away (setting
catalog.pricesVisibleBeforeValidation, defaulttrue, decision to confirm); ordering waits for validation. - Password policy (AUTH-07): 8 to 128 characters, at least one letter and one digit, not containing the last 6 digits of the phone, not a common password. Confirmation must match (client check).
- The account starts PENDING_VALIDATION; exception: a point of sale already approved by HOCO (an existing customer) gets an ACTIVE account immediately (CRM-09).
- Limits: store name 2–120 characters, owner name 2–80, address 5–200; the commune must belong to the wilaya (GEO-01).
- The store address becomes the first, default delivery address (visible on RA-19). Typed values survive the app going to the background.
States
| Situation | Message or behaviour |
|---|---|
400 VALIDATION_FAILED with field codes | Message under each field: PASSWORD_TOO_WEAK « Mot de passe trop faible : 8 caractères minimum, avec au moins une lettre et un chiffre. » · INVALID_PHONE · INVALID_EMAIL · COMMUNE_NOT_IN_WILAYA « Cette commune n'appartient pas à la wilaya choisie. » · TERMS_NOT_ACCEPTED (terms changed meanwhile: the new version is shown) |
422 VERIFICATION_TOKEN_INVALID | Dialog « Votre vérification a expiré. Recommencez l'inscription. » → RA-03 with the number kept |
409 PHONE_ALREADY_EXISTS (account created meanwhile, for example on the web) | « Ce numéro a déjà un compte. » → RA-07 |
404 INVITATION_NOT_FOUND · 410 INVITATION_EXPIRED | « Code d'invitation invalide ou expiré. » with « Continuer sans code » |
409 INVITATION_ALREADY_USED | « Ce magasin a déjà un compte avec un autre numéro. Contactez HOCO. » with the WhatsApp button |
| Location permission denied | The button returns to its initial state; the step stays optional |
| Offline | Button disabled, form kept |
Copy
| Key | French |
|---|---|
| Sections | « Votre magasin » · « Vous êtes » · « Adresse du magasin » · « Contact » · « Mot de passe » |
| Customer-type helper | « Votre type de client détermine vos prix. HOCO le vérifie à la validation de votre compte ; ensuite, seul HOCO peut le modifier. » |
| Fields | « Nom du magasin » · « Nom du responsable » · « Type de commerce » · « Wilaya » · « Commune » · « Adresse » · « Repère (facultatif) » · « Même numéro pour WhatsApp » · « Email (facultatif) » · « Mot de passe » · « Confirmer le mot de passe » |
| Password mismatch | « Les mots de passe ne correspondent pas. » |
| Button | « Créer mon compte » |
Analytics — signup_completed with claimed, hasInvitation, hasLocation, customerType, status; signup_failed with code.
Acceptance criteria
- Signing up with the number of a field-created prospect prefills the form (including its customer type when recorded) and produces a single reseller record in BO-09, still linked to its delegate.
- The form cannot be sent without a customer type; the declared type appears in the validation queue BO-13.
- On success the session is open, the device is registered for push and the app lands on RA-06; the catalog then shows the prices of the declared type.
- Leaving the app mid-form and coming back keeps the typed values.
RA-06 — Account pending validation / rejected
| Field | Value |
|---|---|
| Screen | Account pending validation / rejected · Onboarding & account |
| French title | Compte en attente |
| Release | V1 |
| Design day | Day 6 — DES-06 |
| Build task | MOB-06 |
| Requirements | AUTH-08 · CRM-09 |
| Reference screen | None — follow the design system |
Purpose — Tell a reseller whose account waits for HOCO's validation — or was rejected — where things stand, what they can already do and how to reach HOCO.
Entry points & exits — Full screen right after RA-05; afterwards a status card at the top of the Compte tab (RA-27) and the banner on checkout open it; the push account.rejected. Exits to RA-09 (« Découvrir le catalogue »), RA-28, RA-26, RA-35, WhatsApp, RA-07 after sign-out.
Layout — pending
- Illustration and title « Compte en attente de validation ».
- Text « Merci, Magasin El Wafa ! Notre équipe vérifie vos informations et votre type de client (Demi-gros). Vous serez notifié dès que votre compte sera validé. »
- Vertical checklist: done « Téléphone vérifié »; done « Informations du magasin »; open « Documents du magasin (facultatif) » with « Ajouter » → RA-28; in progress « Validation par HOCO ».
- Info line: « En attendant, vous pouvez parcourir le catalogue avec vos prix et préparer votre panier. La commande sera possible après validation. »
- Primary « Découvrir le catalogue »; secondary « Compléter mon profil » → RA-28.
- Notification pre-prompt card when the permission is not granted: « Soyez prévenu dès la validation de votre compte » with « Activer ».
- « Une question ? » row: the
WhatsAppButton(declared region) and the assigned delegate's name and phone when there is one. - Text button « Se déconnecter ».
Layout — rejected — Title « Votre demande n'a pas été acceptée », « Motif : … » (the reason typed by HOCO), « Pour plus d'informations, contactez HOCO. », the WhatsApp button, « Se déconnecter ». The catalog stays browsable with the price lock.
Data
| Shown | Source |
|---|---|
| Store name, declared type, validation | me.get → reseller.storeName, reseller.customerType, status, reseller.validationStatus (En attente (PENDING), Refusé (REJECTED)), reseller.rejectionReason |
| Documents step | profile.documents.list → items.length (done when at least one document) |
| Delegate | me.get → reseller.assignedDelegate.displayName, reseller.assignedDelegate.phone |
me.get → supportRegion.whatsappNumber, else settings.support.whatsapp |
Actions
- Pull-to-refresh and every return to the foreground →
me.get. The pushaccount.approved(livenotification.created) triggers the same refresh. - When
statusbecomes ACTIVE: full-screen success « Votre compte est validé ! » — and, when HOCO changed the type at validation, « Votre type de client : Super-gros » — with « Commencer mes achats » → RA-09. No new sign-in. - « Se déconnecter » →
auth.logout→ guest browsing.
Rules
- Validation is done by an authorised back-office user in BO-13: approve or reject with a reason, and confirm or change the customer type (CRM-09, AUTH-11).
- Before validation: no checkout, no assisted-order confirmation, no payment declaration (
403 ACCOUNT_PENDING_VALIDATION) (AUTH-08). Cart, favorites, profile, documents and tickets work. - No self re-submission in V1: a rejected reseller contacts HOCO.
States — Loading: skeleton of the checklist. Offline: last known status with the offline banner. Validated: transition screen above. Rejected: rejected layout. Error on me.get: last known status plus « Réessayer ».
Copy
| Key | French |
|---|---|
| Pending title | « Compte en attente de validation » |
| Pending text | « Merci, {storeName} ! Notre équipe vérifie vos informations et votre type de client ({customerType}). Vous serez notifié dès que votre compte sera validé. » |
| Waiting info | « En attendant, vous pouvez parcourir le catalogue avec vos prix et préparer votre panier. La commande sera possible après validation. » |
| Validated | « Votre compte est validé ! » · « Votre type de client : {customerType} » · « Commencer mes achats » |
| Rejected | « Votre demande n'a pas été acceptée » · « Motif : {reason} » |
Analytics — pending_viewed; pending_profile_clicked; account_validated_seen with customerTypeChanged.
Acceptance criteria
- After approval in BO-13 the reseller sees the validated state immediately (live event or push), or at the next return to the app, without signing in again.
- A type changed by HOCO at validation is shown on the validated screen and the catalog prices switch to it.
- A rejected account shows the reason entered in the back-office.
RA-07 — Sign in
| Field | Value |
|---|---|
| Screen | Sign in · Onboarding & account |
| French title | Connexion |
| Release | V1 |
| Design day | Day 6 — DES-06 |
| Build task | MOB-06 |
| Requirements | AUTH-04 · AUTH-08 |
| Reference screen | None — follow the design system |
Purpose — Sign in with the shop's main phone number and the password (AUTH-04).
Entry points & exits — From RA-02, the guest prompts of RA-41, « Se connecter » in guest headers, RA-01 (suspended account), /signin, after any 401. Exits to RA-09, RA-06 or the pending deep link; to RA-08; to RA-03.
Layout
- Logo, title « Connexion », subtitle « Accédez à votre espace revendeur ».
Bannerslot: info (« Votre session a expiré. Reconnectez-vous. », « Mot de passe modifié. Connectez-vous. ») or error (suspension, see States).PhoneField« Numéro de téléphone », prefilled with the last number used on this device.PasswordField« Mot de passe » with show/hide.- Right-aligned link « Mot de passe oublié ? ».
- Primary « Se connecter ».
- « Pas encore de compte ? » « Créer un compte ».
WhatsAppButtonin the footer area.
Data — auth.login → token, expiresAt, user (the full Me: type, status, mustChangePassword, reseller.validationStatus).
Actions — « Se connecter » → auth.login { identifier, password, app: "RESELLER", deviceName } with the X-App-Version, X-App-Platform and X-Device-Id headers → store the token → me.devices.register → events.stream → route on user (no extra me.get). Disabled until the number is valid and the password non-empty; spinner while signing in. Guests have no cart or favorites, so nothing needs merging; the screen the guest was on reopens with prices.
Rules
- Five attempts per minute per number and IP; five consecutive failures lock the number for 15 minutes (SEC-02).
- SUSPENDED and DISABLED accounts are refused with an explicit message (AUTH-08).
mustChangePasswordtrue → the change-password form of RA-35 opens before anything else.- Each sign-in creates a session listed on RA-35 (AUTH-06); a sign-in from a new device notifies the other devices (
security.new_device).
States
| Situation | Message or behaviour |
|---|---|
401 INVALID_CREDENTIALS | « Numéro ou mot de passe incorrect. » — never says which |
429 TOO_MANY_ATTEMPTS (details.lockedUntil) | « Trop de tentatives. Réessayez dans {minutes} min. » and « Mot de passe oublié ? » emphasised |
429 RATE_LIMITED (Retry-After) | « Trop de tentatives. Réessayez dans {seconds} s. » |
403 ACCOUNT_SUSPENDED (details.status) | Error banner « Votre compte est suspendu. Contactez HOCO pour le réactiver. » (or « Ce compte a été désactivé. ») with the WhatsApp button and the support phone |
403 WRONG_APP (details.userType: "DELEGATE") | « Ce compte est un compte délégué : utilisez l'application HOCO Délégué. » |
503 MAINTENANCE | RA-36 maintenance |
| Offline | « Connexion requise pour vous connecter. » |
Copy — « Connexion » · « Accédez à votre espace revendeur » · « Numéro de téléphone » · « Mot de passe » · « Mot de passe oublié ? » · « Se connecter » · « Pas encore de compte ? » · « Créer un compte ».
Analytics — login_succeeded with fromDeepLink, fromGuest; login_failed with code.
Acceptance criteria
- A valid number and password open the app on the screen matching the account status, with prices of the reseller's type.
- Five wrong passwords lock the number for 15 minutes with the waiting time shown.
- A suspended account cannot sign in and sees how to contact HOCO.
- A delegate's credentials are refused with the explicit message.
RA-08 — Forgot password
| Field | Value |
|---|---|
| Screen | Forgot password · Onboarding & account |
| French title | Mot de passe oublié |
| Release | V1 |
| Design day | Day 6 — DES-06 |
| Build task | MOB-06 |
| Requirements | AUTH-04 · AUTH-07 |
| Reference screen | None — follow the design system |
Purpose — Let the reseller set a new password after proving ownership of the number by SMS (AUTH-04, AUTH-07).
Entry points & exits — From the link on RA-07 (number prefilled), from RA-03 on a phone conflict, from /forgot-password. Exits to RA-04 (code), then back to this screen's last step, then RA-07.
Layout — Two steps on this screen around RA-04:
- Step « Votre numéro »:
PhoneField, primary « Recevoir un code ». - (RA-04 verifies the code.)
- Step « Nouveau mot de passe »:
PasswordFieldNouveau mot de passe with the policy hint,PasswordFieldConfirmer, primary « Enregistrer le nouveau mot de passe ».
Data — auth.otp.send with purpose: "PASSWORD_RESET"; auth.otp.verify → verificationToken; auth.password.reset.
Actions
- « Recevoir un code » →
auth.otp.send→ RA-04. - « Enregistrer le nouveau mot de passe » →
auth.password.reset{ verificationToken, newPassword }→ RA-07 with the number prefilled and the banner « Mot de passe modifié. Connectez-vous. »
Rules
- No account enumeration: the first step answers the same whether or not the number has an account — « Si un compte existe pour ce numéro, un code vous a été envoyé par SMS. »
- Same SMS limits and password policy as sign-up. A reset revokes every session of the account and detaches its devices from push; the user also receives the security SMS « Votre mot de passe HOCO a été modifié… ».
States — OTP errors as on RA-04; 400 VALIDATION_FAILED with PASSWORD_TOO_WEAK as on RA-05; 422 VERIFICATION_TOKEN_INVALID → back to step 1 with « Le délai est dépassé. Demandez un nouveau code. »; offline → buttons disabled.
Copy — « Mot de passe oublié » · « Votre numéro » · « Recevoir un code » · « Si un compte existe pour ce numéro, un code vous a été envoyé par SMS. » · « Nouveau mot de passe » · « Enregistrer le nouveau mot de passe » · « Mot de passe modifié. Connectez-vous. »
Analytics — password_reset_requested; password_reset_completed.
Acceptance criteria
- An unknown number gets the same answer as a known one and no SMS.
- After a reset the old password no longer works and other devices are signed out.
- The reseller can sign in with the new password straight away, number prefilled.
Reseller app
Who uses the HOCO reseller app, how it is navigated, the behaviours every screen shares (guest mode, pricing, live updates, WhatsApp support), its deep links and the list of its 41 screens.
Shopping
Screen specifications RA-09 to RA-16 of the reseller app — home, search, barcode scanner, catalog, advanced filters and sort, product detail, full specifications and favorites, for guests and resellers.