Skip to content

UI DESIGN SPECIFICATION

VietGara — Multi-Tenant SaaS Platform for Auto Garage Management & Maintenance

Version 1.5 — October 2, 2026 · Cloud: Google Cloud Platform (GCP) · Backend: Go

Superseded. Version 2 (../v2/02_UI_Design_Spec.md) replaces this document; it is kept for history, and where the two differ, v2 is the decision.

1. Introduction

This document specifies the content, layout, and behavior of the main screens in VietGara in detail. It does NOT replace the visual design files in Figma — Figma is the source of truth for exact pixels/colors/spacing; this document describes the content/behavior requirements to guide the design team building wireframes/UI in Figma, and for developers to cross-reference when Figma annotations are incomplete.

The layout diagrams below use block notation (text-based wireframes, which Mermaid cannot express) purely to illustrate structure and do not reflect the final visual design; flows are drawn in Mermaid.

2. General Conventions

2.1. Grid & Breakpoints

PlatformBreakpointGrid ColumnsNotes
Owner Web≥1280px (desktop)12 columns, 24px gutterPrimary layout, optimized for office screens
Owner Web768–1279px (tablet)8 columns, 16px gutterSidebar collapses to icon-only
Customer App / Technician AppNative 8pt grid (iOS/Android)4 columns (mobile)No desktop layout needed

2.2. Standardized States (applied to every screen with dynamic data)

StateDisplay Requirement
LoadingA skeleton screen matching the upcoming content layout — never a full-screen spinner that hides all context
Empty (no data yet)An illustration/icon + a short explanation + a CTA for the next action (e.g. “No quotes yet — Create your first quote”)
Error (failed to load data)An error message stating the cause (if known) + a “Retry” button
Offline (Technician App only)A persistent banner at the top of the screen, not blocking interaction, stating the number of actions pending sync

3. Screen Specifications — Owner Web

3.1. Dashboard

Purpose: Gives the garage owner an overview immediately upon login: revenue, repair orders in progress, low-stock alerts, overdue receivables.

+--------------------------------------------------------------+
| [Logo] [Select garage: Garage A v] [Notifications] [Avatar] |
+---------+------------------------------------------------------+
| Sidebar | KPI Cards: Monthly revenue | Orders in progress | AR |
| - Dashb.|------------------------------------------------------|
| - Quotes | Revenue chart, last 30 days |
| - Orders |------------------------------------------------------|
| - Stock | Alerts: [Low stock: 3 items] [Overdue receivables] |
| - Cust. |------------------------------------------------------|
| - Reports| List of repair orders in progress (5 most recent) |
+---------+------------------------------------------------------+

UI components used (from the Design System):

  • Sidebar Navigation
  • KPI Card
  • Chart (line/bar)
  • Alert Banner
  • Data Table (condensed) | State | Display Requirement | | --- | --- | | Empty (new garage) | KPIs show 0, chart replaced with guidance: “Create your first quote to get started” | | Loading | Skeleton for each KPI Card and the chart |

Release 1.5: “Monthly revenue” and the revenue chart are the one figure this screen needed that nothing already served — both come from the new Reporting revenue-by-period endpoint (API Specification 13.1). “Orders in progress”, “AR” (outstanding receivables) and the low-stock alert, and the in-progress orders list, are each composed from the module that already owns that data (Repair Order, Billing & Settlement’s outstandingOnly filter, Inventory) — the screen itself does the composing, not a new backend aggregate call.

3.2. Quote List & Creation

Purpose: Lets the service advisor create a quote quickly while the customer waits, selecting an existing vehicle/customer or creating a new one inline.

+--------------------------------------------------------------+
| Quotes > New [Save][Send] |
+--------------------------------------------------------------+
| Customer: [Search/Select...] [+ New] |
| Vehicle: [Search by plate...] [+ Add new vehicle] |
| Approval: (o) Send a link ( ) Record manually |
+--------------------------------------------------------------+
| Labor / Parts line items |
| [+ Add line item] |
| ---------------------------------------------------------------|
| | Description | Qty | Unit price | Line total | [x] |
| | Oil change... | 1 | 250,000 | 250,000 | |
+--------------------------------------------------------------+
| Total: 730,000 VND |
+--------------------------------------------------------------+

