From 1dd216f93386c48c0505d5d1b9f2260f044d458c Mon Sep 17 00:00:00 2001 From: fahed Date: Tue, 31 Mar 2026 15:54:52 +0300 Subject: [PATCH] docs: add hijri seasons feature design spec Co-Authored-By: Claude Opus 4.6 (1M context) --- .../specs/2026-03-31-hijri-seasons-design.md | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 docs/superpowers/specs/2026-03-31-hijri-seasons-design.md diff --git a/docs/superpowers/specs/2026-03-31-hijri-seasons-design.md b/docs/superpowers/specs/2026-03-31-hijri-seasons-design.md new file mode 100644 index 0000000..e01bde8 --- /dev/null +++ b/docs/superpowers/specs/2026-03-31-hijri-seasons-design.md @@ -0,0 +1,118 @@ +# 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 `>` 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