Purpose: Screen-by-screen component breakdown, role-based UI rules, state management patterns, and design system specifications for the React/TypeScript frontend. References the wireframe mockups and maps every UI element to its backend API endpoint.
The application uses a dark-first design with the following base palette:
| Token | Hex | Usage |
|---|---|---|
--bg-primary |
#0f0f1a |
Page background |
--bg-secondary |
#1a1a2e |
Sidebar, cards |
--bg-tertiary |
#16213e |
Elevated surfaces, modals |
--bg-input |
#0f3460 |
Input fields |
--text-primary |
#e0e0e0 |
Primary text |
--text-secondary |
#a0a0a0 |
Secondary/muted text |
--text-heading |
#ffffff |
Headings |
--accent-green |
#00c853 |
Primary actions, success states, Active/Verified badges |
--accent-red |
#ff4444 |
Destructive actions, error states, Missing/Inactive badges |
--accent-amber |
#ffab00 |
Warning states, Damaged badges |
--accent-blue |
#2196f3 |
Links, selected sidebar items |
--accent-teal |
#00bcd4 |
KPI card accents |
--border |
#2a2a3e |
Card/table borders |
- Font:
Inter(Google Fonts) — clean, modern, highly legible at small sizes - Headings:
Inter 600(semi-bold) - Body:
Inter 400(regular) - Monospace (asset tags, codes):
JetBrains MonoorFira Code
- Sidebar width:
240px(fixed) - Content area: fluid, with
max-width: 1200pxandpadding: 24px - Card padding:
16px–20px - Border radius:
8px(cards),6px(buttons/inputs),12px(badges)
Every authenticated screen shares the same shell:
┌──────────────────────────────────────────────────────┐
│ Sidebar (fixed left) │ Content Area (scrollable) │
│ │ │
│ ┌─────────────────┐ │ ┌────────────────────────┐ │
│ │ AssetFlow (logo)│ │ │ Page Header │ │
│ │ │ │ │ (title + actions) │ │
│ │ Dashboard │ │ ├────────────────────────┤ │
│ │ Organization │ │ │ │ │
│ │ Assets │ │ │ Page Content │ │
│ │ Allocation & │ │ │ │ │
│ │ Transfer │ │ │ │ │
│ │ Resource │ │ │ │ │
│ │ Booking │ │ │ │ │
│ │ Maintenance │ │ │ │ │
│ │ Audit │ │ │ │ │
│ │ Reports │ │ │ │ │
│ │ Notifications │ │ │ │ │
│ └─────────────────┘ │ └────────────────────────┘ │
└──────────────────────────────────────────────────────┘
- Sidebar has a highlighted active item (filled background, accent color)
- Navigation items are conditionally shown based on user role (UI-only — backend enforces the real access)
- User name displayed at bottom of sidebar with role badge
| Label | Route | Visible To |
|---|---|---|
| Dashboard | /dashboard |
All roles |
| Organization Setup | /org |
Admin |
| Assets | /assets |
All roles |
| Allocation & Transfer | /allocations |
All roles |
| Resource Booking | /bookings |
All roles |
| Maintenance | /maintenance |
All roles |
| Audit | /audits |
Admin, Asset Manager |
| Reports | /reports |
Admin, Asset Manager, Dept Head (scoped) |
| Notifications | /notifications |
All roles |
Route: /login, /signup, /forgot-password
Layout: Centered card on dark background, NO sidebar (unauthenticated).
Components:
LoginForm— Email input, password input, "Forgot password" link, Login buttonSignupForm— Name, email, password inputs, info notice: "Sign up creates an employee account. Admin roles assigned later." , "Create Account" buttonForgotPasswordForm— Email input, submit button
API Calls:
POST /auth/login→ store tokens, redirect to/dashboardPOST /auth/signup→ show success, redirect to/loginPOST /auth/forgot-password→ show generic success message
Key UX Detail (from mockup): The signup form shows a clear notice that the account starts as Employee — this matches the no-self-elevation guarantee.
Route: /dashboard
Components:
KPICardGrid— 6 cards in 2 rows of 3:- Row 1: Available (count), Allocated (count), Available (separate metric)
- Row 2: Active Bookings, Pending Transfers, Upcoming Returns
OverdueBar— Red/green progress bar showing overdue vs. on-time returnsUserGreeting— "Welcome, [Name]" with user avatar/iconQuickActions— 3 buttons: "+ Register Asset", "Book Resource", "Raise Request"RecentActivity— Last 5-10 activity log entries (compact list)
API Calls:
GET /dashboard/kpis→ populates all KPI cards (role-scoped server-side)GET /activity-log?limit=10→ populates recent activity
Role Behavior:
- Admin/Asset Manager: sees org-wide KPIs
- Dept Head: sees department-scoped KPIs
- Employee: sees only their own allocations/bookings
Route: /org
Layout: Three horizontal tabs: Departments | Categories | Employees + "+ Add" button
Components:
TabBarwith 3 tabsDepartmentsTab:DataTablewith columns: Department, Head, Parent Dept, Status- Status shown as colored badge (green Active, red Inactive)
- Inline edit or modal for create/edit
CategoriesTab:DataTablewith columns: Name, Custom Fields count, Status- Modal for creating with dynamic custom field builder
EmployeesTab:DataTablewith columns: Name, Email, Department, Role, Status- "Promote" action button (Admin-only) — triggers
/org/employees/:id/promote
API Calls:
GET /org/departments,POST /org/departments,PATCH /org/departments/:idGET /org/categories,POST /org/categories,PATCH /org/categories/:idGET /org/employees,POST /org/employees/:id/promote
Route: /assets
Components:
SearchBar— "Search by tag, serial, or QR code..." with full-text searchFilterPills— Category, Status, Department dropdownsRegisterAssetButton— "+ Register Asset" (opens modal/form)AssetTable— columns: Tag, Name, Category, Status, Location- Tag displayed in monospace font
- Status as colored badge
- Clickable rows → asset detail page
RegisterAssetModal/Form— Name, Category (dropdown), Serial Number, Acquisition Date, Cost, Condition, Location, is_bookable checkbox, photos upload
API Calls:
GET /assets?q=&category=&status=&department=→ search/filterPOST /assets→ register new assetGET /assets/:id→ asset detail
Route: /allocations
Components:
AssetSelector— Search and select an asset (shows tag + name)AllocationStatus— Shows current allocation state:- If available: allocation form (employee/dept dropdown, expected return date)
- If already allocated: conflict banner — "Already allocated to [Name] (Department). Direct allocation is blocked — submit a Transfer request below"
TransferRequestForm— From (pre-filled), To (employee dropdown), Reason textarea, "Submit Request" button (green)AllocationHistory— Timeline/list of past allocations for the selected asset
API Calls:
POST /allocations→ attempt allocation, handle409 ALLOCATION_CONFLICTPOST /transfers→ create transfer requestGET /allocations?assetId=→ historyGET /transfers→ pending transfers list
Key UX (from mockup): The conflict banner is shown inline with a red/warning style, and the transfer form appears below it — this is the direct visual representation of the 409 conflict response.
Route: /bookings
Components:
ResourceSelector— Dropdown to pick a bookable resourceCalendarView— Vertical timeline showing hourly slots (9:00–17:00)- Booked slots shown as filled blocks with booking info
- Conflicting/rejected slots shown with dotted red borders
- Available slots are empty/clickable
BookSlotButton— "Book a slot" (green) → opens booking formBookingForm— Start time, end time, optional department
API Calls:
GET /bookings/resource/:assetId/calendar?date=→ calendar dataPOST /bookings→ create booking, handle409 BOOKING_OVERLAPPOST /bookings/:id/cancel→ cancel booking
Route: /maintenance
Layout: Kanban board with 5 columns
Components:
KanbanBoardwith columns:- Pending — newly raised requests
- Approved — approved, awaiting technician
- Technician Assigned — technician named
- In Progress — work ongoing
- Resolved — completed
MaintenanceCard— Shows asset tag, brief description, priority badge- Cards can be dragged between columns (triggers the appropriate API endpoint)
- Or: action buttons on each card for the next valid transition
RaiseRequestButton— Opens form to create new maintenance request
API Calls:
GET /maintenance→ all requests grouped by statusPOST /maintenance→ raise new requestPOST /maintenance/:id/approve→ move to ApprovedPOST /maintenance/:id/assign-technician→ assignPOST /maintenance/:id/start→ move to In ProgressPOST /maintenance/:id/resolve→ resolve
Key UX (from mockup): Bottom note explains: "Approving a card moves the asset to under maintenance, resolving returns it to available"
Route: /audits
Components:
AuditCycleHeader— Shows cycle name, date range, assigned auditorsAuditChecklist— Table with columns: Asset (tag), Expected Location, Verification- Verification column: dropdown or button group for
Verified | Missing | Damaged - Color-coded: green Verified, red Missing, amber Damaged
- Verification column: dropdown or button group for
DiscrepancyBanner— "[N] assets flagged — discrepancy report generated automatically"CloseAuditButton— "Close audit cycle" (destructive action, confirmation modal)CreateAuditForm— Scope (department, location), date range, auditor selection
API Calls:
GET /audits→ list cyclesGET /audits/:id/results→ checklist dataPATCH /audits/:id/results/:assetId→ submit verification resultGET /audits/:id/discrepancy-report→ discrepancy dataPOST /audits/:id/close→ close cycle
Route: /reports
Components:
UtilizationChart— Bar chart: "Utilization by Department"MaintenanceChart— Line chart: "Maintenance Frequency"MostUsedAssets— Ranked list of most-used assetsIdleAssets— List of idle/underused assetsLifecycleAlerts— "Assets due for maintenance / nearing retirement"ExportButton— "Export report" (red/orange) → triggers CSV/PDF download
API Calls:
GET /reports/utilization→ bar chart dataGET /reports/maintenance-frequency→ line chart dataGET /reports/upcoming-lifecycle→ lifecycle alertsGET /reports/department-allocation-summary→ department breakdownGET /reports/:reportName/export?format=csv|pdf→ file download
Charting Library: Recharts (React-native, composable, works well with dark themes)
Route: /notifications
Layout: Tab-based: All | Alerts | Approvals | Bookings
Components:
TabBar— Filter tabs for notification typesNotificationList— Chronological list with:- Icon per notification type
- Description text
- Relative timestamp ("2m ago", "1d ago", "5d ago")
- Unread indicator (dot or highlight)
MarkAllReadButton— Bulk mark-read action
API Calls:
GET /notifications?unreadOnly=true→ unread notificationsPATCH /notifications/:id/read→ mark individual as readPATCH /notifications/read-all→ bulk mark readGET /activity-log→ full activity log (separate sub-tab or view)
All data from the API is managed via React Query:
- Query keys follow the pattern:
['assets'],['assets', assetId],['dashboard-kpis'],['bookings', { resourceId, date }] - Mutations invalidate related queries automatically:
- Allocating an asset → invalidate
['assets'],['dashboard-kpis'],['allocations'] - Approving maintenance → invalidate
['maintenance'],['assets'],['dashboard-kpis'] - Closing audit → invalidate
['audits'],['assets']
- Allocating an asset → invalidate
- Form inputs, modal open/close, sidebar collapse state
- Current selected filters (category, status, department on asset list)
- Notification unread count (synced from React Query cache)
All API errors follow the backend's standard envelope. The frontend handles:
switch (error.code) {
case 'ALLOCATION_CONFLICT':
// Show conflict banner with current holder info
break;
case 'BOOKING_OVERLAP':
// Show overlap error with conflicting booking details
break;
case 'INVALID_STATE_TRANSITION':
// Show toast: "This action is not allowed in the current state"
break;
case 'FORBIDDEN':
// Show toast: "You don't have permission for this action"
break;
case 'VALIDATION_ERROR':
// Map field-level errors to form inputs
break;
}- Desktop (≥1024px): Full sidebar + content area
- Tablet (768–1023px): Collapsible sidebar (hamburger toggle)
- Mobile (≤767px): Bottom navigation bar replaces sidebar, simplified table views (card layout on small screens)
All tables switch to a stacked card layout on mobile, with each row becoming a card showing the most important 3-4 fields.