eafc1c33fc
- FastAPI backend with SQLite database - React + TailwindCSS frontend - JWT authentication - Excel upload/download for attendance and accident records - Shift roster management with S7a/S7b split - Lateness/OT calculation based on shift times - Roster info API endpoints - Export API with token query parameter support - Docker compose deployment
378 lines
11 KiB
Markdown
378 lines
11 KiB
Markdown
# AARS - Attendance & Accident Record System
|
||
|
||
## 1. Concept & Vision
|
||
|
||
一個為中小企設計嘅出勤同意外記錄系統。介面乾淨、專業、易用,帶有香港本地特色(繁體中文、HKT時區)。兩大一頁式 dashboard,展示即時數據,所有記錄都可以 filter、sort、export。
|
||
|
||
目標係取代 Excel 試算表,做一個可以多人協作、數據唔會重複、report 一click 生成嘅系統。
|
||
|
||
---
|
||
|
||
## 2. Design Language
|
||
|
||
**Aesthetic:** 淺色專業商務風,白底配淺灰,帶有 System Blue 強調色。似 classic Notion / Linear 嗰種乾淨感覺,但再 warm 少少。
|
||
|
||
**Colors (TailwindCSS custom):**
|
||
- Primary: `#2563EB` (blue-600)
|
||
- Primary Dark: `#1D4ED8` (blue-700)
|
||
- Accent: `#06B6D4` (cyan-500)
|
||
- Background: `#F8FAFC` (slate-50)
|
||
- Card: `#FFFFFF`
|
||
- Border: `#E2E8F0` (slate-200)
|
||
- Text Primary: `#0F172A` (slate-900)
|
||
- Text Secondary: `#64748B` (slate-500)
|
||
- Success: `#10B981` (emerald-500)
|
||
- Warning: `#F59E0B` (amber-500)
|
||
- Danger: `#EF4444` (red-500)
|
||
|
||
**Typography:**
|
||
- Font: Inter (Google Fonts) + Noto Sans TC (繁體中文)
|
||
- Headings: 600-700 weight
|
||
- Body: 400-500 weight
|
||
|
||
**Spatial System:**
|
||
- Base unit: 4px
|
||
- Card padding: 24px
|
||
- Section gap: 32px
|
||
- Border radius: 8px (cards), 6px (buttons), 4px (inputs)
|
||
|
||
**Motion:**
|
||
- Page transitions: fade 150ms
|
||
- Hover states: 200ms ease
|
||
- Loading: pulse animation
|
||
- No bouncy/playful animations - keep it professional
|
||
|
||
---
|
||
|
||
## 3. Layout & Structure
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────┐
|
||
│ Header: Logo + Nav (Attendance | Accident) + User │
|
||
├─────────────────────────────────────────────────────┤
|
||
│ Page Content: │
|
||
│ ┌─ Dashboard ─┐ ┌─ List View ─┐ ┌─ Detail ─┐ │
|
||
│ │ Stats cards │ │ Table + Fn │ │ Edit form│ │
|
||
│ │ Charts │ │ Filter/Sort │ │ History │ │
|
||
│ └────────────┘ └─────────────┘ └─────────┘ │
|
||
└─────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
**Pages:**
|
||
1. **Login** (`/login`) - Email + password,JWT auth
|
||
2. **Dashboard** (`/`) - Stats cards + recent activity + quick actions
|
||
3. **Attendance List** (`/attendance`) - Table with all records
|
||
4. **Attendance Detail** (`/attendance/:id`) - View/Edit single record
|
||
5. **Accident List** (`/accident`) - Table with all records
|
||
6. **Accident Detail** (`/accident/:id`) - View/Edit single record
|
||
7. **Import** (`/import`) - Excel upload + mapping + preview
|
||
|
||
**Responsive:** Desktop-first,但 table 可以 horizontal scroll 喺 mobile
|
||
|
||
---
|
||
|
||
## 4. Features & Interactions
|
||
|
||
### 4.1 Authentication
|
||
- Login with email + password
|
||
- JWT token stored in localStorage
|
||
- Token expiry: 24 hours
|
||
- Auto-redirect to login if expired
|
||
|
||
### 4.2 Dashboard
|
||
**Attendance Section:**
|
||
- Total records count
|
||
- Today attendance count
|
||
- This month attendance count
|
||
- Records trend chart (last 7 days / 30 days)
|
||
|
||
**Accident Section:**
|
||
- Total records count
|
||
- This month accidents count
|
||
- Unresolved accidents count
|
||
- Recent accidents list (last 5)
|
||
|
||
### 4.3 List View
|
||
- Paginated table (20 per page)
|
||
- Columns: ID, Date, Employee Name, Department, Status, Actions
|
||
- **Filter:** By date range, department, status, search text
|
||
- **Sort:** Click column header to sort ASC/DESC
|
||
- **Actions:** View, Edit, Delete (soft delete)
|
||
- **Bulk Actions:** Select multiple → Delete / Export selected
|
||
- **Export:** PDF (single/all selected) / Excel (all filtered results)
|
||
|
||
### 4.4 Detail / Edit View
|
||
- Form with all fields
|
||
- Read-only view mode vs Edit mode
|
||
- **History tab:** Show who created/modified when
|
||
- **Save:** Validates + saves to SQLite
|
||
- **Cancel:** Returns to list without saving
|
||
|
||
### 4.5 Excel Import
|
||
- Upload Excel file (.xlsx, .xls)
|
||
- Auto-detect columns from first row
|
||
- **Field mapping:** Map Excel columns to system fields
|
||
- **Duplicate detection:** Check by unique key (Employee ID + Date for attendance, Date + Location for accident)
|
||
- **Preview:** Show first 10 rows with validation status
|
||
- **Options:**
|
||
- "Update existing" → skip duplicates or update
|
||
- "Create new only" → skip duplicates
|
||
- **Import button:** Bulk insert/update
|
||
- **Result:** Show success count, skipped (duplicates), errors
|
||
|
||
### 4.6 Field Management
|
||
- Default fields per section (pre-defined)
|
||
- **Add custom field:** Name, type (text/number/date/select), required
|
||
- Custom fields stored in JSON column
|
||
- Can be used in list view, detail view, export
|
||
|
||
### 4.7 Export
|
||
- **Excel:** All filtered results, includes all fields
|
||
- **PDF:** Formatted report with header, table, pagination
|
||
- Export respects current filter/sort state
|
||
|
||
---
|
||
|
||
## 5. Component Inventory
|
||
|
||
### 5.1 Header
|
||
- Logo (left): "AARS" text logo
|
||
- Nav tabs (center): "出勤 Attendance" | "意外 Accident"
|
||
- Active tab: blue underline
|
||
- User menu (right): Username + dropdown (Profile, Logout)
|
||
|
||
### 5.2 Stats Card
|
||
- Icon (colored circle bg) + metric value (large) + label (small)
|
||
- Hover: subtle shadow lift
|
||
|
||
### 5.3 Data Table
|
||
- Header row: grey bg, bold text
|
||
- Sortable columns: sort icon, click to toggle
|
||
- Row hover: light blue bg
|
||
- Checkbox column for bulk select
|
||
- Action buttons: View (blue), Edit (gray), Delete (red)
|
||
- Empty state: "No records found" + illustration
|
||
|
||
### 5.4 Filter Bar
|
||
- Date range picker (two inputs)
|
||
- Dropdown for department/status
|
||
- Text search input
|
||
- "Clear filters" button
|
||
- "Export" button
|
||
|
||
### 5.5 Form
|
||
- Label + input pairs, stacked
|
||
- Required indicator: red asterisk
|
||
- Input states: default, focus (blue ring), error (red border + message)
|
||
- Select dropdowns, date pickers
|
||
- Submit button (blue), Cancel button (gray outline)
|
||
|
||
### 5.6 Modal
|
||
- Centered overlay, white card
|
||
- Header + body + footer
|
||
- Close X button top right
|
||
- Backdrop click to close
|
||
|
||
### 5.7 Toast Notifications
|
||
- Success (green), Error (red), Info (blue)
|
||
- Auto-dismiss after 3s
|
||
- Bottom right corner
|
||
|
||
### 5.8 Import Wizard
|
||
- Step 1: Upload Excel
|
||
- Step 2: Map columns
|
||
- Step 3: Preview + validate
|
||
- Step 4: Confirm + import
|
||
- Progress indicator at top
|
||
|
||
---
|
||
|
||
## 6. Technical Approach
|
||
|
||
### 6.1 Backend (FastAPI)
|
||
```
|
||
backend/
|
||
├── main.py # FastAPI app entry
|
||
├── database.py # SQLite + SQLAlchemy
|
||
├── models.py # SQLAlchemy models
|
||
├── schemas.py # Pydantic schemas
|
||
├── auth.py # JWT auth
|
||
├── routers/
|
||
│ ├── attendance.py
|
||
│ ├── accident.py
|
||
│ ├── import.py
|
||
│ └── export.py
|
||
└── utils/
|
||
├── excel.py # openpyxl helpers
|
||
└── pdf.py # reportlab helpers
|
||
```
|
||
|
||
**API Endpoints:**
|
||
```
|
||
POST /api/auth/login
|
||
GET /api/auth/me
|
||
|
||
GET /api/attendance # List with filter/sort/paginate
|
||
POST /api/attendance # Create
|
||
GET /api/attendance/:id # Get one
|
||
PUT /api/attendance/:id # Update
|
||
DELETE /api/attendance/:id # Soft delete
|
||
|
||
GET /api/accident # List with filter/sort/paginate
|
||
POST /api/accident # Create
|
||
GET /api/accident/:id # Get one
|
||
PUT /api/accident/:id # Update
|
||
DELETE /api/accident/:id # Soft delete
|
||
|
||
POST /api/import/attendance # Excel import
|
||
POST /api/import/accident # Excel import
|
||
|
||
GET /api/export/attendance/excel
|
||
GET /api/export/attendance/pdf
|
||
GET /api/export/accident/excel
|
||
GET //api/export/accident/pdf
|
||
|
||
GET /api/dashboard/attendance # Stats
|
||
GET /api/dashboard/accident # Stats
|
||
|
||
GET /api/fields/attendance # Custom fields
|
||
POST /api/fields/attendance
|
||
GET /api/fields/accident
|
||
POST /api/fields/accident
|
||
```
|
||
|
||
### 6.2 Database Schema
|
||
|
||
**Users:**
|
||
```sql
|
||
id, email, password_hash, name, role, created_at, updated_at, is_active
|
||
```
|
||
|
||
**Attendance:**
|
||
```sql
|
||
id, employee_id, employee_name, department, date, check_in, check_out,
|
||
overtime_hours, leave_type, leave_hours, status, remarks,
|
||
custom_fields (JSON), created_by, created_at, updated_at, deleted_at
|
||
```
|
||
|
||
**Accident:**
|
||
```sql
|
||
id, date, time, location, employee_name, employee_id, department,
|
||
description, severity, medical_report, action_taken, responsible_person,
|
||
custom_fields (JSON), created_by, created_at, updated_at, deleted_at
|
||
```
|
||
|
||
**AttendanceCustomFields / AccidentCustomFields:**
|
||
```sql
|
||
id, field_name, field_type, required, options, order, created_at
|
||
```
|
||
|
||
### 6.3 Frontend (React + Vite)
|
||
```
|
||
frontend/
|
||
├── src/
|
||
│ ├── App.jsx
|
||
│ ├── main.jsx
|
||
│ ├── api.js # Axios API calls
|
||
│ ├── context/
|
||
│ │ └── AuthContext.jsx
|
||
│ ├── pages/
|
||
│ │ ├── Login.jsx
|
||
│ │ ├── Dashboard.jsx
|
||
│ │ ├── attendance/
|
||
│ │ │ ├── List.jsx
|
||
│ │ │ └── Detail.jsx
|
||
│ │ ├── accident/
|
||
│ │ │ ├── List.jsx
|
||
│ │ │ └── Detail.jsx
|
||
│ │ └── import/
|
||
│ │ └── Import.jsx
|
||
│ └── components/
|
||
│ ├── Layout.jsx
|
||
│ ├── DataTable.jsx
|
||
│ ├── FilterBar.jsx
|
||
│ ├── StatsCard.jsx
|
||
│ ├── Modal.jsx
|
||
│ └── Toast.jsx
|
||
├── index.html
|
||
├── tailwind.config.js
|
||
└── vite.config.js
|
||
```
|
||
|
||
### 6.4 Docker Setup
|
||
```yaml
|
||
# docker-compose.yml
|
||
services:
|
||
aars:
|
||
build: .
|
||
ports:
|
||
- "18775:8000"
|
||
volumes:
|
||
- ./data:/app/data
|
||
environment:
|
||
- TIMEZONE=Asia/Hong_Kong
|
||
- SECRET_KEY=<generated>
|
||
restart: unless-stopped
|
||
```
|
||
|
||
### 6.5 Key Libraries
|
||
- **Backend:** fastapi, uvicorn, sqlalchemy, pydantic, python-jose, passlib, openpyxl, reportlab
|
||
- **Frontend:** react, react-router-dom, axios, tailwindcss, recharts (charts), react-hot-toast
|
||
|
||
---
|
||
|
||
## 7. Default Fields
|
||
|
||
### Attendance Default Fields
|
||
| Field | Type | Required |
|
||
|-------|------|----------|
|
||
| employee_id | text | Yes |
|
||
| employee_name | text | Yes |
|
||
| department | select | Yes |
|
||
| date | date | Yes |
|
||
| check_in | time | No |
|
||
| check_out | time | No |
|
||
| overtime_hours | number | No |
|
||
| leave_type | select | No |
|
||
| leave_hours | number | No |
|
||
| status | select | Yes |
|
||
| remarks | text | No |
|
||
|
||
### Accident Default Fields
|
||
| Field | Type | Required |
|
||
|-------|------|----------|
|
||
| date | date | Yes |
|
||
| time | time | Yes |
|
||
| location | text | Yes |
|
||
| employee_name | text | Yes |
|
||
| employee_id | text | Yes |
|
||
| department | select | Yes |
|
||
| description | text | Yes |
|
||
| severity | select | Yes |
|
||
| medical_report | text | No |
|
||
| action_taken | text | No |
|
||
| responsible_person | text | No |
|
||
|
||
---
|
||
|
||
## 8. Security
|
||
|
||
- Passwords hashed with bcrypt
|
||
- JWT with 24h expiry
|
||
- CORS configured for known origins only
|
||
- Input sanitization on all endpoints
|
||
- Soft delete (never hard delete user data)
|
||
- Rate limiting on login endpoint
|
||
|
||
---
|
||
|
||
## 9. Out of Scope (v1)
|
||
|
||
- User management (create/edit users) - only admin can do this via direct DB
|
||
- Multi-company support
|
||
- Recurring attendance (scheduled check-in reminders)
|
||
- Document/photo upload for accidents
|
||
- Email notifications
|
||
- Mobile responsive redesign
|
||
- API for third-party integrations
|