diff --git a/src/shared/api/appSettings.ts b/src/shared/api/appSettings.ts index e0c6d1ca..618b04fe 100644 --- a/src/shared/api/appSettings.ts +++ b/src/shared/api/appSettings.ts @@ -6,6 +6,25 @@ export interface AppSettingsFilterOptions { timeStep?: string; } +export interface AppSettingsButtonsBuyTicketPeriod { + min?: string; + max?: string; +} + +export interface AppSettingsButtonsBuyTicket { + period?: AppSettingsButtonsBuyTicketPeriod; +} + +export interface AppSettingsButtonsFlightStatus { + availableFrom?: string; + visible?: string; +} + +export interface AppSettingsButtons { + flightStatus?: AppSettingsButtonsFlightStatus; + buyTicket?: AppSettingsButtonsBuyTicket; +} + export interface AppSettingsResponse { showDebugVersion?: string; uiOptions?: { @@ -14,7 +33,7 @@ export interface AppSettingsResponse { onlineboard?: AppSettingsFilterOptions; schedule?: AppSettingsFilterOptions; }; - buttons?: Record; + buttons?: AppSettingsButtons; }; } diff --git a/src/shared/hooks/useAppSettings.test.ts b/src/shared/hooks/useAppSettings.test.ts index 96cef73a..db8024f0 100644 --- a/src/shared/hooks/useAppSettings.test.ts +++ b/src/shared/hooks/useAppSettings.test.ts @@ -82,4 +82,34 @@ describe("useAppSettings", () => { expect(result.current.error).toBe(err); expect(result.current.onlineboardSearchFrom).toBe(2); }); + + it("parses buttons config into hour numbers", async () => { + const response: AppSettingsResponse = { + uiOptions: { + buttons: { + flightStatus: { availableFrom: "24h" }, + buyTicket: { period: { min: "2h", max: "72h" } }, + }, + }, + }; + mockGetAppSettings.mockResolvedValue(response); + + const { result } = renderHook(() => useAppSettings()); + + await waitFor(() => expect(result.current.loading).toBe(false)); + expect(result.current.flightStatusAvailableFromHours).toBe(24); + expect(result.current.buyTicketMinHours).toBe(2); + expect(result.current.buyTicketMaxHours).toBe(72); + }); + + it("returns button-config defaults when fields are missing", async () => { + mockGetAppSettings.mockResolvedValue({}); + + const { result } = renderHook(() => useAppSettings()); + + await waitFor(() => expect(result.current.loading).toBe(false)); + expect(result.current.flightStatusAvailableFromHours).toBe(24); + expect(result.current.buyTicketMinHours).toBe(2); + expect(result.current.buyTicketMaxHours).toBe(72); + }); }); diff --git a/src/shared/hooks/useAppSettings.ts b/src/shared/hooks/useAppSettings.ts index b096004f..de351945 100644 --- a/src/shared/hooks/useAppSettings.ts +++ b/src/shared/hooks/useAppSettings.ts @@ -3,33 +3,52 @@ import { useApiClient } from "@/shared/api/provider.js"; import { getAppSettings } from "@/shared/api/appSettings.js"; const DAYS_PATTERN = /^(\d+)d$/; +const HOURS_PATTERN = /^(\d+)h$/; const DEFAULTS = { onlineboardSearchFrom: 2, onlineboardSearchTo: 14, scheduleSearchFrom: 30, scheduleSearchTo: 30, + flightStatusAvailableFromHours: 24, + buyTicketMinHours: 2, + buyTicketMaxHours: 72, } as const; -function parseDays(value: string | undefined, fallback: number): number { +function parsePattern( + value: string | undefined, + pattern: RegExp, + fallback: number, +): number { if (!value) return fallback; - const match = DAYS_PATTERN.exec(value); + const match = pattern.exec(value); if (!match) 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); +} + 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 numbers. - * On error or parse failure, returns default values (2/14/30/30). + * 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(); @@ -45,11 +64,20 @@ export function useAppSettings(): UseAppSettingsResult { 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: parseHours(bt?.period?.min, DEFAULTS.buyTicketMinHours), + buyTicketMaxHours: parseHours(bt?.period?.max, DEFAULTS.buyTicketMaxHours), }); setLoading(false); })