UI components used (from the Design System):

  • Autocomplete Search (customer/vehicle)
  • Dynamic Table (add/remove rows)
  • Currency-formatted Number Input
  • Segmented Control (approval mode: Send a link / Record manually)
  • Sticky Footer (total + action buttons) | State | Display Requirement | | --- | --- | | Draft saving | Show a small indicator “Saved at HH:mm” next to the Save button | | Missing required field | Disable the “Send” button, highlight the missing field with a red border + a specific message |

Approval mode (FR-QUO-02): pre-selected from the garage’s default (Garage Settings), changeable per quote. Pressing Send:

  • Send a link — generates the public approval link; a confirmation shows it with a “Copy link” button and states whether it was also e-mailed (“E-mailed to {customer}” or “No e-mail on file — share the link yourself”). The quote’s status badge becomes “Sent”.
  • Record manually — no link or e-mail; the quote’s status becomes “Sent” and the screen opens the Record decision panel directly: the same line list with an Approve/Reject toggle per line and a “Save decision” button, for the advisor to enter what the customer said face to face.

Once decided (either path), the quote becomes read-only with an “Approved”/“Rejected” badge and, when approved, a Convert to repair order button.

3.3. Repair Orders — Kanban Board

Purpose: Shows service advisors/garage owners every repair order in progress by status, with drag-and-drop to change status.

+--------------------------------------------------------------+
| Repair Orders [Filter: Technician v] |
+------------+------------+------------+---------------------+
| To Do | In Progress| Waiting on | Completed |
| (3) | (5) | Parts (1) | (2 today) |
|------------|------------|------------|---------------------|
| [Card A] | [Card C] | [Card E] | [Card F] |
| [Card B] | [Card D] | | [Card G] |
+------------+------------+------------+---------------------+

Opening a card shows its detail panel: the order’s lines, each with an item-type badge (Labor/Part), a per-line status control (Not Started/In Progress/Completed) and a technician multi-select (FR-REP-02) — one or more technicians per line, not per order. The card’s column is derived automatically from line progress, not moved by a free drag between arbitrary columns: editing a line’s status (or dragging it within the panel) is what actually advances the card. A Close button, enabled once every line reads Completed, performs the explicit close action (FR-REP-06) and removes the card from the board.

UI components used (from the Design System):

  • Kanban Column
  • Kanban Card (plate, technician name, time, priority badge)
  • Card Detail Panel: per-line status control, technician multi-select, Close button
  • Filter Dropdown
  • Drag & Drop interaction | State | Display Requirement | | --- | --- | | Card at risk of delay | Card border switches to a warning color (amber/red) + a clock icon | | Waiting on Parts | Defined status, but Release 1.3 has no automatic trigger for it — no inventory-stock link to Repair Order yet |

3.4. Inventory — Stock on Hand

Purpose: Lets inventory staff quickly check current stock levels and spot items running low.

+--------------------------------------------------------------+
| Inventory > Stock on Hand [Search...] [+ Goods Receipt] |
+--------------------------------------------------------------+
| SKU | Item Name | On Hand | Reorder Threshold | ... |
| P-001 | Engine oil 4L | 12 | 5 | |
| P-002 | Engine air filter| 2 (!!) | 5 | |
+--------------------------------------------------------------+

The + Goods Receipt button opens a form: item (searched by SKU/name), quantity, unit cost (optional) — submitting records a stock-in transaction (FR-INV-01) and refreshes the row’s On Hand value. A second tab, Stock Movements, lists every transaction (FR-INV-05) newest first — direction (In/Out), quantity, reference (a purchase, or the repair order that triggered an automatic issue), date — filterable by item and direction, matching the Data Table conventions of Quote List (3.2).

