import { browser } from "$app/environment"; import { PUBLIC_API_BASE } from "$env/static/public"; import type { ActiveIngredient, BatchSuggestion, GroomingSchedule, LabResult, MedicationEntry, MedicationUsage, PartOfDay, Product, ProductContext, ProductEffectProfile, ProductInteraction, ProductInventory, Routine, RoutineSuggestion, RoutineStep, SkinConditionSnapshot, } from "./types"; // ─── Core fetch helpers ────────────────────────────────────────────────────── async function request(path: string, init: RequestInit = {}): Promise { // Server-side uses PUBLIC_API_BASE (e.g. http://localhost:8000). // Browser-side uses /api so nginx proxies the request on the correct host. const base = browser ? "/api" : PUBLIC_API_BASE; const url = `${base}${path}`; const res = await fetch(url, { headers: { "Content-Type": "application/json", ...init.headers }, ...init, }); if (!res.ok) { const detail = await res.json().catch(() => ({ detail: res.statusText })); throw new Error(detail?.detail ?? res.statusText); } if (res.status === 204) return undefined as T; return res.json(); } export const api = { get: (path: string) => request(path), post: (path: string, body: unknown) => request(path, { method: "POST", body: JSON.stringify(body) }), patch: (path: string, body: unknown) => request(path, { method: "PATCH", body: JSON.stringify(body) }), del: (path: string) => request(path, { method: "DELETE" }), }; // ─── Products ──────────────────────────────────────────────────────────────── export interface ProductListParams { category?: string; brand?: string; targets?: string[]; is_medication?: boolean; is_tool?: boolean; } export function getProducts( params: ProductListParams = {}, ): Promise { const q = new URLSearchParams(); if (params.category) q.set("category", params.category); if (params.brand) q.set("brand", params.brand); if (params.targets) params.targets.forEach((t) => q.append("targets", t)); if (params.is_medication != null) q.set("is_medication", String(params.is_medication)); if (params.is_tool != null) q.set("is_tool", String(params.is_tool)); const qs = q.toString(); return api.get(`/products${qs ? `?${qs}` : ""}`); } export const getProduct = (id: string): Promise => api.get(`/products/${id}`); export const createProduct = ( body: Record, ): Promise => api.post("/products", body); export const updateProduct = ( id: string, body: Record, ): Promise => api.patch(`/products/${id}`, body); export const deleteProduct = (id: string): Promise => api.del(`/products/${id}`); export const getInventory = (productId: string): Promise => api.get(`/products/${productId}/inventory`); export const createInventory = ( productId: string, body: Record, ): Promise => api.post(`/products/${productId}/inventory`, body); export const updateInventory = ( id: string, body: Record, ): Promise => api.patch(`/inventory/${id}`, body); export const deleteInventory = (id: string): Promise => api.del(`/inventory/${id}`); export interface ProductParseResponse { name?: string; brand?: string; line_name?: string; sku?: string; url?: string; barcode?: string; category?: string; recommended_time?: string; texture?: string; absorption_speed?: string; leave_on?: boolean; price_tier?: string; size_ml?: number; full_weight_g?: number; empty_weight_g?: number; pao_months?: number; inci?: string[]; actives?: ActiveIngredient[]; recommended_for?: string[]; targets?: string[]; contraindications?: string[]; usage_notes?: string; fragrance_free?: boolean; essential_oils_free?: boolean; alcohol_denat_free?: boolean; pregnancy_safe?: boolean; product_effect_profile?: ProductEffectProfile; ph_min?: number; ph_max?: number; incompatible_with?: ProductInteraction[]; synergizes_with?: string[]; context_rules?: ProductContext; min_interval_hours?: number; max_frequency_per_week?: number; is_medication?: boolean; is_tool?: boolean; needle_length_mm?: number; } export const parseProductText = (text: string): Promise => api.post("/products/parse-text", { text }); // ─── Routines ──────────────────────────────────────────────────────────────── export interface RoutineListParams { from_date?: string; to_date?: string; part_of_day?: string; } export function getRoutines( params: RoutineListParams = {}, ): Promise { const q = new URLSearchParams(); if (params.from_date) q.set("from_date", params.from_date); if (params.to_date) q.set("to_date", params.to_date); if (params.part_of_day) q.set("part_of_day", params.part_of_day); const qs = q.toString(); return api.get(`/routines${qs ? `?${qs}` : ""}`); } export const getRoutine = (id: string): Promise => api.get(`/routines/${id}`); export const createRoutine = ( body: Record, ): Promise => api.post("/routines", body); export const updateRoutine = ( id: string, body: Record, ): Promise => api.patch(`/routines/${id}`, body); export const deleteRoutine = (id: string): Promise => api.del(`/routines/${id}`); export const addRoutineStep = ( routineId: string, body: Record, ): Promise => api.post(`/routines/${routineId}/steps`, body); export const updateRoutineStep = ( stepId: string, body: Record, ): Promise => api.patch(`/routines/steps/${stepId}`, body); export const deleteRoutineStep = (stepId: string): Promise => api.del(`/routines/steps/${stepId}`); export const suggestRoutine = (body: { routine_date: string; part_of_day: PartOfDay; notes?: string; include_minoxidil_beard?: boolean; leaving_home?: boolean; }): Promise => api.post("/routines/suggest", body); export const suggestBatch = (body: { from_date: string; to_date: string; notes?: string; include_minoxidil_beard?: boolean; minimize_products?: boolean; }): Promise => api.post("/routines/suggest-batch", body); export const getGroomingSchedule = (): Promise => api.get("/routines/grooming-schedule"); export const createGroomingScheduleEntry = ( body: Record, ): Promise => api.post("/routines/grooming-schedule", body); export const updateGroomingScheduleEntry = ( id: string, body: Record, ): Promise => api.patch(`/routines/grooming-schedule/${id}`, body); export const deleteGroomingScheduleEntry = (id: string): Promise => api.del(`/routines/grooming-schedule/${id}`); // ─── Health – Medications ──────────────────────────────────────────────────── export interface MedicationListParams { kind?: string; product_name?: string; } export function getMedications( params: MedicationListParams = {}, ): Promise { const q = new URLSearchParams(); if (params.kind) q.set("kind", params.kind); if (params.product_name) q.set("product_name", params.product_name); const qs = q.toString(); return api.get(`/health/medications${qs ? `?${qs}` : ""}`); } export const getMedication = (id: string): Promise => api.get(`/health/medications/${id}`); export const createMedication = ( body: Record, ): Promise => api.post("/health/medications", body); export const updateMedication = ( id: string, body: Record, ): Promise => api.patch(`/health/medications/${id}`, body); export const deleteMedication = (id: string): Promise => api.del(`/health/medications/${id}`); export const getMedicationUsages = ( medicationId: string, ): Promise => api.get(`/health/medications/${medicationId}/usages`); export const createMedicationUsage = ( medicationId: string, body: Record, ): Promise => api.post(`/health/medications/${medicationId}/usages`, body); // ─── Health – Lab results ──────────────────────────────────────────────────── export interface LabResultListParams { test_code?: string; flag?: string; lab?: string; from_date?: string; to_date?: string; } export function getLabResults( params: LabResultListParams = {}, ): Promise { const q = new URLSearchParams(); if (params.test_code) q.set("test_code", params.test_code); if (params.flag) q.set("flag", params.flag); if (params.lab) q.set("lab", params.lab); if (params.from_date) q.set("from_date", params.from_date); if (params.to_date) q.set("to_date", params.to_date); const qs = q.toString(); return api.get(`/health/lab-results${qs ? `?${qs}` : ""}`); } export const getLabResult = (id: string): Promise => api.get(`/health/lab-results/${id}`); export const createLabResult = ( body: Record, ): Promise => api.post("/health/lab-results", body); export const updateLabResult = ( id: string, body: Record, ): Promise => api.patch(`/health/lab-results/${id}`, body); export const deleteLabResult = (id: string): Promise => api.del(`/health/lab-results/${id}`); // ─── Skin ──────────────────────────────────────────────────────────────────── export interface SnapshotListParams { from_date?: string; to_date?: string; overall_state?: string; } export function getSkinSnapshots( params: SnapshotListParams = {}, ): Promise { const q = new URLSearchParams(); if (params.from_date) q.set("from_date", params.from_date); if (params.to_date) q.set("to_date", params.to_date); if (params.overall_state) q.set("overall_state", params.overall_state); const qs = q.toString(); return api.get(`/skincare${qs ? `?${qs}` : ""}`); } export const getSkinSnapshot = (id: string): Promise => api.get(`/skincare/${id}`); export const createSkinSnapshot = ( body: Record, ): Promise => api.post("/skincare", body); export const updateSkinSnapshot = ( id: string, body: Record, ): Promise => api.patch(`/skincare/${id}`, body); export const deleteSkinSnapshot = (id: string): Promise => api.del(`/skincare/${id}`); export interface SkinPhotoAnalysisResponse { overall_state?: string; texture?: string; skin_type?: string; hydration_level?: number; sebum_tzone?: number; sebum_cheeks?: number; sensitivity_level?: number; barrier_state?: string; active_concerns?: string[]; risks?: string[]; priorities?: string[]; notes?: string; } export async function analyzeSkinPhotos( files: File[], ): Promise { const body = new FormData(); for (const file of files) body.append("photos", file); const base = browser ? "/api" : PUBLIC_API_BASE; const res = await fetch(`${base}/skincare/analyze-photos`, { method: "POST", body, }); if (!res.ok) { const detail = await res.json().catch(() => ({ detail: res.statusText })); throw new Error(detail?.detail ?? res.statusText); } return res.json(); }