5.0 KiB
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
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.StartDatetoseason.EndDate - Fill with
season.Colorat 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