feat: add district filter (Hiraa/AsSaffiyah) from static mapping

- ETL writes District column to NocoDB DailySales
- Museums mapped: Hiraa (Revelation, Holy Quraan, Trail, Makkah, VIP)
  AsSaffiyah (Creation Story, Best of Creation)
- District filter added to Dashboard and Comparison (cascades to museum)
- District Performance chart added (desktop + mobile)
- Locale keys added for both EN and AR

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
fahed
2026-03-31 14:08:16 +03:00
parent 4f4559023b
commit 219680fb5e
9 changed files with 120 additions and 9 deletions

View File

@@ -39,6 +39,21 @@ export function getMuseumsFromProduct(productDescription: string): MuseumMatch {
return { museums: matched, split: 1 / matched.length };
}
// Static museum → district mapping
const MUSEUM_DISTRICT: Record<string, string> = {
'Revelation Exhibition': 'Hiraa',
'Holy Quraan Museum': 'Hiraa',
'Trail To Hira Cave': 'Hiraa',
'Makkah Greets Us': 'Hiraa',
'VIP Experience': 'Hiraa',
'Creation Story Museum': 'AsSaffiyah',
'Best of Creation': 'AsSaffiyah',
};
export function getDistrict(museumName: string): string {
return MUSEUM_DISTRICT[museumName] || 'Other';
}
export const CHANNEL_LABELS: Record<string, string> = {
'B2C': 'HiHala Website/App',
'B2B': 'B2B',

View File

@@ -1,6 +1,6 @@
import { fetchSales } from './erpClient';
import { discoverTableIds, deleteRowsByMonth, deleteAllRows, insertRecords } from './nocodbClient';
import { getMuseumsFromProduct, getChannelLabel } from '../config/museumMapping';
import { getMuseumsFromProduct, getChannelLabel, getDistrict } from '../config/museumMapping';
import type { ERPSaleRecord, AggregatedRecord } from '../types';
function generateMonthBoundaries(startYear: number, startMonth: number): Array<[string, string]> {
@@ -51,12 +51,14 @@ export function aggregateTransactions(sales: ERPSaleRecord[]): AggregatedRecord[
? museums.filter(m => m !== museum).join(', ')
: '';
const ticketType = isCombo ? 'combo' : 'single';
const district = getDistrict(museum);
const key = `${date}|${museum}|${channel}|${ticketType}`;
let entry = map.get(key);
if (!entry) {
entry = {
Date: date,
District: district,
MuseumName: museum,
Channel: channel,
TicketType: ticketType,

View File

@@ -18,6 +18,7 @@ export interface ERPSaleRecord {
export interface AggregatedRecord {
Date: string;
District: string;
MuseumName: string;
Channel: string;
TicketType: 'single' | 'combo';