119 lines
5.0 KiB
Markdown
119 lines
5.0 KiB
Markdown
# Hijri Seasons Feature
|
||
|
||
## Goal
|
||
|
||
Add configurable hijri seasons (Ramadan, Hajj, etc.) to the dashboard as a presentation overlay. Seasons are user-defined with Gregorian date ranges (since hijri months shift ~11 days each year). They appear as filter presets, chart bands, and are managed through a settings page.
|
||
|
||
## Data Storage
|
||
|
||
New NocoDB `Seasons` table:
|
||
|
||
| Column | Type | Example |
|
||
|--------|------|---------|
|
||
| Name | string | `Ramadan` |
|
||
| HijriYear | number | `1446` |
|
||
| StartDate | string | `2025-02-28` |
|
||
| EndDate | string | `2025-03-30` |
|
||
| Color | string | `#10b981` |
|
||
|
||
Read on dashboard load alongside PilgrimStats. Written via server proxy to keep NocoDB credentials server-side.
|
||
|
||
**Loading lifecycle:** Seasons load independently of the main data fetch. A failure to load seasons degrades gracefully — seasons state defaults to `[]`, the dashboard renders normally without bands or season presets. Seasons are non-blocking and non-critical.
|
||
|
||
## Server Changes
|
||
|
||
### New files
|
||
|
||
| File | Responsibility |
|
||
|------|----------------|
|
||
| `server/src/routes/seasons.ts` | `GET /api/seasons` (read all), `POST /api/seasons` (create), `PUT /api/seasons/:id` (update), `DELETE /api/seasons/:id` (delete) |
|
||
|
||
### Modified files
|
||
|
||
| File | Change |
|
||
|------|--------|
|
||
| `server/src/index.ts` | Mount seasons routes at `/api/seasons` |
|
||
| `server/src/services/nocodbClient.ts` | Add generic CRUD helpers typed as `<T extends Record<string, unknown>>` so both ETL and seasons routes can share them without coupling |
|
||
| `vite.config.ts` | Add `/api/seasons` proxy rule **before** the catch-all `/api` rule (same pattern as `/api/erp`). Order: `/api/erp` → `/api/etl` → `/api/seasons` → `/api` |
|
||
|
||
## Client Changes
|
||
|
||
### New files
|
||
|
||
| File | Responsibility |
|
||
|------|----------------|
|
||
| `src/components/Settings.tsx` | Settings page with seasons CRUD table |
|
||
| `src/services/seasonsService.ts` | Fetch/create/update/delete seasons via server proxy |
|
||
|
||
### Modified files
|
||
|
||
| File | Change |
|
||
|------|--------|
|
||
| `src/types/index.ts` | Add `Season` interface |
|
||
| `src/App.tsx` | Add `/settings` route, nav link (both desktop and mobile bottom nav), load seasons on mount (non-blocking) |
|
||
| `src/components/Dashboard.tsx` | Add season filter dropdown, chart annotation bands |
|
||
| `src/components/Comparison.tsx` | Add season filter as period preset |
|
||
| `src/config/chartConfig.ts` | Import and register `chartjs-plugin-annotation` in the central `ChartJS.register()` call |
|
||
| `src/locales/en.json` | Settings page labels, season filter labels |
|
||
| `src/locales/ar.json` | Arabic translations |
|
||
| `package.json` | Add `chartjs-plugin-annotation` dependency |
|
||
|
||
## Season Interface
|
||
|
||
```typescript
|
||
export interface Season {
|
||
Id?: number;
|
||
Name: string;
|
||
HijriYear: number;
|
||
StartDate: string;
|
||
EndDate: string;
|
||
Color: string;
|
||
}
|
||
```
|
||
|
||
## Settings Page (`/settings`)
|
||
|
||
New route accessible from the nav bar (gear icon on desktop, gear in mobile bottom nav). Contains:
|
||
|
||
- **Seasons table**: lists all defined seasons with columns: Name, Hijri Year, Start Date, End Date, Color, Actions (edit/delete)
|
||
- **Add season form**: inline row at the bottom of the table with inputs for each field + color picker + save button
|
||
- **Edit**: click a row to edit inline
|
||
- **Delete**: delete button per row with confirmation
|
||
- **No empty state needed**: just show the empty table with the add form
|
||
|
||
## Period Filter Integration
|
||
|
||
### Dashboard
|
||
|
||
Add a "Season" select in the filters section (after Quarter). Options populated from the loaded seasons list:
|
||
- `All Seasons` (default — no date filtering from season)
|
||
- `Ramadan 1446 (Feb 28 – Mar 30, 2025)`
|
||
- `Hajj 1446 (Jun 4 – Jun 9, 2025)`
|
||
- etc.
|
||
|
||
Selecting a season sets a date range filter on the data — equivalent to filtering by start/end date. This works alongside existing year/district/channel/event filters.
|
||
|
||
Implementation: when a season is selected, filter data to `row.date >= season.StartDate && row.date <= season.EndDate`. Store the selected season ID in state (not URL params — seasons are dynamic).
|
||
|
||
### Comparison
|
||
|
||
Seasons appear as preset period options alongside months/quarters. Selecting "Ramadan 1446" sets the period dates and auto-compares with the same season name in the previous hijri year if defined (e.g. "Ramadan 1445").
|
||
|
||
## Chart Bands (Revenue Trend)
|
||
|
||
Uses `chartjs-plugin-annotation` to draw semi-transparent vertical bands on the revenue trend chart. Must be registered in `chartConfig.ts` via `ChartJS.register(Annotation)`.
|
||
|
||
For each season whose date range overlaps the chart's visible range:
|
||
- Draw a vertical box from `season.StartDate` to `season.EndDate`
|
||
- Fill with `season.Color` at 15% opacity
|
||
- Label at the top with season name + hijri year
|
||
|
||
Only the revenue trend chart gets bands (it's the only time-series chart where seasons make visual sense).
|
||
|
||
## What's NOT Changing
|
||
|
||
- ETL pipeline unchanged — seasons are a UI/presentation concern
|
||
- NocoDB DailySales schema unchanged
|
||
- All existing filters (year, district, channel, event, quarter) unchanged
|
||
- Seasons don't affect data aggregation or storage
|