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
11 KiB
11 KiB
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:
- Login (
/login) - Email + password,JWT auth - Dashboard (
/) - Stats cards + recent activity + quick actions - Attendance List (
/attendance) - Table with all records - Attendance Detail (
/attendance/:id) - View/Edit single record - Accident List (
/accident) - Table with all records - Accident Detail (
/accident/:id) - View/Edit single record - 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:
id, email, password_hash, name, role, created_at, updated_at, is_active
Attendance:
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:
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:
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
# 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