15 KiB
Frontend Pages Specification — Swish Auto Care GMS
React 18 + TypeScript + Vite + React Router v6 + CSS Modules
Global Layout
The app uses a persistent shell layout after login:
┌─────────────────────────────────────────────────────────┐
│ TOP BAR: Logo | "Swish Auto Care GMS" | Date | Admin ▼ │
├──────────┬──────────────────────────────────────────────┤
│ │ │
│ SIDEBAR │ PAGE CONTENT │
│ (Left) │ │
│ │ │
└──────────┴──────────────────────────────────────────────┘
Sidebar Nav Items (in order):
- 🏠 Dashboard
- 🔧 Job Cards
- 📋 Status Board
- 👤 Staff
- 📅 Attendance
- 💰 Salary & Payments
- 🔩 Parts & Inventory
- 🛒 Parts Sales
- 💵 Cash Ledger
- 📊 Accounts
- 📈 Daily Report
- 📤 Excel Exports
TopBar shows: Current date in DD/MM/YYYY format | "Admin" label | Logout button
Page 1 — Login (/login)
Full-screen centered card. No sidebar/topbar.
Fields:
- Username (text input)
- Password (password input)
- "Login" button
Behavior:
- On submit: POST /api/auth/login
- On success: Store JWT in localStorage, store user in AuthContext, redirect to /dashboard
- On fail: Show "Invalid username or password" error below form
- Auto-redirect to /dashboard if already logged in
Design: Clean card, Swish logo placeholder at top, business colors.
Page 2 — Dashboard (/dashboard)
Today's overview. Data from GET /api/dashboard/today.
6 Widget Cards:
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Active Jobs │ │ Completed Jobs │ │ Today's Revenue │
│ 4 🔧 │ │ 7 ✅ │ │ ₹28,500 💰 │
└──────────────────┘ └──────────────────┘ └──────────────────┘
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Cash Balance │ │ Staff Present │ │ Quick Actions │
│ ₹14,200 │ │ 3 / 4 👤 │ │ + New Job Card │
│ │ │ │ │ Mark Attendance │
└──────────────────┘ └──────────────────┘ └──────────────────┘
Quick action buttons: "New Job Card" → /job-cards/new | "Mark Attendance" → /attendance | "Daily Report" → /daily-report
Data refresh: On page load only (no auto-refresh needed for demo).
Page 3 — Job Cards (/job-cards)
List of all job cards with search, filter, and create button.
Top bar:
- Search by Reg No. (text input, real-time filter)
- Filter by Status (All / Active / Done) — dropdown
- Filter by Date (date picker, default today)
- "+ New Job Card" button (primary, top right)
Table columns:
| Job No | Type | Reg No | Car | Owner | Service | Staff | Amount | Status | Date | Actions |
|---|
- Type badge: "Full" (blue) / "Wash" (teal)
- Status badge: "Active" (amber) / "Done" (green)
- Actions: View 👁 | Edit ✏ (only if Active) | Close ✓ (only if Active)
"Close Job" action: Opens a modal:
- Final Amount (₹) — required
- Payment Mode — Cash / UPI / Card dropdown
- "Mark as Done" button
Page 4 — New Job Card (/job-cards/new)
Toggle at top: "Full Service Job" | "Quick Wash Job" — determines which form shows.
Full Job Form:
- Registration No. * (text)
- Car Model / Name * (text)
- Owner Name * (text)
- Mobile No. * (text, 10 digits)
- Service Type * (dropdown: Wash, Polish, PPF, Detailing, Interior Cleaning, Ceramic Coating, Underbody Coating, Other)
- Assigned Staff * (dropdown — fetched from active staff list)
- Estimated Amount ₹ (number)
- Notes / Remarks (textarea)
Quick Wash Form (simplified):
- Registration No. * (text)
- Car Name * (text)
- Mobile No. (optional)
- Wash Type * (dropdown: Basic Wash, Premium Wash, Interior Clean)
- Assigned Staff * (dropdown)
- Amount ₹ * (number)
Buttons: "Create Job Card" (primary) | "Cancel" (secondary)
On success: Show toast "Job card SAC-YYYYMMDD-001 created" | Redirect to /job-cards
Page 5 — Status Board (/status-board)
Two-column Kanban-style view:
┌─────── ACTIVE (4) ──────────┐ ┌─────── DONE (7) ────────────┐
│ SAC-20260324-001 │ │ SAC-20260324-003 │
│ GJ06AB1234 — Swift │ │ GJ05XY9900 — Fortuner │
│ Full Detailing | Rajan M. │ │ Quick Wash | Suresh K. │
│ Est. ₹5,000 | 10:30 AM │ │ ₹350 | 09:15 AM ✅ │
│ [Mark Done] │ │ │
└─────────────────────────────┘ └─────────────────────────────┘
Each active card has a "Mark Done" button that opens the Close Job modal (same as in Job Cards list).
Page 6 — Staff (/staff)
Top bar: "+ Add Staff" button | Search by name
Table:
| Staff Code | Name | Role | Mobile | Joining | Salary | Status | Actions |
|---|
- Status: "Active" (green toggle) / "Inactive" (gray toggle) — click to toggle
- Actions: Edit ✏
Add/Edit Staff Modal:
- Full Name *
- Role / Designation *
- Mobile No.
- Date of Joining (date picker)
- Monthly Salary ₹ *
- Active toggle
Page 7 — Attendance (/attendance)
Date selector at top (date picker, default today).
Attendance grid (bulk mark):
┌─────────────────────────────────────────────────────────┐
│ Date: 24/03/2026 [Save Attendance] │
├──────────────────┬─────────────────────────────────────┤
│ Rajan Mehta │ ● Present ○ Absent ○ Half Day │
│ Suresh Kumar │ ○ Present ● Absent ○ Half Day │
│ Vikram Patel │ ○ Present ○ Absent ● Half Day │
│ Arjun Shah │ ○ Present ○ Absent ○ Half Day │
└──────────────────┴─────────────────────────────────────┘
All staff listed. Radio button per row. "Save Attendance" calls POST /api/attendance/bulk.
Below grid: "View Monthly Summary" → Modal or accordion per staff showing month grid.
Page 8 — Salary & Payments (/salary)
Two sections:
Section A — New Payment:
- Select Staff * (dropdown)
- Payment Month * (month/year picker or text e.g. "March 2026")
- [Auto-calculate button] → calls GET /api/salary/calculate/:id → fills Days Present & Calculated Salary fields
- Days Present (auto-filled, editable)
- Calculated Salary ₹ (auto-filled, read-only display)
- Payment Amount ₹ * (editable — admin override)
- Payment Type * (Full Salary / Advance / Bonus / Deduction)
- Payment Mode * (Cash / Bank / UPI)
- Notes (textarea)
- "Record Payment" button
Section B — Payment History Table:
- Filter by Staff and Month
- Columns: Date | Staff | Month | Type | Mode | Amount | Notes
Page 9 — Parts & Inventory (/parts)
Top bar: "+ Add Part" | Search | Filter by Category | Low Stock toggle
Table:
| Part Code | Name | Category | Unit | Buy Price | Sell Price | Stock | Low Stock? | Actions |
|---|
- Low Stock?: Red badge if stock_qty ≤ low_stock_alert
- Actions: Edit ✏ | Restock + (opens restock modal)
Add/Edit Part Modal: All fields from parts table. Restock Modal: Adjustment quantity input + reason.
Page 10 — Parts Sales (/parts-sales)
Top bar: "+ Record Sale" button | Date filter
Sales history table:
| Date | Part | Qty | Unit Price | Total | Customer | Payment | Job Card |
|---|
New Sale Modal:
- Select Part * (searchable dropdown showing part name + stock)
- Link to Job Card? (optional — searchable dropdown of active + recent job cards)
- Quantity * (number — validated against stock)
- Unit Price ₹ (auto-filled from part.selling_price, editable)
- Total Amount (auto-calculated, display only)
- Customer Name (optional text)
- Payment Mode * (Cash / UPI / Card)
- "Record Sale" button
Validation: If quantity > stock_qty, show error "Insufficient stock (X available)".
Page 11 — Cash Ledger (/cash-ledger)
Top bar: "+ Cash In" | "+ Cash Out" | Date range filter
Ledger table:
| Date | Type | Category | Description | Amount |
|---|
- Type badge: "IN" (green) / "OUT" (red)
- Running balance column (optional — nice to have)
New Entry Modal (shared for In and Out, pre-fills type):
- Entry Type (pre-selected Cash In or Cash Out)
- Amount ₹ *
- Category * (dropdown: Utilities, Supplies, Advance, Refund, Other)
- Description * (mandatory free text)
- Date & Time (auto-filled, editable)
Page 12 — Accounts (/accounts)
Date Range filter at top: Today | This Week | This Month | Custom Range
Summary cards:
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Total Income │ │ Total Expense│ │ Net Balance │
│ ₹38,000 │ │ ₹15,800 │ │ ₹22,200 │
└──────────────┘ └──────────────┘ └──────────────┘
Breakdown table (Income): | Source | Amount | | Job Revenue | ₹28,500 | | Parts Sales | ₹7,500 | | Cash In (Other) | ₹2,000 |
Breakdown table (Expenses): | Source | Amount | | Staff Payments | ₹15,000 | | Cash Out (Other) | ₹800 |
All Transactions tab: Unified chronological list, clickable to see source.
Page 13 — Daily Report (/daily-report)
Date picker (default today).
On-screen preview sections:
- Header: Business name, date, summary totals
- Completed Jobs (table)
- Parts Sales (table)
- Cash In entries (table)
- Cash Out entries (table)
- Staff Payments (table)
- Day Totals (summary row)
"Download Excel" button → calls GET /api/reports/daily/download?date=YYYY-MM-DD → browser saves .xlsx file.
Page 14 — Excel Exports (/exports)
4 export cards, each with description and download button:
┌───────────────────────────────────────┐
│ 📥 Job Cards Database │
│ Export all job cards with date filter │
│ [From Date] [To Date] [Download xlsx] │
└───────────────────────────────────────┘
┌───────────────────────────────────────┐
│ 📥 Staff & Attendance │
│ Export staff list + monthly attendance│
│ [Month] [Year] [Download xlsx] │
└───────────────────────────────────────┘
┌───────────────────────────────────────┐
│ 📥 Parts Inventory │
│ Full parts catalog with stock levels │
│ [Download xlsx] │
└───────────────────────────────────────┘
┌───────────────────────────────────────┐
│ 📥 Accounts Ledger │
│ All financial transactions │
│ [From Date] [To Date] [Download xlsx] │
└───────────────────────────────────────┘
Shared Components
<Badge status="active|done|in|out|full|wash" />
Colored pill badge for statuses.
<Modal title onClose>children</Modal>
Overlay modal with close button.
<DataTable columns data loading onAction />
Reusable table with loading skeleton.
<ConfirmDialog message onConfirm onCancel />
Simple yes/no confirmation popup.
Toast Notifications
Use a simple toast system: success (green), error (red), info (blue). Auto-dismiss in 3s.
AuthContext & Protected Routes
// context/AuthContext.tsx
interface AuthContextType {
user: { id: number; username: string; full_name: string } | null;
token: string | null;
login: (token: string, user: object) => void;
logout: () => void;
isAuthenticated: boolean;
}
// Protected route wrapper
<ProtectedRoute> wraps all pages except /login
// Redirects to /login if !isAuthenticated
Axios Instance
// api/axios.ts
import axios from 'axios';
const api = axios.create({ baseURL: 'http://localhost:5000/api' });
// Request interceptor — attach JWT
api.interceptors.request.use(config => {
const token = localStorage.getItem('gms_token');
if (token) config.headers.Authorization = `Bearer ${token}`;
return config;
});
// Response interceptor — handle 401 (auto logout)
api.interceptors.response.use(
r => r,
err => {
if (err.response?.status === 401) {
localStorage.removeItem('gms_token');
window.location.href = '/login';
}
return Promise.reject(err);
}
);
export default api;