UI components used (from the Design System):

  • Data Table with sort/filter
  • Low-stock warning Badge (red/amber)
  • Search Input
  • Goods Receipt form (Modal): item search, quantity, unit cost
  • Stock Movements tab: Data Table with direction/item filters | State | Display Requirement | | --- | --- | | Low stock | Red badge + a subtly highlighted row background, not relying on text color alone (accessibility) |

3.5. Sign-in, Sign-up, E-mail Verification and Password Reset

Purpose: let an owner start in minutes and recover access without support (FR-IAM-01/02/04).

+---------------------------+----------------------------------+
| Brand panel | [Language: VI v] |
| tagline, 3 feature bullets| Sign in |
| | [ G Continue with Google ] |
| | ---------- or ---------- |
| | Email [ ] |
| | Password [ ] (eye) |
| | Forgot password? |
| | [ Sign in ] |
| | No user yet? Create one |
+---------------------------+----------------------------------+
stateDiagram-v2
  [*] --> SignUp
  SignUp --> VerifyEmail: user created, code e-mailed
  SignIn --> VerifyEmail: e-mail not verified (a new code is sent)
  SignIn --> Garages: verified
  VerifyEmail --> Garages: correct code
  VerifyEmail --> VerifyEmail: resend (after 30 s)
  SignIn --> ForgotEmail: Forgot password?
  ForgotEmail --> ForgotCode: code e-mailed
  ForgotCode --> NewPassword: correct code
  NewPassword --> SignIn: password changed
ElementBehavior
Sign-up formFull name, e-mail, optional phone, password (≥ 8, hint shown); the current interface language becomes the user language
OTP field6 digits, numeric keyboard, one-time-code autofill; countdown “Code expires in 1:52”; submit disabled once expired; “Resend code” disabled with a 30 s countdown
Unverified userEvery page except Account redirects to verification; the page explains that garages, staff and data are available after verification
Password resetThree steps on one page (e-mail → code → new password + confirmation); the e-mail step never reveals whether the user exists
ErrorsTranslated from the API error code (e.g. OTP_EXPIRED → “The code has expired. Request a new one.”)

3.6. Garages and Garage Settings

  • Garages: one row per garage with logo, name, address, “Owner”/“Staff” tag, Active/Inactive status, “Open” (switch the active garage) and a settings button when the user may edit it. Empty state: “No garage yet — create your first garage”. Creating a garage beyond the plan shows the PLAN_LIMIT_REACHED message with a link to Subscription.
  • Garage settings (/garages/:id/settings), cards saved independently: Profile (name, address, phone, e-mail, tax ID); Configuration (time zone, currency, quote and repair-order numbering templates with the placeholder hint, and a default quote approval mode — Send a link / Record manually, FR-QUO-02 — new quotes start with, still changeable per quote); Operating hours (7 rows: open checkbox, opens, closes); Logo (preview, upload PNG/JPEG/WebP ≤ 1 MB, remove); Status (owner only: deactivate with a confirmation that explains staff lose access, or reactivate). Users without garage.write see the page read-only.

3.7. Staff, Invitations, Roles and Groups

