docs: add hijri seasons feature design spec
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
118
docs/superpowers/specs/2026-03-31-hijri-seasons-design.md
Normal file
118
docs/superpowers/specs/2026-03-31-hijri-seasons-design.md
Normal file
@@ -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 `<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
|
||||
Reference in New Issue
Block a user