From 97a208734e75ce2bf6e032bc220ebc5140217c34 Mon Sep 17 00:00:00 2001 From: fahed Date: Wed, 18 Feb 2026 14:13:45 +0300 Subject: [PATCH] updates --- src/components/Comparison.tsx | 51 +++++++++++++++++++++++++++-------- src/components/Dashboard.tsx | 9 ++++++- src/services/dataService.ts | 41 +++++++++++++++++++++++++--- 3 files changed, 85 insertions(+), 16 deletions(-) diff --git a/src/components/Comparison.tsx b/src/components/Comparison.tsx index 622fe65..90b602b 100644 --- a/src/components/Comparison.tsx +++ b/src/components/Comparison.tsx @@ -242,17 +242,46 @@ function Comparison({ data, showDataLabels, setShowDataLabels, includeVAT, setIn return null; }; - // Calculate capture rate and pilgrim data for quarters + // Estimate pilgrims for any date range by prorating quarterly data + const estimatePilgrims = useCallback((start: string, end: string): number | null => { + const startDate = new Date(start); + const endDate = new Date(end); + let total = 0; + let hasData = false; + + // Iterate through each quarter that overlaps with the range + const startYear = startDate.getFullYear(); + const endYear = endDate.getFullYear(); + + for (let year = startYear; year <= endYear; year++) { + for (let q = 1; q <= 4; q++) { + const qStart = new Date(year, (q - 1) * 3, 1); + const qEnd = new Date(year, q * 3, 0); // last day of quarter + + // Check overlap + if (qEnd < startDate || qStart > endDate) continue; + + const pilgrims = umrahData[year]?.[q]; + if (!pilgrims) continue; + + // Calculate overlap fraction + const overlapStart = new Date(Math.max(qStart.getTime(), startDate.getTime())); + const overlapEnd = new Date(Math.min(qEnd.getTime(), endDate.getTime())); + const overlapDays = (overlapEnd.getTime() - overlapStart.getTime()) / (1000 * 60 * 60 * 24) + 1; + const quarterDays = (qEnd.getTime() - qStart.getTime()) / (1000 * 60 * 60 * 24) + 1; + + total += pilgrims * (overlapDays / quarterDays); + hasData = true; + } + } + + return hasData ? Math.round(total) : null; + }, []); + + // Calculate capture rate and pilgrim data for any date range const quarterData = useMemo(() => { - const prevYear = parseInt(ranges.prev.start.substring(0, 4)); - const currYear = parseInt(ranges.curr.start.substring(0, 4)); - const prevQ = getQuarterFromRange(ranges.prev.start, ranges.prev.end); - const currQ = getQuarterFromRange(ranges.curr.start, ranges.curr.end); - - if (!prevQ || !currQ) return null; // Only show for quarter comparisons - - const prevPilgrims = umrahData[prevYear]?.[prevQ]; - const currPilgrims = umrahData[currYear]?.[currQ]; + const prevPilgrims = estimatePilgrims(ranges.prev.start, ranges.prev.end); + const currPilgrims = estimatePilgrims(ranges.curr.start, ranges.curr.end); if (!prevPilgrims && !currPilgrims) return null; @@ -263,7 +292,7 @@ function Comparison({ data, showDataLabels, setShowDataLabels, includeVAT, setIn pilgrims: { prev: prevPilgrims, curr: currPilgrims }, captureRate: { prev: prevRate, curr: currRate } }; - }, [ranges, prevMetrics.visitors, currMetrics.visitors]); + }, [ranges, prevMetrics.visitors, currMetrics.visitors, estimatePilgrims]); const captureRates = quarterData?.captureRate || null; const pilgrimCounts = quarterData?.pilgrims || null; diff --git a/src/components/Dashboard.tsx b/src/components/Dashboard.tsx index 4f8ca76..ce6f937 100644 --- a/src/components/Dashboard.tsx +++ b/src/components/Dashboard.tsx @@ -1,4 +1,4 @@ -import React, { useState, useMemo } from 'react'; +import React, { useState, useMemo, useEffect } from 'react'; import { useSearchParams } from 'react-router-dom'; import { Line, Doughnut, Bar } from 'react-chartjs-2'; import { Carousel, EmptyState, FilterControls, StatCard } from './shared'; @@ -14,6 +14,7 @@ import { groupByMuseum, groupByDistrict, umrahData, + fetchPilgrimStats, getUniqueYears, getUniqueDistricts, getDistrictMuseumMap, @@ -32,6 +33,12 @@ const filterKeys = ['year', 'district', 'museum', 'quarter']; function Dashboard({ data, showDataLabels, setShowDataLabels, includeVAT, setIncludeVAT }) { const { t } = useLanguage(); const [searchParams, setSearchParams] = useSearchParams(); + const [pilgrimLoaded, setPilgrimLoaded] = useState(false); + + // Fetch pilgrim stats from NocoDB on mount + useEffect(() => { + fetchPilgrimStats().then(() => setPilgrimLoaded(true)); + }, []); // Initialize filters from URL or defaults const [filters, setFiltersState] = useState(() => { diff --git a/src/services/dataService.ts b/src/services/dataService.ts index 55b55f8..89408ca 100644 --- a/src/services/dataService.ts +++ b/src/services/dataService.ts @@ -24,7 +24,8 @@ const NOCODB_TOKEN = process.env.REACT_APP_NOCODB_TOKEN || ''; const NOCODB_TABLES = { districts: 'm8cup7lesbet0sa', museums: 'm1c7od7mdirffvu', - dailyStats: 'mc7qhbdh3mjjwl8' + dailyStats: 'mc7qhbdh3mjjwl8', + pilgrimStats: 'mmqgj0a0l5qxeqf' }; // Cache keys @@ -32,11 +33,43 @@ const CACHE_KEY = 'hihala_data_cache'; const CACHE_TIMESTAMP_KEY = 'hihala_data_cache_timestamp'; const CACHE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000; // 7 days -export const umrahData: UmrahData = { - 2024: { 1: 11574494, 2: 10521465, 3: 3364627, 4: 7435625 }, - 2025: { 1: 15222497, 2: 5443393, 3: null, 4: null } +// Default umrah data (overridden by NocoDB PilgrimStats when available) +export let umrahData: UmrahData = { + 2024: { 1: 15222497, 2: 10521465, 3: 6270868, 4: 7435625 }, + 2025: { 1: 15222497, 2: 5443393, 3: 26643148, 4: 31591871 } }; +// Fetch pilgrim stats from NocoDB and update umrahData +export async function fetchPilgrimStats(): Promise { + try { + const url = `${NOCODB_URL}/api/v2/tables/${NOCODB_TABLES.pilgrimStats}/records?limit=50`; + const res = await fetch(url, { headers: { 'xc-token': NOCODB_TOKEN } }); + if (!res.ok) throw new Error(`HTTP ${res.status}`); + const json = await res.json(); + const records = json.list || []; + + const data: UmrahData = { 2024: {}, 2025: {} }; + for (const r of records) { + const year = r.Year as number; + const qStr = r.Quarter as string; // "Q1", "Q2", etc. + const qNum = parseInt(qStr.replace('Q', '')); + const total = r.TotalPilgrims as number; + if (year && qNum && total) { + if (!data[year]) data[year] = {}; + data[year][qNum] = total; + } + } + + // Update the global umrahData + umrahData = data; + console.log('PilgrimStats loaded from NocoDB:', data); + return data; + } catch (err) { + console.warn('Failed to fetch PilgrimStats, using defaults:', (err as Error).message); + return umrahData; + } +} + // ============================================ // Offline Cache Functions // ============================================