HOCO B2B
DesignScreen specificationsReseller app

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.

IDScreenUI titleSectionReleaseDesignBuild
RA-01Splash & version checkDémarrageOnboarding & accountV1Day 6MOB-05
RA-02Welcome (sign in, sign up, browse as guest)BienvenueOnboarding & accountV1Day 6MOB-06
RA-03Sign-up — phone & invitation codeInscription — téléphoneOnboarding & accountV1Day 6MOB-06
RA-04OTP verificationVérification du codeOnboarding & accountV1Day 6MOB-06
RA-05Sign-up — store details, customer type & passwordInscription — magasinOnboarding & accountV1Day 6MOB-06
RA-06Account pending validation / rejectedCompte en attenteOnboarding & accountV1Day 6MOB-06
RA-07Sign inConnexionOnboarding & accountV1Day 6MOB-06
RA-08Forgot passwordMot de passe oubliéOnboarding & accountV1Day 6MOB-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

FieldValue
ScreenSplash & version check · Onboarding & account
French titleDémarrage
ReleaseV1
Design dayDay 6 — DES-06
Build taskMOB-05
RequirementsAUTH-09 · ADM-07
Reference screenNone — 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

  1. Full-bleed brand background with the centred HOCO logo; under it the three brand marks HOCO · ACEFAST · BOROFONE, small and monochrome.
  2. The Android 12+ native splash hands over to this Flutter screen with the identical visual — no visible jump.
  3. A small progress indicator under the logo, shown only if the work takes more than 1 second.
  4. Caption at the bottom: « Version 1.0.0 (42) ».

Data

Shown or usedSource
Version policyappConfig.get → forceUpdate, updateAvailable, minVersion, latestVersion, storeUrl, releaseNotes
MaintenanceappConfig.get → maintenance.enabled, maintenance.message, maintenance.until
Public settings, server clockappConfig.get → settings (pricing, orders, catalog, returns, afterSales, signup, support, legal), serverTime
Session, status, termsme.get → type, status, mustChangePassword, consents.termsVersion, reseller.validationStatus, reseller.customerType, supportRegion
Guest flag, stored invitation codeDevice storage
Installed versionPackage info, sent on every call as X-App-Version and to appConfig.get as version

Actions — No user action. The sequence:

  1. Read the session token, the guest flag and the cached config from storage.
  2. In parallel: appConfig.get?app=RESELLER_APP&platform=android&version=… (timeout 5 s) and, when a token exists, me.get (timeout 8 s).
  3. Apply the version policy, then maintenance, then route (overview › launch and session restore).
  4. First launch only: read the Play install referrer; if it holds inv=AB12CD34 (and the signed token t), store them and call invitations.attribute with an Idempotency-Key (fire and forget, retried at the next launch on failure).
  5. Launched by an App Link /i/{code}: store the code the same way.
  6. Signed in: refresh the push token with me.devices.register if the app version changed, then open events.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.get itself never answers 426 or 503.
  • If appConfig.get fails, 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

StateBehaviour
NormalAutomatic routing, no interaction
Slow network (over 1 s)Progress indicator under the logo
Offline, session token and cache presentRA-09 on cached data with the offline banner
Offline at the very first launchRA-36 offline state with « Réessayer »
forceUpdate, or 426 UPGRADE_REQUIREDRA-36 forced update
Maintenance, or 503 MAINTENANCERA-36 maintenance
401 UNAUTHENTICATED on me.getToken cleared → RA-09 in guest mode with the banner « Votre session a expiré. Reconnectez-vous. »
403 ACCOUNT_SUSPENDEDRA-07 with the suspension banner
5xx on me.getTwo retries, then RA-09 from cache if any, else ErrorState

Copy

KeyFrench
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.
  • forceUpdate true 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.attribute is 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

FieldValue
ScreenWelcome (sign in, sign up, browse as guest) · Onboarding & account
French titleBienvenue
ReleaseV1
Design dayDay 6 — DES-06
Build taskMOB-06
RequirementsAUTH-03 · AUTH-04 · IVT-04 · AUTH-10
Reference screenNone — 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

  1. HOCO logo at the top.
  2. Carousel of 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. »
  3. 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 ».
  4. 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. »
  5. WhatsAppButton (guest resolution, see the overview).
  6. No language selector in V1 (French only); it appears when settings.i18n.enabledLocales holds more than one language (NFR-06).

