414 lines
15 KiB
Markdown
414 lines
15 KiB
Markdown
# 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):**
|
|
1. 🏠 Dashboard
|
|
2. 🔧 Job Cards
|
|
3. 📋 Status Board
|
|
4. 👤 Staff
|
|
5. 📅 Attendance
|
|
6. 💰 Salary & Payments
|
|
7. 🔩 Parts & Inventory
|
|
8. 🛒 Parts Sales
|
|
9. 💵 Cash Ledger
|
|
10. 📊 Accounts
|
|
11. 📈 Daily Report
|
|
12. 📤 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:**
|
|
1. Header: Business name, date, summary totals
|
|
2. Completed Jobs (table)
|
|
3. Parts Sales (table)
|
|
4. Cash In entries (table)
|
|
5. Cash Out entries (table)
|
|
6. Staff Payments (table)
|
|
7. 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
|
|
|
|
```tsx
|
|
// 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
|
|
|
|
```ts
|
|
// 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;
|
|
```
|