+--------------------------------------------------------------+
| Staff [+ Invite staff] |
| Members |
| (AV) An Vo [email protected] · 0901.. Service Advisor ● Active ⋯ |
| (BN) Binh [email protected] Technician ○ Inactive ⋯ |
| Invitations |
| Chi [email protected] Technician Pending Expires 03/10 [Revoke]|
+--------------------------------------------------------------+
  • Invite: a dialog with one field “E-mail or phone” and “Find”; the found user is shown with its name and contact; if it is already a member or invited, a notice replaces the form; otherwise the owner ticks staff groups and presses “Send invitation”.
  • Member actions (with staff.write): change groups, deactivate/reactivate, remove (confirmation states that access is lost immediately). The owner row has no actions.
  • Roles & groups (owners, /roles): roles list (name, Default tag, permission count, “used by N groups”) with Add and Edit opening the role editor; below, the active garage’s groups (name, role, member count) with create/edit/delete.
  • Role editor (owners, /roles/new, /roles/:id/edit), a full page rather than a dialog so it stays readable on mobile:
    • Header: breadcrumb back to Roles & groups, and a role selector (edit mode) that switches to another role’s editor.
    • Name and description fields; an impact line: “Applies at once to all your garages, for the members of N groups using this role” (or “No staff group uses this role yet”).
    • Permission grid: one row per catalogue module, in catalogue order, and two columns: View (<module>.read) and Add & edit (<module>.write). Each cell is a checkbox labelled with the permission’s description (e.g. “View customers and vehicles”). A cell whose action the module does not offer shows ”—” (screen readers: “Not available”). Modules of later releases (quotes, repair orders, inventory, billing, reports) are muted with a “Coming soon” tag but stay editable, so owners can prepare roles in advance.
    • Write includes read: ticking Add & edit also ticks View; unticking View also unticks Add & edit.
    • Reset to default (default roles only, next to the impact line): replaces the grid with the permissions the platform created the role with (defaultPermissions). The name and description are kept. The result is an unsaved change like any other, reviewed in the grid and applied with Save. The button is disabled when the grid already matches the defaults.
    • Save bar (sticky at the bottom): “N unsaved changes” / “No unsaved changes”, Undo changes and Save. Save is enabled only with changes and a name, and sends the role’s whole permission set in one request. It then shows “Changes saved.” (or “Role created.” after a create, which moves to the new role’s editor).
    • Unsaved changes: leaving the page or picking another role in the selector opens “Discard unsaved changes?” (Cancel / Discard); closing or reloading the tab triggers the browser’s own warning.
  • My invitations (/invitations, linked from the e-mail as /invitations/:id): “{inviter} invites you to join {garage}” with Accept / Decline; a badge on the navigation item and a dot on the bell show pending invitations.

3.8. Subscription (Owner Web) and Platform Administration (Admin Web)

  • Subscription (all verified users): current plan and status, period/trial end, grace end when read-only; usage meters for garages, staff and today’s transactions against the plan limits; plan cards (price per month/year, limits line “2 garages · 20 staff · 100 transactions/day”, features) with “Choose this plan” or “Renew”, the credit balance, and the open payment request with its status. Choosing a plan opens the payment request sheet (FR-LIC-07): the plan limits check (an invalid plan names the exceeded limits and cannot continue), the amount due with the credit for the unused days, the platform’s bank details with the QR code and the transfer reference with a copy button, then the form with the payer, bank, account, amount, bank transaction reference, date and an evidence screenshot; a submitted request shows “Waiting for review”, its cancel action, or the rejection reason with a way to submit again.
  • License banner (all Owner Web pages): trial countdown (info), read-only (warning), suspended (error); owners get a “Choose a plan” button, staff are told to contact the owner.
  • Admin Web — Plans (/plans, vietgara-admin-web): table (code, name, price, limits, active/hidden) and a two-column plan form (code fixed after creation, price, currency, cycle, the three limits, features, public, active, sort order).
  • Admin Web — Subscriptions (/subscriptions): search by owner e-mail or name, status filter, paginated table (owner, plan, status, period end, usage) with “Extend” (number of days), the credit balance and an open-request badge.
  • Admin Web — Payment requests (/payment-requests): the review queue of submitted requests (owner, plan, quoted and declared amounts with a mismatch warning, submitted time, waiting days); a detail with the evidence image, the owner’s plan and usage, Approve (a note is required when the amounts differ) and Reject (reason required).
  • Admin Web — Payment details (/payment-settings): bank, account number, holder, branch, transfer instructions and the QR code upload with a preview; a change shows who and when it was last edited.

3.9. Header and Language

The header of the Owner Web holds the active-garage selector, a language switcher (Tiếng Việt / English, Vietnamese by default; the choice is saved on the user), the invitations bell and sign-out. The sidebar shows only the modules the member’s permissions allow; modules of later releases appear disabled with a “Soon” tag. The Admin Web has no garage selector: its sidebar lists Plans, Subscriptions and Account, and the header holds the language switcher and sign-out. Non-admin users see an “Access denied” page.

