import { useEffect, useState } from "react"; import { useApiClient } from "@/shared/api/provider.js"; import { getAppSettings } from "@/shared/api/appSettings.js"; const DAYS_PATTERN = /^(\d+)d$/; const HOURS_PATTERN = /^(\d+)h$/; // Mirrors Angular's `AppSettings` default payload // (ClientApp/src/app/shared/models-legacy/app-settings.model.ts): // boardSearchFrom: 1, boardSearchTo: 7, // scheduleSearchFrom: 1, scheduleSearchTo: 330, // flightStatusAvailableFrom: 2 (hours), buyPeriod.min: 2h, buyPeriod.max: 330d // `buyTicketMaxHours` mirrors Angular's `330d` → 330 * 24 = 7920 hours; // without this the Buy button hides for any flight more than ~3 days // out, which made the schedule details page look like 'no Купить // button' for the typical search-2-weeks-ahead flow. const DEFAULTS = { onlineboardSearchFrom: 1, onlineboardSearchTo: 7, scheduleSearchFrom: 1, scheduleSearchTo: 330, flightStatusAvailableFromHours: 2, buyTicketMinHours: 2, buyTicketMaxHours: 330 * 24, } as const; function parsePattern( value: string | undefined, pattern: RegExp, fallback: number, ): number { if (!value) return fallback; const match = pattern.exec(value); if (!match?.[1]) return fallback; return parseInt(match[1], 10); } function parseDays(value: string | undefined, fallback: number): number { return parsePattern(value, DAYS_PATTERN, fallback); } function parseHours(value: string | undefined, fallback: number): number { return parsePattern(value, HOURS_PATTERN, fallback); } /** Parse an `h` or `d` duration into hours. Days expand to 24×n. * Used for `buyPeriod.max` which Angular's settings API returns as * `"330d"` (any other consumer in the React codebase that needs to * accept either unit should use this rather than parseHours). */ function parseHoursOrDays(value: string | undefined, fallback: number): number { if (!value) return fallback; const hMatch = HOURS_PATTERN.exec(value); if (hMatch?.[1]) return parseInt(hMatch[1], 10); const dMatch = DAYS_PATTERN.exec(value); if (dMatch?.[1]) return parseInt(dMatch[1], 10) * 24; return fallback; } export interface UseAppSettingsResult { onlineboardSearchFrom: number; onlineboardSearchTo: number; scheduleSearchFrom: number; scheduleSearchTo: number; flightStatusAvailableFromHours: number; buyTicketMinHours: number; buyTicketMaxHours: number; loading: boolean; error: Error | null; } /** * Fetches the global app settings and exposes day-range and button-config numbers. * On error or parse failure, returns defaults. */ export function useAppSettings(): UseAppSettingsResult { const client = useApiClient(); const [state, setState] = useState>(DEFAULTS); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { let cancelled = false; getAppSettings(client) .then((response) => { if (cancelled) return; const ob = response.uiOptions?.filter?.onlineboard; const sc = response.uiOptions?.filter?.schedule; const fs = response.uiOptions?.buttons?.flightStatus; const bt = response.uiOptions?.buttons?.buyTicket; setState({ onlineboardSearchFrom: parseDays(ob?.searchFrom, DEFAULTS.onlineboardSearchFrom), onlineboardSearchTo: parseDays(ob?.searchTo, DEFAULTS.onlineboardSearchTo), scheduleSearchFrom: parseDays(sc?.searchFrom, DEFAULTS.scheduleSearchFrom), scheduleSearchTo: parseDays(sc?.searchTo, DEFAULTS.scheduleSearchTo), flightStatusAvailableFromHours: parseHours( fs?.availableFrom, DEFAULTS.flightStatusAvailableFromHours, ), buyTicketMinHours: parseHoursOrDays(bt?.period?.min, DEFAULTS.buyTicketMinHours), buyTicketMaxHours: parseHoursOrDays(bt?.period?.max, DEFAULTS.buyTicketMaxHours), }); setLoading(false); }) .catch((err: Error) => { if (cancelled) return; setError(err); setLoading(false); }); return () => { cancelled = true; }; }, [client]); return { ...state, loading, error }; }