export type Activity = { spot_id: string; waterbody_slug: string; waterbody: string; fish_slug: string; fish: string; x: number; y: number; best_bait: string | null; catches: number; unique_players: number; average_weight_g: number; max_weight_g: number; last_confirmed_at: string; activity_score: number; confidence_score: number; explanation: string; sources: string[]; }; export type PaginatedActivity = { items: Activity[]; total: number; limit: number; offset: number; }; export type Spot = { id: string; waterbody_slug: string; waterbody: string; x: number; y: number; description: string | null; catches_24h: number; catches_3d: number; catches_7d: number; top_baits: string[] }; export type Catch = { id: string; fish: string; weight_g: number; bait: string | null; player_name: string | null; caught_at: string | null; reported_at: string; retrieve_method: string | null; retrieve_speed: number | null; source_system: string; source_url: string | null }; export type DictionaryItem = { id: string; slug: string; name_ru: string }; export type OfficialRecord = { id: string; fish: string; weight_g: number; waterbody: string; bait: string | null; player_name: string | null; record_date: string | null; category: string | null; region: string | null; source_url: string | null; source_system: string }; export type PublicObservation = { id: string; source_system: string; source_name: string; source_url: string; fish_name: string; waterbody_name: string; x: number | null; y: number | null; weight_g: number | null; last_seen_at: string; missing_fields: string[]; quality: "incomplete" | "unverified" }; export type ImportRun = { id: string; started_at: string; finished_at: string | null; status: string; source_url: string; rows_seen: number; rows_created: number; rows_updated: number; error_summary: string | null }; export type SourceStatus = { source_system: string; name: string; status: "healthy" | "stale" | "temporarily_limited" | "source_changed" | "waiting" | "disabled"; last_started_at: string | null; last_success_at: string | null; observations: number }; export const spotPath = (item: Pick) => `/spots/${item.waterbody_slug}-${item.x}x${item.y}`; export { activityLevel, ago, kg, plural } from "./presentation"; const base = process.env.API_INTERNAL_URL || import.meta.env.API_INTERNAL_URL || "http://localhost:8000"; export class ApiError extends Error { constructor(public status: number) { super(`API ${status}`); } } export async function api(path: string): Promise { const response = await fetch(`${base}${path}`, { signal: AbortSignal.timeout(8000) }); if (!response.ok) throw new ApiError(response.status); return response.json() as Promise; }