3.10. Vehicle Detail — Repair History

Purpose: Lets a service advisor or owner see everything that has been quoted or done on a specific vehicle before creating a new quote (FR-CUS-03).

+--------------------------------------------------------------+
| < 51F-123.45 - Toyota Vios [+ New quote] |
+--------------------------------------------------------------+
| Owner: Nguyen Van A · 0901234567 |
+--------------------------------------------------------------+
| History |
| LSC-202609-0003 Repair Ord. In Progress 730,000 VND 30/09/26 |
| BG-202609-0007 Quote Approved 730,000 VND 30/09/26 |
| BG-202608-0012 Quote Converted 1,250,000 VND 12/08/26 |
+--------------------------------------------------------------+

UI components used (from the Design System): Data Table (condensed), Status Badge (per entry type/status), Empty state.

StateDisplay Requirement
Empty (no history yet)“Quotes, repair orders and settlements for this vehicle appear here — create the first quote to get started.”
LoadingSkeleton rows matching the table layout

Release 1.2 lists quotes; Release 1.3 adds repair orders to the same list (its own type badge and status text), newest first regardless of type; settlements join in Release 1.4.

3.11. Billing & Receivables (Release 1.4)

Purpose: Lets the Garage Accountant turn a closed repair order into a settlement, split it between the customer and an insurer, record what was actually collected, and track what is still outstanding — one list, not two screens (UX Guideline 5.4).

+--------------------------------------------------------------+
| Billing & Receivables [ ] Outstanding only [Status: v] |
+--------------------------------------------------------------+
| Number | Customer | Vehicle | Total | Status |
| QT-202610-0001 | Nguyen V.A | 51F-123.45 | 1,000,000| Pending|
| QT-202609-0042 | Tran T.B | 29A-456.78 | 730,000| Partial|
+--------------------------------------------------------------+

The Outstanding only toggle is FR-REC-01’s receivables view — a client-side label over the same list with outstandingOnly=true, not a different page. A closed repair order with no settlement yet shows a Create settlement action on its own detail screen (Section 3.3); creating one here is a single confirmation, nothing to fill in (FR-BIL-01). Opening a settlement row shows its detail panel:

+--------------------------------------------------------------+
| QT-202610-0001 [Export receipt PDF] |
+--------------------------------------------------------------+
| Customer: Nguyen Van A Vehicle: 51F-123.45 |
| Total: 1,000,000 VND Status: Partially paid |
+--------------------------------------------------------------+
| Allocation |
| Customer [ 600,000] Insurer [ 400,000] Insurer: v |
| [ Save allocation ] |
+--------------------------------------------------------------+
| Payments [ + Record payment ] |
| 30/09/26 Customer Cash 300,000 |
+--------------------------------------------------------------+

The allocation form only appears/applies while customerAmount + insurerAmount is kept equal to the total — the Save button stays disabled otherwise, with the running difference shown so the accountant does not have to do the subtraction themselves; the insurer dropdown (Partners of type Insurer) is required and only enabled once the insurer amount is non-zero. Record payment opens a short form (payer, amount, method, note); the amount field is capped to what is still due from that payer, and the status badge updates immediately (Pending → Partially paid → Paid) once saved. Export receipt PDF (FR-DOC-01) downloads the document directly, no preview step, the same pattern as the Quote and Repair Order PDF buttons below.

UI components used (from the Design System):

  • Data Table with status filter and a toggle filter (Outstanding only)
  • Status Badge (Pending/Partially paid/Paid)
  • Allocation form: two amount inputs validated against the total, Partner Select
  • Record Payment form (Modal): payer radio, amount (capped), method select, note
  • Export button (PDF download, no preview)
StateDisplay Requirement
No settlement yet on a closed order“Create settlement” button on the repair order’s own detail screen, not here
Allocation does not sum to the totalSave button disabled; the running difference is shown next to the two inputs
Payment amount exceeds what is dueInline validation before submit, not a server round-trip
Empty (no settlements yet)“Settlements appear here once a repair order is closed and settled.”

