# Progressive Account Creation — Redesign Plan ## 1. Problem Statement The current sign-up flow (`apps/dashboard/src/app/sign-up/[[...sign-up]]/page.tsx`) asks for **everything at once**: - Owner identity & credentials - Full legal/company identity (RC, ICE, IF, license, issuing authority…) - Company contact & address (bilingual) - Responsible person details - Plan + billing + payment provider That's ~30 required fields across 4 steps before a user has even seen the product. This is the classic cause of sign-up abandonment: the perceived effort to "just try the app" is too high. **Goal:** Let someone create an account with almost nothing, start exploring the dashboard immediately, and only be asked for a specific piece of information **right when an action requires it** — never before. This pattern is commonly called **progressive profiling** or **just-in-time (JIT) data collection**. --- ## 2. Core Principle > Collect the minimum data needed to create an identity. Collect everything else lazily, tied to the action that needs it, not to a step number. Two reframes that drive the whole design: 1. **Stop thinking in "steps."** Replace the 4-step wizard with a single **account state machine**. The account always exists in some state (Created → Active → Listing-Ready → Payable → Compliant → Subscribed), and each state unlocks more of the product. 2. **Stop thinking in "forms."** Replace big forms with **micro-prompts**: a single field, or a tiny group of 2-3 tightly related fields, asked contextually, usually as a modal/drawer over the action the user just tried to take. --- ## 3. New Sign-up Flow (Minimum Viable Signup) ### Step 0 — only step that blocks entry | Field | Why it's required immediately | |---|---| | Owner email | Login identifier, needed to send confirmation | | Password | Needed to create a session | | Preferred language | Needed to render anything afterward correctly | That's it. No name, no company, no legal data, no plan. ``` POST /api/v1/auth/account/start { "email": "string", "password": "string (8-128)", "preferredLanguage": "en | fr | ar" } ``` Response creates: - A `User` record (owner) - A `Company` record in a `DRAFT` state with `name = null` and a system-generated placeholder slug - A session/JWT so the user is **logged in immediately** The user lands directly in the dashboard home, in a clearly "draft workspace" visual state (banner: *"Your workspace is being set up — finish anytime"*). This single change — going from ~30 required fields to 3 — is the biggest win and should be shipped first even before the rest of the JIT system exists. --- ## 4. Account Maturity Model Replace the rigid 4-step form with an explicit состояние/state on the `Company` record: | State | Unlocked by | What it unlocks | |---|---|---| | `DRAFT` | Step 0 above | Browsing dashboard UI, exploring settings, reading docs | | `IDENTIFIED` | Owner first/last name + company display name | Personalizing UI, inviting teammates | | `LISTABLE` | Company contact info + address | Creating/saving a fleet item or offer as **draft** | | `PUBLISHABLE` | Legal identity block (legal name, legal form, RC, ICE, IF, license) | Publishing a listing live on the marketplace | | `PAYABLE` | Payment provider config (AmanPay/PayPal) + responsible person | Accepting bookings/payments | | `SUBSCRIBED` | Plan + billing period selection | Past the 90-day trial, billed account | Each state is just a derived boolean/computed field (`companyState`) based on which fields are filled — not a separate workflow table. The UI reads this state to decide what to show/lock. This also directly maps the **existing legal/compliance fields** (RC, ICE, IF, operating license) to the **one moment they actually matter**: right before something goes live on the marketplace. Today they're collected on day 1, before the user has even decided to publish anything. --- ## 5. Just-In-Time Prompt Catalog Map every current form field to the **specific user action** that should trigger asking for it. This is the heart of the redesign — a table the eng + product team should review and tune together. | Action the user takes | Data requested at that moment | Fields involved | |---|---|---| | First login after signup | Display name | Owner first/last name, company commercial name | | Click "Add a vehicle / offer" | Basic company contact | Phone, company email, address, city, country, zip | | Click "Publish" on a listing | Legal identity | Legal name, legal form, RC, ICE, IF, license #, issue date, issuing authority | | Click "Enable bookings" / "Connect payments" | Payment + responsible person | Payment provider, responsible person name/role/ID/phone/email | | Visit `/subscription` or trial reaches day 75 | Plan selection | Plan, billing period | | Click "Invite teammate" | (no new data) | — | | Optional, never blocking | Fax, legal representative name/title, qualification/diploma | These stay fully optional, surfaced only in Settings | Design rule: **never ask for more than 1 group of tightly-related fields in a single prompt.** E.g. "Legal identity" can still be ~8 fields, but it's one cohesive concept the user understands ("info needed to verify your business"), not an arbitrary step number. --- ## 6. UX Pattern: Contextual Prompt, Not a Form Page When a user triggers an action that needs missing data: 1. The action is allowed to start optimistically (e.g. they can build a full listing draft). 2. On the actual gating action (Publish, Enable bookings…), intercept with a **slide-over panel or modal**, not a full-page redirect. 3. The modal: - States *why* in one line: "We need your business registration details before this listing can go public." - Shows only the missing fields (if some were already filled earlier, don't ask again). - Has a "Save and publish" primary action, and a "Save as draft, finish later" secondary action. 4. On submit, persist via a generic incremental-update endpoint, recompute `companyState`, and continue the original action automatically. This keeps the user inside their task instead of bouncing them to a separate "complete your profile" flow. A persistent, low-key "Workspace setup: 60% complete" indicator (e.g. in the sidebar) lets motivated users finish everything in one sitting if they prefer — progressive disclosure should never *block* someone who wants to do it all at once. --- ## 7. Backend Changes ### 7.1 Schema - Make nearly all `Company` fields nullable (`legalName`, `registrationNumber`, `iceNumber`, etc. all become optional at the DB level — required-ness becomes a **business rule per action**, not a DB constraint). - Add `companyState` (computed/cached enum) or compute on read. - Add `profileCompletionPercent` for the UI progress indicator. ### 7.2 API Replace the single big `POST /auth/company/signup` with: ``` POST /api/v1/auth/account/start → minimal signup (Section 3) PATCH /api/v1/company/profile → generic partial update, accepts any subset of fields PATCH /api/v1/company/legal-identity → group endpoint for legal fields (own validation rules) PATCH /api/v1/company/payment-setup → group endpoint for payment + responsible person PATCH /api/v1/company/plan → plan/billing selection GET /api/v1/company/state → returns companyState + missing fields per state ``` Each `PATCH` endpoint validates only its own slice with its own Zod schema (split `auth.company.schemas.ts` into per-group schemas), instead of one monolithic `companySignupSchema`. ### 7.3 Gating middleware Add a small middleware/guard used on the actions that require a given state, e.g.: ```ts requireCompanyState("PUBLISHABLE") // on POST /listings/:id/publish requireCompanyState("PAYABLE") // on POST /bookings, /payments/connect ``` If the guard fails, return a structured error listing exactly which fields are missing, so the frontend can render the right contextual modal without guessing. ### 7.4 Email confirmation Confirmation email still sends immediately after Step 0, but its copy changes from "your account is ready" to "your account is ready — finish setup anytime to start publishing," since there's much less to confirm. --- ## 8. Frontend Changes - Replace the 937-line multi-step page with: - A short `~/sign-up` page (Step 0 only). - A set of small, reusable "completion modals," one per group in Section 5, each built from the existing `BilingualInput` and form components (these don't need to be rebuilt, just reorganized). - Introduce a `useCompanyState()` hook that fetches `GET /company/state` and exposes `{state, missingFields, completionPercent}` to any component that needs to gate an action. - Add a generic `` wrapper component used to gate the relevant buttons/actions, opening the right modal when triggered. --- ## 9. Edge Cases & Risks | Risk | Mitigation | |---|---| | Users abandon before ever reaching `PUBLISHABLE`/`PAYABLE` | That's expected and fine — they were never going to finish the old form either. Track funnel by state instead of all-or-nothing. | | Marketplace ends up with unpublishable "ghost" companies | `DRAFT`/`IDENTIFIED` companies are simply never shown publicly — only `PUBLISHABLE`+ companies appear, so this is a non-issue by construction. | | Legal/compliance requirements might mandate some data before *any* account exists, in some jurisdictions | Confirm with legal/compliance which fields (if any) are truly required by regulation before allowing a transaction vs. before allowing signup at all. Likely only the `PAYABLE` gate has real regulatory weight. | | Users get repeatedly interrupted by modals | Batch by group (Section 5), not by individual field; show the completion indicator so users see how close they are; never re-ask for data already given. | | Existing accounts (already fully filled out under the old flow) | No migration needed — they'll simply compute as `SUBSCRIBED`/fully complete already. | | Analytics/CRM relying on full data at signup | Will need to adjust to track partial profiles; add a scheduled job/report on stalled accounts per state for sales/CS follow-up. | --- ## 10. Suggested Rollout Phases 1. **Phase 1 — Minimal signup.** Ship Section 3 (3-field signup) behind a flag. Move all other fields to a single post-signup "Settings → Complete your profile" page (not yet contextual, just relocated). This alone should meaningfully cut signup abandonment. 2. **Phase 2 — State machine + gating.** Add `companyState`, the `requireCompanyState` guard, and the `GET /company/state` endpoint. Gate "Publish" and "Enable bookings" only. 3. **Phase 3 — Contextual modals.** Replace the static Settings page prompts with the in-context slide-over modals described in Section 6. 4. **Phase 4 — Polish.** Add the sidebar completion indicator, refine copy per modal, A/B test prompt timing (e.g. ask for legal identity right after first draft listing vs. only at publish time). --- ## 11. Summary of File-Level Changes | File | Change | |---|---| | `apps/dashboard/src/app/sign-up/[[...sign-up]]/page.tsx` | Shrink to Step 0 only (~50-100 lines instead of 937) | | `apps/api/src/modules/auth/auth.company.routes.ts` | Replace single signup route with `account/start` + add new `PATCH` group routes | | `apps/api/src/modules/auth/auth.company.service.ts` | Split `createCompanyAndOwner` into minimal creation + separate update-group methods | | `apps/api/src/modules/auth/auth.company.schemas.ts` | Split into `accountStartSchema`, `companyContactSchema`, `legalIdentitySchema`, `paymentSetupSchema`, `planSchema` | | New: `apps/api/src/modules/company/company.state.ts` | Computes `companyState` + missing fields from a `Company` record | | New: `apps/api/src/middleware/requireCompanyState.ts` | Gating middleware described in 7.3 | | New: `apps/dashboard/src/components/profile/CompletionModal*.tsx` | Set of contextual prompt modals, one per group | | New: `apps/dashboard/src/hooks/useCompanyState.ts` | Frontend hook described in Section 8 |