Data

ShownSource
Invitation cardinvitations.resolve → valid, delegateFirstName, storeName, expiresAt — nothing else personal (IVT-04)
WhatsApp numbersupport.whatsapp (guest), else settings.support.whatsapp
Stored invitation codeDevice 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 when valid is 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 expiresAt passes. 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

KeyFrench
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/AB12CD34 with 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

FieldValue
ScreenSign-up — phone & invitation code · Onboarding & account
French titleInscription — téléphone
ReleaseV1
Design dayDay 6 — DES-06
Build taskMOB-06
RequirementsAUTH-03 · AUTH-05 · IVT-04
Reference screenNone — 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

  1. AppBar with back and the step indicator « Étape 1 sur 3 ».
  2. Title « Créez votre compte revendeur », helper « Utilisez le numéro principal de votre magasin. Il vous servira à vous connecter. »
  3. PhoneField « Numéro de mobile »: fixed +213 prefix with the flag, national input formatted as typed (« 0555 12 34 56 »), numeric keyboard.
  4. TextField « Code d'invitation (facultatif) »: mask XXXX-XXXX, upper-case, helper « Donné par votre délégué HOCO »; when resolved, a success line « Invitation de Yacine ».
  5. StickyFooter with the primary button « Recevoir le code par SMS ».

Data

Shown or usedSource
Prefilled codeDevice storage (App Link or install referrer)
Code validity and delegateinvitations.resolve → valid, delegateFirstName
Code sentauth.otp.send → destination (masked), expiresAt, resendAvailableAt, codeLength — passed to RA-04

