// Shared types for the TREK travel planner. // // Domain entity/response types are now sourced from @trek/shared — the single // source of truth shared with the server. The Zod schemas there are built to // match the REAL server response shapes (see shared/src//*.schema.ts, // each documented against the producing service). Re-exported here so the rest // of the client keeps importing from '../types' unchanged. import type { Trip, TripMember, Day, DayNote, Place, AssignmentPlace, PlaceCategory, Assignment, AssignmentParticipant, PackingItem, PackingBag, PackingBagMember, BudgetItem, BudgetItemMember, Reservation, ReservationEndpoint, Accommodation, Tag, Category, } from '@trek/shared' export type { Trip, TripMember, Day, DayNote, Place, AssignmentPlace, PlaceCategory, Assignment, AssignmentParticipant, PackingItem, PackingBag, PackingBagMember, BudgetItem, BudgetItemMember, Reservation, ReservationEndpoint, Accommodation, Tag, Category, } export interface User { id: number username: string email: string role: 'admin' | 'user' avatar_url: string | null maps_api_key: string | null created_at: string /** Present after load; true when TOTP MFA is enabled for password login */ mfa_enabled?: boolean /** True when a password change is required before the user can continue */ must_change_password?: boolean } export interface TodoItem { id: number trip_id: number name: string category: string | null checked: number sort_order: number due_date: string | null description: string | null assigned_user_id: number | null priority: number } export interface TripFile { id: number trip_id: number place_id?: number | null reservation_id?: number | null note_id?: number | null uploaded_by?: number | null uploaded_by_name?: string | null uploaded_by_avatar?: string | null filename: string original_name: string file_size?: number | null mime_type: string description?: string | null starred?: number deleted_at?: string | null created_at: string reservation_title?: string linked_reservation_ids?: (number | null)[] linked_place_ids?: (number | null)[] /** Served download path — always present on list/create/update responses (formatFile). */ url: string } export interface Settings { map_tile_url: string default_lat: number default_lng: number default_zoom: number dark_mode: boolean | string default_currency: string language: string temperature_unit: string time_format: string show_place_description: boolean blur_booking_codes?: boolean map_booking_labels?: boolean map_provider?: 'leaflet' | 'mapbox-gl' mapbox_access_token?: string mapbox_style?: string mapbox_3d_enabled?: boolean mapbox_quality_mode?: boolean } export interface AssignmentsMap { [dayId: string]: Assignment[] } export interface DayNotesMap { [dayId: string]: DayNote[] } export interface RouteSegment { mid: [number, number] from: [number, number] to: [number, number] distance: number duration: number walkingText: string drivingText: string distanceText: string durationText?: string } export interface RouteWithLegs { coordinates: [number, number][] distance: number duration: number legs: RouteSegment[] } export interface RouteResult { coordinates: [number, number][] distance: number duration: number distanceText: string durationText: string walkingText: string drivingText: string } export interface Waypoint { lat: number lng: number } // User with optional OIDC fields export interface UserWithOidc extends User { oidc_issuer?: string | null } // Atlas place detail export interface AtlasPlace { id: number name: string lat: number | null lng: number | null } // GeoJSON types (simplified for atlas map) export interface GeoJsonFeature { type: 'Feature' properties: Record geometry: { type: string coordinates: unknown } id?: string } export interface GeoJsonFeatureCollection { type: 'FeatureCollection' features: GeoJsonFeature[] } // App config from /auth/app-config export interface AppConfig { has_users: boolean allow_registration: boolean demo_mode: boolean oidc_configured: boolean oidc_display_name?: string oidc_only_mode?: boolean has_maps_key?: boolean allowed_file_types?: string timezone?: string /** When true, users without MFA cannot use the app until they enable it */ require_mfa?: boolean // Granular auth toggles password_login?: boolean password_registration?: boolean oidc_login?: boolean oidc_registration?: boolean env_override_oidc_only?: boolean } // Translation function type export type TranslationFn = (key: string, params?: Record) => string // WebSocket event type export interface WebSocketEvent { type: string [key: string]: unknown } // Vacay types export interface VacayHolidayCalendar { id: number plan_id: number region: string label: string | null color: string sort_order: number } export interface VacayPlan { id: number holidays_enabled: boolean holidays_region: string | null holiday_calendars: VacayHolidayCalendar[] block_weekends: boolean carry_over_enabled: boolean company_holidays_enabled: boolean // Comma-separated weekday indices (e.g. '0,6'); stored as TEXT on vacay_plans. weekend_days?: string week_start?: number name?: string year?: number owner_id?: number created_at?: string updated_at?: string } export interface VacayUser { id: number username: string color: string | null } export interface VacayEntry { date: string user_id: number plan_id?: number person_color?: string person_name?: string } // Vacay per-user stats row as returned by getStats // (server/src/services/vacayService.ts -> getStats). export interface VacayStat { user_id: number person_name: string person_color: string year: number vacation_days: number carried_over: number total_available: number used: number remaining: number } export interface HolidayInfo { name: string localName: string color: string label: string | null } export interface HolidaysMap { [date: string]: HolidayInfo } // API error shape from axios export interface ApiError { response?: { data?: { error?: string } status?: number } message: string } /** Safely extract an error message from an unknown catch value */ export function getApiErrorMessage(err: unknown, fallback: string): string { if (typeof err === 'object' && err !== null && 'response' in err) { const apiErr = err as ApiError if (apiErr.response?.data?.error) return apiErr.response.data.error } if (err instanceof Error) return err.message return fallback } // MergedItem used in day notes hook export interface MergedItem { type: 'assignment' | 'note' | 'place' | 'transport' sortKey: number data: Assignment | DayNote | Reservation }