PDF/Excel export buttons also appear on the screens that already existed: the Quote List & Creation detail view (3.2) and the Repair Order Kanban card panel (3.3) each get an Export PDF button (FR-DOC-01); the Inventory Stock Movements tab (3.4) gets an Export Excel button (FR-DOC-02) next to its existing filters. All three share the same no-preview, direct-download pattern as the settlement receipt above.

4. Screen Specifications — Customer App

4.1. Quote Detail & Approval

Purpose: Lets the customer clearly see each line item and the total, and decide to approve/reject — this is the single most trust-critical screen for the customer. Reached only when the advisor chose the Send a link approval mode (FR-QUO-02); a Record manually quote never shows this screen to the customer at all — the advisor enters the decision directly in Owner Web instead (Section 3.2). Delivered in Release 1.2 as a public, no-login web page at the link’s URL; it moves into the native Customer App shown below once that app ships (Phase 11, Release 11.2), same content and rule.

+----------------------------+
| < Quote #Q-1024 |
+----------------------------+
| Vehicle: 51A-123.45 - Toyota Vios|
| Line items: |
| - Engine oil change 250,000|
| - Air filter (x4) 480,000|
+----------------------------+
| Total: 730,000 VND|
+----------------------------+
| [ Reject ] [ Approve Quote ]|
+----------------------------+

UI components used (from the Design System):

  • Line Item List
  • Sticky Bottom CTA (2 buttons, Approve emphasized)
  • Price Formatting | State | Display Requirement | | --- | --- | | Approved | Hide the 2 action buttons, show a badge “Approved at HH:mm” + a “View repair order” button |

4.2. Real-Time Repair Progress Tracking

Purpose: Lets the customer know exactly what stage their vehicle is at without calling the garage.

+----------------------------+
| Repair progress: 51A-123.45 |
+----------------------------+
| [x] Received |
| [x] In repair |
| [ ] Quality check |
| [ ] Ready for pickup |
+----------------------------+
| Latest photo update: [img] |
+----------------------------+

UI components used (from the Design System):

  • Stepper/Progress Timeline
  • Image Gallery (photos uploaded by the technician) | State | Display Requirement | | --- | --- | | Real-time update | The timeline auto-updates without requiring a refresh (via push/socket), with a subtle animation when a new status arrives |

5. Screen Specifications — Technician App

5.1. Today’s Repair Order List

Purpose: Lets the technician see what needs to be done as soon as they open the app, without needing to search.

+----------------------------+
| Today - Jul 12 [Sync: OK]|
+----------------------------+
| [Card] 51A-123.45 |
| Oil change + air filter |
| Status: In progress |
+----------------------------+
| [Card] 29B-678.90 |
| Brake inspection |
| Status: Not started |
+----------------------------+

UI components used (from the Design System):

  • Sync indicator (top corner)
  • Large Task Card, easy to tap with a thumb
  • Status Badge | State | Display Requirement | | --- | --- | | Offline | Persistent amber banner: “Offline — 2 changes pending sync” |

5.2. Update Line Item + Take Photos

Purpose: The technician’s core action of the day — needs to minimize the number of taps.

+----------------------------+
| Engine oil change |
+----------------------------+
| [ Not started ] |
| [ In progress ] (selected) |
| [ Completed ] |
+----------------------------+
| Before/after photos: [+][+][+]|
+----------------------------+
| [ Save Update ] |
+----------------------------+

UI components used (from the Design System):

  • Segmented Control (status selection, not a dropdown)
  • Photo Capture Button (opens camera directly)
  • Large Primary Button | State | Display Requirement | | --- | --- | | Saving while offline | Button changes to “Saved locally” with an hourglass icon, no error shown |

6. Developer Handoff

  • Every screen in this document corresponds to one complete Figma frame (covering every state: default/loading/empty/error), annotated via Figma Dev Mode.
  • Components used in a specification must be instances from the 03_Design_System.md Team Library — never redraw basic controls (input, button) inside the UI Screens file.
  • Before handoff, run the design QA checklist in 03_Design_System.md, Section 8.