Actions

  • « Recevoir le code par SMS » → auth.otp.send with { 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 — +213 followed 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_EXISTS and 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

SituationMessage 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. »
OfflineButton 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

FieldValue
ScreenOTP verification · Onboarding & account
French titleVérification du code
ReleaseV1
Design dayDay 6 — DES-06
Build taskMOB-06
RequirementsAUTH-03 · AUTH-04 · AUTH-07
Reference screenNone — 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

  1. AppBar with back; « Étape 2 sur 3 » during sign-up only.
  2. Title « Entrez le code reçu par SMS »; helper « Code envoyé au +213 555 •• •• 56 » with the link « Modifier ».
  3. OtpField with six boxes, numeric keyboard, paste supported, filled automatically from the SMS on Android (SMS Retriever API).
  4. Resend line: « Renvoyer le code dans 0:45 », then the link « Renvoyer le code ».
  5. Expiry hint in caption style: « Le code expire dans 4:12 ».
  6. 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.send with 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 androidAppHash with auth.otp.send.

States

SituationMessage 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_SOONCountdown 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

FieldValue
ScreenSign-up — store details, customer type & password · Onboarding & account
French titleInscription — magasin
ReleaseV1
Design dayDay 6 — DES-06
Build taskMOB-06
RequirementsAUTH-03 · AUTH-05 · CRM-01 · GEO-01 · AUTH-11
Reference screenNone — 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 »:

  1. Claim banner, only when signup.claimable: Banner info « Nous avons trouvé votre magasin, enregistré par Yacine. Vérifiez vos informations. »
  2. Section « Votre magasin »: TextField Nom du magasin, TextField Nom du responsable, Select Type de commerce (« Boutique de téléphonie », « Accessoires mobiles », « Multiservices / taxiphone », « Électronique / électroménager », « Grossiste », « Autre »).
  3. 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. »
  4. Section « Adresse du magasin »: Select Wilaya (searchable, « 31 — Oran »), Select Commune (enabled once a wilaya is chosen), TextField Adresse (« Cité USTO, local n° 14 »), TextField Repère (facultatif) (« En face de la pharmacie »), button « Ajouter la position du magasin » (optional).
  5. Section « Contact »: switch « Même numéro pour WhatsApp » (on by default; off reveals a PhoneField), TextField Email (facultatif).
  6. Section « Mot de passe »: PasswordField Mot de passe with the policy hint, PasswordField Confirmer le mot de passe.
  7. Checkbox « J'accepte les conditions d'utilisation et la politique de confidentialité » — both links open an in-app browser.
  8. StickyFooter with 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 usedSource
Prefilled values (claim)auth.otp.verify → signup.prospect.storeName, ownerName, businessTypeCode, customerType, wilayaCode, communeId, addressLine, delegateFirstName
Business typesappConfig.get → settings.signup.businessTypes[] (code, label, sortOrder) — sign-up happens before any session, so not reference.lists
Customer types offeredappConfig.get → settings.signup.customerTypes; labels from the app's l10n (Super-gros, Demi-gros, Détail)
Wilayas, communesreference.wilayas → items[].code, nameFr; reference.communes → items[].id, nameFr — public, ETag-cached; a copy ships in the app bundle as fallback
TermsappConfig.get → settings.legal.termsUrl, privacyUrl, termsVersion
Resultauth.signup.reseller → token, claimed, invitation.delegateFirstName, user.status, user.reseller.customerType, user.supportRegion

Actions

  • « Créer mon compte » → auth.signup.reseller with { verificationToken, password, ownerName, customerType, store: { name, businessTypeCode, wilayaCode, communeId, addressLine, landmark?, location?, whatsapp?, email? }, invitationCode?, acceptedTermsVersion, deviceName }. On success: store the token, call me.devices.register, open events.stream, route on user.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_SIGNUP record (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, default true, 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

SituationMessage or behaviour
400 VALIDATION_FAILED with field codesMessage 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_INVALIDDialog « 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 deniedThe button returns to its initial state; the step stays optional
OfflineButton disabled, form kept

Copy

KeyFrench
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

FieldValue
ScreenAccount pending validation / rejected · Onboarding & account
French titleCompte en attente
ReleaseV1
Design dayDay 6 — DES-06
Build taskMOB-06
RequirementsAUTH-08 · CRM-09
Reference screenNone — 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

  1. Illustration and title « Compte en attente de validation ».
  2. 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é. »
  3. 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 ».
  4. Info line: « En attendant, vous pouvez parcourir le catalogue avec vos prix et préparer votre panier. La commande sera possible après validation. »
  5. Primary « Découvrir le catalogue »; secondary « Compléter mon profil » → RA-28.
  6. Notification pre-prompt card when the permission is not granted: « Soyez prévenu dès la validation de votre compte » with « Activer ».
  7. « Une question ? » row: the WhatsAppButton (declared region) and the assigned delegate's name and phone when there is one.
  8. 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

ShownSource
Store name, declared type, validationme.get → reseller.storeName, reseller.customerType, status, reseller.validationStatus (En attente (PENDING), Refusé (REJECTED)), reseller.rejectionReason
Documents stepprofile.documents.list → items.length (done when at least one document)
Delegateme.get → reseller.assignedDelegate.displayName, reseller.assignedDelegate.phone
WhatsAppme.get → supportRegion.whatsappNumber, else settings.support.whatsapp

Actions

  • Pull-to-refresh and every return to the foreground → me.get. The push account.approved (live notification.created) triggers the same refresh.
  • When status becomes 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

KeyFrench
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

FieldValue
ScreenSign in · Onboarding & account
French titleConnexion
ReleaseV1
Design dayDay 6 — DES-06
Build taskMOB-06
RequirementsAUTH-04 · AUTH-08
Reference screenNone — 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

  1. Logo, title « Connexion », subtitle « Accédez à votre espace revendeur ».
  2. Banner slot: info (« Votre session a expiré. Reconnectez-vous. », « Mot de passe modifié. Connectez-vous. ») or error (suspension, see States).
  3. PhoneField « Numéro de téléphone », prefilled with the last number used on this device.
  4. PasswordField « Mot de passe » with show/hide.
  5. Right-aligned link « Mot de passe oublié ? ».
  6. Primary « Se connecter ».
  7. « Pas encore de compte ? » « Créer un compte ».
  8. WhatsAppButton in 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).
  • mustChangePassword true → 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

SituationMessage 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 MAINTENANCERA-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

FieldValue
ScreenForgot password · Onboarding & account
French titleMot de passe oublié
ReleaseV1
Design dayDay 6 — DES-06
Build taskMOB-06
RequirementsAUTH-04 · AUTH-07
Reference screenNone — 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:

  1. Step « Votre numéro »: PhoneField, primary « Recevoir un code ».
  2. (RA-04 verifies the code.)
  3. Step « Nouveau mot de passe »: PasswordField Nouveau mot de passe with the policy hint, PasswordField Confirmer, 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.

On this page