Wanxai
/

Text Generation
Transformers
PyTorch
English
llama
finance
Eval Results (legacy)
text-generation-inference
Hermit11 commited on
Commit
85a930a
·
verified ·
1 Parent(s): 2f7db6d

Upload DriveSmart_Dashboard_PRD.md

Browse files
Files changed (1) hide show
  1. DriveSmart_Dashboard_PRD.md +882 -0
DriveSmart_Dashboard_PRD.md ADDED
@@ -0,0 +1,882 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # DriveSmart AI — Instructor Dashboard
2
+ ## Product Requirements Document (PRD)
3
+ ### For: lovable.dev / v0.dev Frontend Generation
4
+ ### Version: 1.0 | Status: Ready for Development
5
+
6
+ ---
7
+
8
+ ## 1. Project Overview
9
+
10
+ **Product Name:** DriveSmart AI Instructor Dashboard
11
+ **Purpose:** A web-based dashboard for driving school instructors to upload and manage training materials, monitor learner engagement, and configure their AI assistant — which serves learners 24/7 via WhatsApp.
12
+ **Primary User:** Driving school instructors and administrators
13
+ **Secondary Users:** Driving school managers / executives (read-only analytics view)
14
+
15
+ ---
16
+
17
+ ## 2. Brand Identity & Design System
18
+
19
+ ### 2.1 Brand Colours (AA Kenya — strictly follow these)
20
+
21
+ ```css
22
+ :root {
23
+ /* Primary Palette */
24
+ --green-900: #0F3D1A; /* deepest green — hero backgrounds */
25
+ --green-700: #1A5C2A; /* primary brand green — nav, buttons, headers */
26
+ --green-500: #2E8B46; /* hover states, active indicators */
27
+ --green-100: #E8F5EC; /* light tinted backgrounds, cards */
28
+
29
+ /* Accent Palette */
30
+ --yellow-500: #F5C200; /* primary yellow — CTAs, highlights, badges */
31
+ --yellow-300: #FFD84D; /* hover state for yellow elements */
32
+ --yellow-100: #FFF8D6; /* light yellow tint for alerts, info banners */
33
+
34
+ /* Neutral Palette */
35
+ --white: #FFFFFF;
36
+ --gray-50: #F8F9FA;
37
+ --gray-100: #F1F3F5;
38
+ --gray-200: #E9ECEF;
39
+ --gray-400: #ADB5BD;
40
+ --gray-600: #6C757D;
41
+ --gray-800: #343A40;
42
+ --black: #0A0A0A;
43
+
44
+ /* Semantic */
45
+ --success: #2E8B46;
46
+ --warning: #F5C200;
47
+ --error: #D63031;
48
+ --info: #0984E3;
49
+ }
50
+ ```
51
+
52
+ ### 2.2 Typography
53
+
54
+ ```css
55
+ /* Import in <head> */
56
+ @import url('https://fonts.googleapis.com/css2?family=Barlow+Condensed:wght@600;700;800&family=DM+Sans:wght@400;500;600&family=DM+Mono:wght@400;500&display=swap');
57
+
58
+ :root {
59
+ --font-display: 'Barlow Condensed', sans-serif; /* headings, hero text, nav */
60
+ --font-body: 'DM Sans', sans-serif; /* body, labels, UI text */
61
+ --font-mono: 'DM Mono', monospace; /* code blocks, IDs, stats */
62
+ }
63
+ ```
64
+
65
+ **Type Scale:**
66
+ | Token | Size | Weight | Font | Usage |
67
+ |---|---|---|---|---|
68
+ | `--text-hero` | 3.5rem / 56px | 800 | Barlow Condensed | Page hero titles |
69
+ | `--text-h1` | 2.25rem / 36px | 700 | Barlow Condensed | Section headings |
70
+ | `--text-h2` | 1.5rem / 24px | 600 | Barlow Condensed | Card headings |
71
+ | `--text-h3` | 1.125rem / 18px | 600 | DM Sans | Subsection labels |
72
+ | `--text-body` | 0.9375rem / 15px | 400 | DM Sans | Body copy |
73
+ | `--text-small` | 0.8125rem / 13px | 400 | DM Sans | Captions, metadata |
74
+ | `--text-mono` | 0.875rem / 14px | 500 | DM Mono | Stats, codes, counts |
75
+
76
+ ### 2.3 Checkered Brand Pattern
77
+
78
+ The AA Kenya diagonal checkered pattern (green + yellow squares) is a signature brand element. Implement it as a CSS background pattern used as decorative accents — NOT as full-page backgrounds.
79
+
80
+ ```css
81
+ /* Checkered accent strip — used as section dividers, card top-borders */
82
+ .checkered-strip {
83
+ background-image:
84
+ repeating-conic-gradient(
85
+ var(--green-700) 0% 25%,
86
+ var(--yellow-500) 0% 50%
87
+ );
88
+ background-size: 16px 16px;
89
+ height: 8px;
90
+ width: 100%;
91
+ }
92
+
93
+ /* Thin checkered border top on highlighted cards */
94
+ .card--featured::before {
95
+ content: '';
96
+ display: block;
97
+ height: 5px;
98
+ background-image: repeating-conic-gradient(
99
+ var(--green-700) 0% 25%, var(--yellow-500) 0% 50%
100
+ );
101
+ background-size: 10px 10px;
102
+ }
103
+ ```
104
+
105
+ ### 2.4 Spacing & Radius
106
+
107
+ ```css
108
+ :root {
109
+ --radius-sm: 4px;
110
+ --radius-md: 8px;
111
+ --radius-lg: 12px;
112
+ --radius-xl: 20px;
113
+
114
+ --space-1: 4px; --space-2: 8px; --space-3: 12px;
115
+ --space-4: 16px; --space-6: 24px; --space-8: 32px;
116
+ --space-10: 40px; --space-12: 48px; --space-16: 64px;
117
+ }
118
+ ```
119
+
120
+ ### 2.5 Shadows
121
+
122
+ ```css
123
+ :root {
124
+ --shadow-sm: 0 1px 3px rgba(0,0,0,0.08), 0 1px 2px rgba(0,0,0,0.06);
125
+ --shadow-md: 0 4px 12px rgba(0,0,0,0.10), 0 2px 4px rgba(0,0,0,0.06);
126
+ --shadow-lg: 0 10px 30px rgba(0,0,0,0.12), 0 4px 8px rgba(0,0,0,0.08);
127
+ --shadow-green: 0 4px 20px rgba(26, 92, 42, 0.25);
128
+ --shadow-yellow: 0 4px 20px rgba(245, 194, 0, 0.30);
129
+ }
130
+ ```
131
+
132
+ ---
133
+
134
+ ## 3. Animation & Motion System
135
+
136
+ ### 3.1 Motion Philosophy
137
+ **"Purposeful momentum"** — Every animation communicates state, not just decoration. Fast in, slower out. Elements that enter from a direction leave in the same direction. No bouncing, no elastic — the design is authoritative and professional.
138
+
139
+ ### 3.2 Core Transitions
140
+
141
+ ```css
142
+ :root {
143
+ --ease-standard: cubic-bezier(0.4, 0, 0.2, 1); /* general transitions */
144
+ --ease-enter: cubic-bezier(0.0, 0, 0.2, 1); /* elements entering */
145
+ --ease-exit: cubic-bezier(0.4, 0, 1, 1); /* elements leaving */
146
+ --ease-sharp: cubic-bezier(0.4, 0, 0.6, 1); /* quick snappy moves */
147
+
148
+ --duration-fast: 120ms;
149
+ --duration-base: 220ms;
150
+ --duration-slow: 380ms;
151
+ --duration-reveal: 500ms;
152
+ }
153
+ ```
154
+
155
+ ### 3.3 Animation Catalogue
156
+
157
+ **Page Entry (staggered reveal):**
158
+ ```css
159
+ /* Apply to dashboard cards, table rows on page load */
160
+ @keyframes fadeSlideUp {
161
+ from { opacity: 0; transform: translateY(18px); }
162
+ to { opacity: 1; transform: translateY(0); }
163
+ }
164
+
165
+ .card { animation: fadeSlideUp var(--duration-reveal) var(--ease-enter) both; }
166
+ .card:nth-child(1) { animation-delay: 0ms; }
167
+ .card:nth-child(2) { animation-delay: 60ms; }
168
+ .card:nth-child(3) { animation-delay: 120ms; }
169
+ .card:nth-child(4) { animation-delay: 180ms; }
170
+ ```
171
+
172
+ **Stat Counter Animation:**
173
+ ```javascript
174
+ // Animate numbers counting up on dashboard load
175
+ // e.g. 0 → 1,247 over 1000ms using requestAnimationFrame
176
+ // Use easeOutExpo easing for satisfying deceleration
177
+ function animateCount(el, from, to, duration) { ... }
178
+ ```
179
+
180
+ **Sidebar Navigation Active Indicator:**
181
+ ```css
182
+ /* Sliding green pill indicator for active nav item */
183
+ .nav-indicator {
184
+ position: absolute;
185
+ left: 0; top: 0;
186
+ width: 3px; height: 100%;
187
+ background: var(--yellow-500);
188
+ transition: transform var(--duration-base) var(--ease-standard);
189
+ }
190
+ ```
191
+
192
+ **Upload Drop Zone:**
193
+ ```css
194
+ @keyframes dashedBorderPulse {
195
+ 0%, 100% { border-color: var(--gray-400); }
196
+ 50% { border-color: var(--green-500); }
197
+ }
198
+ /* On drag-over: smooth scale up + green glow */
199
+ .dropzone--active {
200
+ transform: scale(1.02);
201
+ box-shadow: var(--shadow-green);
202
+ transition: all var(--duration-base) var(--ease-enter);
203
+ }
204
+ ```
205
+
206
+ **Document Processing Progress Bar:**
207
+ ```css
208
+ @keyframes progressShimmer {
209
+ from { background-position: -200% center; }
210
+ to { background-position: 200% center; }
211
+ }
212
+ .progress-bar--processing {
213
+ background: linear-gradient(
214
+ 90deg,
215
+ var(--green-500) 0%,
216
+ var(--yellow-500) 50%,
217
+ var(--green-500) 100%
218
+ );
219
+ background-size: 200% auto;
220
+ animation: progressShimmer 1.8s linear infinite;
221
+ }
222
+ ```
223
+
224
+ **Toast Notifications:**
225
+ - Enter: slide in from top-right, fade in — 220ms
226
+ - Exit: slide out to right, fade out — 180ms
227
+ - Auto-dismiss after 4s with shrinking progress bar at bottom
228
+
229
+ **Modal:**
230
+ - Backdrop: fade in 200ms
231
+ - Panel: scale from 0.95 → 1.0 + fade in, 250ms ease-enter
232
+ - Exit: reverse at 180ms
233
+
234
+ **Button States:**
235
+ ```css
236
+ .btn-primary {
237
+ transition: transform var(--duration-fast), box-shadow var(--duration-fast);
238
+ }
239
+ .btn-primary:hover { transform: translateY(-1px); box-shadow: var(--shadow-green); }
240
+ .btn-primary:active { transform: translateY(0px); box-shadow: none; }
241
+ ```
242
+
243
+ ---
244
+
245
+ ## 4. Application Architecture
246
+
247
+ ### 4.1 Tech Stack (for lovable.dev / v0.dev)
248
+ - **Framework:** Next.js 14 (App Router)
249
+ - **Styling:** Tailwind CSS with custom config extending AA Kenya tokens
250
+ - **Auth:** `@supabase/ssr` + `@supabase/supabase-js`
251
+ - **File Upload:** `@supabase/storage-js` (direct-to-bucket)
252
+ - **State:** React Context + `useState`/`useReducer` (no Redux)
253
+ - **Icons:** `lucide-react`
254
+ - **Charts:** `recharts`
255
+ - **Notifications:** Custom toast system (no library)
256
+ - **Drag & Drop:** `react-dropzone`
257
+
258
+ ### 4.2 Route Structure
259
+
260
+ ```
261
+ / → redirect to /auth/login
262
+ /auth/login → Login page (public)
263
+ /auth/forgot-password → Password reset (public)
264
+ /dashboard → Overview / home (protected)
265
+ /dashboard/documents → Document library (protected)
266
+ /dashboard/upload → Upload new document (protected)
267
+ /dashboard/analytics → Usage analytics (protected)
268
+ /dashboard/settings → Account & system settings (protected)
269
+ ```
270
+
271
+ ### 4.3 Environment Variables (Supabase + n8n)
272
+
273
+ ```env
274
+ # .env.local — provide these to the generated frontend
275
+ NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
276
+ NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
277
+ NEXT_PUBLIC_N8N_WEBHOOK_BASE=https://your-n8n-domain.com/webhook
278
+ NEXT_PUBLIC_N8N_API_SECRET=your-shared-secret
279
+ ```
280
+
281
+ ---
282
+
283
+ ## 5. Pages — Detailed Specifications
284
+
285
+ ---
286
+
287
+ ### 5.1 Authentication — Login Page (`/auth/login`)
288
+
289
+ **Layout:** Full-screen split. Left panel: brand/visual. Right panel: form.
290
+
291
+ **Left Panel (40% width on desktop, hidden on mobile):**
292
+ - Background: `var(--green-900)` — deep forest green
293
+ - AA Kenya logo centered (white version)
294
+ - Tagline: "Inspiring Mobility" in `--font-display`, `--yellow-500`
295
+ - Below tagline: checkered strip (horizontal, full width of panel)
296
+ - Subtle road texture overlay — dark semi-transparent diagonal lines at 15deg suggesting tarmac grain (`opacity: 0.07`)
297
+ - Quote at bottom: *"Equipping drivers with knowledge for safer roads"* — white, italic, small
298
+
299
+ **Right Panel (60% width on desktop, 100% on mobile):**
300
+ - Background: `--white`
301
+ - Top-right corner: small checkered square motif (decorative, 40×40px)
302
+ - Vertical center-aligned form
303
+
304
+ **Form Elements:**
305
+ ```
306
+ [AA Logo — small, green version] ← shown only on mobile
307
+ [H1: "Welcome back"] ← Barlow Condensed 700, green-700
308
+ [Subtext: "Sign in to your instructor dashboard"]
309
+
310
+ [Label: "Email address"]
311
+ [Input: email — full width]
312
+
313
+ [Label: "Password"]
314
+ [Input: password �� with show/hide toggle]
315
+
316
+ [Row: spacer | "Forgot password?" link] ← right-aligned, yellow-500
317
+
318
+ [Button: "Sign In"] ← full width, green-700 bg, white text
319
+
320
+ [Divider line]
321
+
322
+ [Small text: "Need access? Contact your administrator."]
323
+ ```
324
+
325
+ **Behaviour:**
326
+ - On submit: button shows loading spinner (replacing text), disabled
327
+ - On error: input borders turn `--error`, error message slides down below input (18px DM Sans, error red)
328
+ - On success: brief green checkmark flash, then router.push('/dashboard') with page transition
329
+ - Form validation: inline, on blur
330
+
331
+ **Animations:**
332
+ - Right panel form elements: staggered fadeSlideUp on mount (logo → title → inputs → button), 60ms delay each
333
+ - Left panel: subtle parallax on mouse move — logo shifts ±8px, tagline shifts ±4px
334
+ - Logo: on hover, slow rotation of the AA badge inner ring (360deg, 3s, infinite, ease-in-out)
335
+
336
+ ---
337
+
338
+ ### 5.2 Forgot Password (`/auth/forgot-password`)
339
+
340
+ Simple centered card, white background with green-900 header strip.
341
+ - Email input + "Send Reset Link" button
342
+ - Success state: green checkmark + "Check your email" message
343
+ - Back to login link
344
+
345
+ ---
346
+
347
+ ### 5.3 App Shell — Persistent Layout (all `/dashboard/*` routes)
348
+
349
+ **Structure:**
350
+ ```
351
+ ┌─────────────────────────────────────────────────────┐
352
+ │ TOPBAR (full width, 64px height) │
353
+ ├──────────┬──────────────────────────────────────────┤
354
+ │ │ │
355
+ │ SIDEBAR │ MAIN CONTENT AREA │
356
+ │ (240px) │ (fluid, scrollable) │
357
+ │ │ │
358
+ └──────────┴──────────────────────────────────────────┘
359
+ ```
360
+
361
+ **Topbar:**
362
+ - Background: `var(--green-700)`
363
+ - Left: hamburger menu icon (mobile) | "DriveSmart AI" wordmark in Barlow Condensed, white
364
+ - Right: notification bell (with badge count) | Avatar circle with initials | Dropdown (Profile, Settings, Sign Out)
365
+ - Bottom border: 4px checkered strip (green-700/yellow-500)
366
+ - On scroll: add `box-shadow: var(--shadow-md)` transition
367
+
368
+ **Sidebar:**
369
+ - Background: `var(--green-900)`
370
+ - Top: AA Kenya logo (small, white) with "Instructor Portal" label below in gray-400
371
+ - Navigation items:
372
+ ```
373
+ [icon] Overview /dashboard
374
+ [icon] Documents /dashboard/documents
375
+ [icon] Upload /dashboard/upload
376
+ [icon] Analytics /dashboard/analytics
377
+ ─────────────────
378
+ [icon] Settings /dashboard/settings
379
+ [icon] Sign Out
380
+ ```
381
+ - Active item: white text + yellow-500 left border (3px) + green-500 background
382
+ - Hover item: green-700 background, 200ms transition
383
+ - Collapse button at bottom (mobile: slides out as drawer)
384
+ - Bottom of sidebar: version number + "Powered by Gemini AI" in small gray text
385
+
386
+ **Mobile:** Sidebar is a slide-in drawer from left, triggered by hamburger. Backdrop overlay fades in.
387
+
388
+ ---
389
+
390
+ ### 5.4 Dashboard Overview (`/dashboard`)
391
+
392
+ **Hero Stats Row — 4 cards:**
393
+ ```
394
+ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
395
+ │ 📄 Documents │ │ ❓ Queries │ │ 👥 Learners │ │ ✅ Resolved │
396
+ │ 12 │ │ 1,247 │ │ 89 │ │ 94.2% │
397
+ │ +2 this week│ │ +18 today │ │ this month │ │ resolution │
398
+ └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
399
+ ```
400
+ - Card style: white bg, `--shadow-sm`, `--radius-lg`
401
+ - Featured card (Queries): `card--featured` with checkered top border
402
+ - Stats animate up from 0 on page load (easeOutExpo, 1000ms)
403
+ - Subtle hover: translateY(-2px) + shadow-md
404
+
405
+ **Quick Actions Row:**
406
+ ```
407
+ [+ Upload Document] [View Analytics] [Open WhatsApp Preview]
408
+ ```
409
+ - Primary: green-700 bg
410
+ - Secondary: outlined green-700 border
411
+ - Tertiary: yellow-500 bg, green-900 text
412
+
413
+ **Recent Documents Table:**
414
+ - Last 5 uploaded documents
415
+ - Columns: Name | Type | Status | Chunks | Uploaded | Actions
416
+ - Status badges: `pending` (gray), `processing` (yellow, animated shimmer), `ready` (green), `failed` (red)
417
+ - Actions: View, Delete
418
+ - "View all" link to `/dashboard/documents`
419
+
420
+ **Activity Feed (right column, 30% width):**
421
+ - Recent WhatsApp queries (last 10)
422
+ - Each entry: phone number (masked: +254 7XX XXX X89) | input type icon | query preview (truncated 50 chars) | time ago
423
+ - Input type icons: 💬 text | 🎙️ voice | 📷 image
424
+ - Scrollable with custom scrollbar styled in green
425
+
426
+ **Animations:**
427
+ - Stats cards: staggered entry (0ms, 80ms, 160ms, 240ms)
428
+ - Activity feed: items slide in from right, staggered 30ms each
429
+ - Processing status cards pulse with green glow
430
+
431
+ ---
432
+
433
+ ### 5.5 Document Library (`/dashboard/documents`)
434
+
435
+ **Header:**
436
+ ```
437
+ [H1: "Document Library"]
438
+ [Subtext: "X documents · Y total chunks indexed"]
439
+ [Right: "+ Upload New" button]
440
+ ```
441
+
442
+ **Filters & Search Bar:**
443
+ ```
444
+ [🔍 Search documents...] [Type ▾] [Status ▾] [Date ▾] [Sort ▾]
445
+ ```
446
+
447
+ **Document Grid (default view) | List toggle:**
448
+
449
+ **Card Mode (2–3 columns responsive grid):**
450
+ ```
451
+ ┌─────────────────────────────┐
452
+ │ [Checkered top border 5px] │
453
+ │ [🗎 icon — large, green] │
454
+ │ │
455
+ │ [Document name — truncated] │
456
+ │ [File type badge] │
457
+ │ │
458
+ │ [Chunks: 47] [Size: 2.3MB] │
459
+ │ [Uploaded: 2 days ago] │
460
+ │ [Status badge] │
461
+ │ │
462
+ │ [──────────────────────────] │
463
+ │ [Delete] [View →] │
464
+ └─────────────────────────────┘
465
+ ```
466
+
467
+ **List Mode:**
468
+ - Full-width table rows with hover highlight (green-100)
469
+ - Sortable column headers with sort direction arrows
470
+
471
+ **Empty State:**
472
+ ```
473
+ [Large dashed border box — center of page]
474
+ [Document illustration or road-sign icon in green-200]
475
+ [H2: "No documents yet"]
476
+ [Body: "Upload your first training material to get started."]
477
+ [Button: "+ Upload Document"]
478
+ ```
479
+
480
+ **Delete Confirmation:**
481
+ - Modal with warning icon
482
+ - "This will remove [filename] and all [N] indexed chunks. Learners will no longer have access to this content."
483
+ - [Cancel] [Delete Document] — red destroy button
484
+
485
+ ---
486
+
487
+ ### 5.6 Document Upload (`/dashboard/upload`)
488
+
489
+ **Layout:** Centered, max-width 720px
490
+
491
+ **Step Indicator:**
492
+ ```
493
+ [1 Select File] ──── [2 Processing] ──── [3 Complete]
494
+ ```
495
+ Yellow active step, green completed steps, gray-400 upcoming.
496
+
497
+ **Step 1 — File Selection:**
498
+
499
+ Drop Zone:
500
+ ```
501
+ ┌─────────────────────────────────────────────────┐
502
+ │ │
503
+ │ [Cloud upload icon — 48px, green-700] │
504
+ │ │
505
+ │ Drop your file here, or click to browse │
506
+ │ Supports: PDF, DOCX, TXT · Max 50MB │
507
+ │ │
508
+ └─────────────────────────────────────────────────┘
509
+ ```
510
+ - Border: 2px dashed gray-300, radius-xl
511
+ - Drag-over: scale(1.02), green border, green-100 bg, shadow-green
512
+ - File preview after selection: filename, size, type icon, remove button
513
+
514
+ Document Name Field:
515
+ - Auto-populated from filename (editable)
516
+ - Character count: 0/100
517
+
518
+ [Upload & Process] button — disabled until file selected, green-700
519
+
520
+ **Step 2 — Processing (auto-navigated after upload):**
521
+ ```
522
+ [Animated document icon with pages flipping — CSS animation]
523
+
524
+ Processing "Traffic Rules Kenya 2024.pdf"
525
+
526
+ [Progress bar — green-500 shimmer animation]
527
+
528
+ Step 1 of 3: Uploading to storage... ✅
529
+ Step 2 of 3: Extracting text... ⏳ (spinning indicator)
530
+ Step 3 of 3: Generating embeddings... ○ (pending)
531
+
532
+ This may take 30–90 seconds depending on document size.
533
+ ```
534
+ - Poll n8n webhook every 3 seconds for status update
535
+ - Polled via: `GET /webhook/document-status?id={id}` with JWT
536
+
537
+ **Step 3 — Complete:**
538
+ ```
539
+ [Large green checkmark — animated draw-on SVG stroke]
540
+
541
+ "Training material ready!"
542
+
543
+ "Traffic Rules Kenya 2024.pdf"
544
+ 47 knowledge chunks indexed and ready for learner queries.
545
+
546
+ [View in Library] [Upload Another]
547
+ ```
548
+
549
+ ---
550
+
551
+ ### 5.7 Analytics (`/dashboard/analytics`)
552
+
553
+ **Date Range Selector:** Today | Last 7d | Last 30d | Custom
554
+ Sticky below topbar on scroll.
555
+
556
+ **Top Row — KPI Cards:**
557
+ ```
558
+ Total Queries | Voice Queries | Image Queries | Avg Response Time
559
+ ```
560
+
561
+ **Input Type Distribution — Donut Chart:**
562
+ - Colors: Text=green-700, Voice=yellow-500, Image=green-500
563
+ - Recharts `PieChart` + custom tooltip
564
+
565
+ **Queries Over Time — Line Chart:**
566
+ - X-axis: dates | Y-axis: query count
567
+ - Green line, yellow fill below line at 15% opacity
568
+ - Smooth curve (`type="monotone"`)
569
+ - Custom dot on hover: green circle + tooltip card
570
+
571
+ **Top Questions Table:**
572
+ ```
573
+ # | Question (truncated) | Count | Input Type | Last Asked
574
+ ─────────────────────────────────────────────────────────
575
+ 1 | What does a yield sign mean? | 34 | 💬 | 2h ago
576
+ 2 | Speed limit in school zones | 28 | 🎙️ | 5h ago
577
+ ...
578
+ ```
579
+ - Hover row: green-100 bg
580
+
581
+ **Document Usage Breakdown:**
582
+ - Horizontal bar chart showing which documents are cited most in responses
583
+ - Bars: green-700 fill, yellow-500 for top document
584
+
585
+ ---
586
+
587
+ ### 5.8 Settings (`/dashboard/settings`)
588
+
589
+ **Sections (tabbed or accordion):**
590
+
591
+ **Profile:**
592
+ - Name, Email (read-only — managed by Supabase)
593
+ - Change Password form
594
+ - Avatar upload (stored in Supabase Storage)
595
+
596
+ **WhatsApp Configuration:**
597
+ ```
598
+ WhatsApp Phone Number ID: [__________________]
599
+ Meta Access Token: [__________________] [Show/Hide]
600
+ Webhook Verify Token: [__________________] [Copy]
601
+ n8n Webhook URL: [read-only display] [Copy]
602
+ ```
603
+ Each field: save inline with green checkmark on success.
604
+
605
+ **AI Configuration:**
606
+ ```
607
+ Response Language: [English ▾] [Swahili ▾] [Both]
608
+ Max Response Length: [Concise] [Standard ●] [Detailed]
609
+ Web Search: [Toggle — ON/OFF]
610
+ Voice Reply Language: [English ▾]
611
+ ```
612
+
613
+ **Danger Zone:**
614
+ - "Delete all documents and embeddings" — outlined red button → confirmation modal
615
+
616
+ ---
617
+
618
+ ## 6. Component Library
619
+
620
+ ### 6.1 Button Variants
621
+
622
+ ```
623
+ [Primary] → bg: green-700 | text: white | hover: green-500
624
+ [Secondary] → bg: white | border: green-700 | text: green-700 | hover: green-100
625
+ [Accent] → bg: yellow-500 | text: green-900 | hover: yellow-300
626
+ [Danger] → bg: white | border: error | text: error | hover: error bg
627
+ [Ghost] → no bg/border | text: green-700 | hover: green-100 bg
628
+ [Loading] → spinner replaces text, disabled, reduced opacity
629
+ ```
630
+
631
+ All buttons: `--radius-md`, `--duration-fast` transition, `translateY(-1px)` on hover.
632
+
633
+ ### 6.2 Status Badges
634
+
635
+ ```css
636
+ .badge--pending { bg: gray-200; color: gray-600; }
637
+ .badge--processing { bg: yellow-100; color: yellow-700; /* + shimmer */ }
638
+ .badge--ready { bg: green-100; color: green-700; }
639
+ .badge--failed { bg: error-light; color: error; }
640
+ ```
641
+
642
+ ### 6.3 Input Fields
643
+
644
+ ```css
645
+ .input {
646
+ border: 1.5px solid var(--gray-200);
647
+ border-radius: var(--radius-md);
648
+ padding: 10px 14px;
649
+ font: var(--font-body);
650
+ transition: border-color var(--duration-fast), box-shadow var(--duration-fast);
651
+ }
652
+ .input:focus {
653
+ border-color: var(--green-700);
654
+ box-shadow: 0 0 0 3px rgba(26, 92, 42, 0.12);
655
+ outline: none;
656
+ }
657
+ .input--error {
658
+ border-color: var(--error);
659
+ box-shadow: 0 0 0 3px rgba(214, 48, 49, 0.10);
660
+ }
661
+ ```
662
+
663
+ ### 6.4 Toast Notification
664
+
665
+ Position: top-right, 16px from edges, z-index: 9999
666
+ ```
667
+ ┌──────────────────────────────┐
668
+ │ ✅ Document uploaded │
669
+ │ Processing has started │
670
+ │ ████████████░░░░░░░░░░░░░░ │ ← auto-dismiss progress bar
671
+ └──────────────────────────────┘
672
+ ```
673
+ Types: success (green), warning (yellow), error (red), info (blue)
674
+ Animation: slide in from right + fade, auto-dismiss 4s
675
+
676
+ ---
677
+
678
+ ## 7. Supabase Integration
679
+
680
+ ### 7.1 Auth Setup
681
+
682
+ ```javascript
683
+ // lib/supabase.js
684
+ import { createClient } from '@supabase/supabase-js'
685
+
686
+ export const supabase = createClient(
687
+ process.env.NEXT_PUBLIC_SUPABASE_URL,
688
+ process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
689
+ )
690
+ ```
691
+
692
+ ```javascript
693
+ // middleware.js — protect /dashboard/* routes
694
+ import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'
695
+
696
+ export async function middleware(req) {
697
+ const res = NextResponse.next()
698
+ const supabase = createMiddlewareClient({ req, res })
699
+ const { data: { session } } = await supabase.auth.getSession()
700
+ if (!session && req.nextUrl.pathname.startsWith('/dashboard')) {
701
+ return NextResponse.redirect(new URL('/auth/login', req.url))
702
+ }
703
+ return res
704
+ }
705
+
706
+ export const config = { matcher: ['/dashboard/:path*'] }
707
+ ```
708
+
709
+ ### 7.2 File Upload to Supabase Storage
710
+
711
+ ```javascript
712
+ async function uploadDocument(file, documentName) {
713
+ // 1. Insert metadata record first
714
+ const { data: doc, error: dbError } = await supabase
715
+ .from('documents')
716
+ .insert({ name: documentName, file_type: file.type, status: 'pending' })
717
+ .select()
718
+ .single()
719
+
720
+ if (dbError) throw dbError
721
+
722
+ // 2. Upload file to storage
723
+ const filePath = `${doc.id}/${file.name}`
724
+ const { error: storageError } = await supabase.storage
725
+ .from('training-documents')
726
+ .upload(filePath, file)
727
+
728
+ if (storageError) throw storageError
729
+
730
+ // 3. Get signed URL (1 hour expiry)
731
+ const { data: urlData } = await supabase.storage
732
+ .from('training-documents')
733
+ .createSignedUrl(filePath, 3600)
734
+
735
+ // 4. Trigger n8n ingestion workflow
736
+ const session = await supabase.auth.getSession()
737
+ await fetch(`${process.env.NEXT_PUBLIC_N8N_WEBHOOK_BASE}/ingest`, {
738
+ method: 'POST',
739
+ headers: {
740
+ 'Content-Type': 'application/json',
741
+ 'Authorization': `Bearer ${session.data.session.access_token}`,
742
+ 'x-api-secret': process.env.NEXT_PUBLIC_N8N_API_SECRET
743
+ },
744
+ body: JSON.stringify({
745
+ document_id: doc.id,
746
+ signed_url: urlData.signedUrl,
747
+ file_name: file.name,
748
+ file_type: file.type
749
+ })
750
+ })
751
+
752
+ return doc.id
753
+ }
754
+ ```
755
+
756
+ ### 7.3 Polling Document Status
757
+
758
+ ```javascript
759
+ // Poll every 3 seconds until status is 'ready' or 'failed'
760
+ async function pollDocumentStatus(documentId, onUpdate) {
761
+ const interval = setInterval(async () => {
762
+ const { data } = await supabase
763
+ .from('documents')
764
+ .select('status, chunk_count')
765
+ .eq('id', documentId)
766
+ .single()
767
+
768
+ onUpdate(data)
769
+
770
+ if (data.status === 'ready' || data.status === 'failed') {
771
+ clearInterval(interval)
772
+ }
773
+ }, 3000)
774
+
775
+ return () => clearInterval(interval) // cleanup
776
+ }
777
+ ```
778
+
779
+ ### 7.4 Fetching Analytics from n8n
780
+
781
+ ```javascript
782
+ async function fetchAnalytics(range = '7d') {
783
+ const session = await supabase.auth.getSession()
784
+ const res = await fetch(
785
+ `${process.env.NEXT_PUBLIC_N8N_WEBHOOK_BASE}/stats?range=${range}`,
786
+ {
787
+ headers: {
788
+ 'Authorization': `Bearer ${session.data.session.access_token}`,
789
+ 'x-api-secret': process.env.NEXT_PUBLIC_N8N_API_SECRET
790
+ }
791
+ }
792
+ )
793
+ return res.json()
794
+ }
795
+ ```
796
+
797
+ ---
798
+
799
+ ## 8. Responsive Breakpoints
800
+
801
+ ```css
802
+ /* Mobile-first */
803
+ --bp-sm: 640px; /* large phone landscape */
804
+ --bp-md: 768px; /* tablet portrait */
805
+ --bp-lg: 1024px; /* tablet landscape / small laptop */
806
+ --bp-xl: 1280px; /* desktop */
807
+ --bp-2xl: 1536px; /* wide desktop */
808
+ ```
809
+
810
+ | Element | Mobile (<768px) | Tablet (768–1024px) | Desktop (>1024px) |
811
+ |---|---|---|---|
812
+ | Sidebar | Hidden (drawer) | Collapsed (icons only) | Full (240px) |
813
+ | Stats cards | 2×2 grid | 4 in a row | 4 in a row |
814
+ | Document grid | 1 column | 2 columns | 3 columns |
815
+ | Login split | Single panel | Single panel | Split 40/60 |
816
+ | Analytics charts | Stacked | Side by side | Side by side |
817
+
818
+ ---
819
+
820
+ ## 9. Accessibility
821
+
822
+ - All interactive elements: `focus-visible` ring in `green-700` at 2px offset
823
+ - Color contrast: all text meets WCAG AA (4.5:1 minimum)
824
+ - Screen reader: `aria-label` on all icon-only buttons
825
+ - Form inputs: `id`/`htmlFor` linked labels
826
+ - Status badges: `role="status"` + `aria-live="polite"` for processing states
827
+ - Modal: focus trap, `aria-modal="true"`, `Escape` key closes
828
+ - Keyboard navigation: full tab order, sidebar navigable with arrow keys
829
+
830
+ ---
831
+
832
+ ## 10. Performance Requirements
833
+
834
+ - Lighthouse score target: 90+ (Performance, Accessibility, Best Practices)
835
+ - First Contentful Paint: < 1.5s
836
+ - Images: next/image with lazy loading
837
+ - Code splitting: automatic via Next.js App Router
838
+ - Supabase queries: select only needed columns (never `select *`)
839
+ - Analytics charts: lazy-loaded (only render when tab is visible)
840
+
841
+ ---
842
+
843
+ ## 11. Error States
844
+
845
+ Every async action must handle three states: loading, success, error.
846
+
847
+ | Scenario | UI Behaviour |
848
+ |---|---|
849
+ | Login fails — wrong password | Inline error below password field |
850
+ | Login fails — no network | Toast: "Connection error. Check your internet." |
851
+ | Upload — file too large | Inline error in drop zone before upload |
852
+ | Upload — server error | Toast error + revert to Step 1 with retry button |
853
+ | Document delete fails | Toast error, document remains in list |
854
+ | Analytics load fails | Skeleton loaders replaced with "Could not load data" + retry |
855
+ | n8n unreachable | Yellow warning banner at top: "AI backend is currently unavailable." |
856
+
857
+ ---
858
+
859
+ ## 12. Skeleton Loading States
860
+
861
+ Every data-loaded section must show a skeleton before data arrives:
862
+ - Stat cards: gray-200 rounded rectangles, `animation: pulse 1.5s ease-in-out infinite`
863
+ - Table rows: alternating gray-100/gray-50 bars
864
+ - Chart areas: gray-200 rounded rectangle same size as chart
865
+ - Document cards: gray placeholder blocks
866
+
867
+ ```css
868
+ @keyframes skeleton-pulse {
869
+ 0%, 100% { opacity: 1; }
870
+ 50% { opacity: 0.5; }
871
+ }
872
+ .skeleton {
873
+ background: var(--gray-200);
874
+ border-radius: var(--radius-sm);
875
+ animation: skeleton-pulse 1.5s ease-in-out infinite;
876
+ }
877
+ ```
878
+
879
+ ---
880
+
881
+ *End of PRD — DriveSmart AI Instructor Dashboard v1.0*
882
+ *Built for AA Kenya driving school branding. Ready for lovable.dev / v0.dev generation.*