innercontext/frontend/src/lib/api.ts

349 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<T>(path: string, init: RequestInit = {}): Promise<T> {
// 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: <T>(path: string) => request<T>(path),
post: <T>(path: string, body: unknown) =>
request<T>(path, { method: "POST", body: JSON.stringify(body) }),
patch: <T>(path: string, body: unknown) =>
request<T>(path, { method: "PATCH", body: JSON.stringify(body) }),
del: (path: string) => request<void>(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<Product[]> {
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<Product> =>
api.get(`/products/${id}`);
export const createProduct = (
body: Record<string, unknown>,
): Promise<Product> => api.post("/products", body);
export const updateProduct = (
id: string,
body: Record<string, unknown>,
): Promise<Product> => api.patch(`/products/${id}`, body);
export const deleteProduct = (id: string): Promise<void> =>
api.del(`/products/${id}`);
export const getInventory = (productId: string): Promise<ProductInventory[]> =>
api.get(`/products/${productId}/inventory`);
export const createInventory = (
productId: string,
body: Record<string, unknown>,
): Promise<ProductInventory> =>
api.post(`/products/${productId}/inventory`, body);
export const updateInventory = (
id: string,
body: Record<string, unknown>,
): Promise<ProductInventory> => api.patch(`/inventory/${id}`, body);
export const deleteInventory = (id: string): Promise<void> =>
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<ProductParseResponse> =>
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<Routine[]> {
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<Routine> =>
api.get(`/routines/${id}`);
export const createRoutine = (
body: Record<string, unknown>,
): Promise<Routine> => api.post("/routines", body);
export const updateRoutine = (
id: string,
body: Record<string, unknown>,
): Promise<Routine> => api.patch(`/routines/${id}`, body);
export const deleteRoutine = (id: string): Promise<void> =>
api.del(`/routines/${id}`);
export const addRoutineStep = (
routineId: string,
body: Record<string, unknown>,
): Promise<RoutineStep> => api.post(`/routines/${routineId}/steps`, body);
export const updateRoutineStep = (
stepId: string,
body: Record<string, unknown>,
): Promise<RoutineStep> => api.patch(`/routines/steps/${stepId}`, body);
export const deleteRoutineStep = (stepId: string): Promise<void> =>
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<RoutineSuggestion> => 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<BatchSuggestion> => api.post("/routines/suggest-batch", body);
export const getGroomingSchedule = (): Promise<GroomingSchedule[]> =>
api.get("/routines/grooming-schedule");
export const createGroomingScheduleEntry = (
body: Record<string, unknown>,
): Promise<GroomingSchedule> => api.post("/routines/grooming-schedule", body);
export const updateGroomingScheduleEntry = (
id: string,
body: Record<string, unknown>,
): Promise<GroomingSchedule> =>
api.patch(`/routines/grooming-schedule/${id}`, body);
export const deleteGroomingScheduleEntry = (id: string): Promise<void> =>
api.del(`/routines/grooming-schedule/${id}`);
// ─── Health Medications ────────────────────────────────────────────────────
export interface MedicationListParams {
kind?: string;
product_name?: string;
}
export function getMedications(
params: MedicationListParams = {},
): Promise<MedicationEntry[]> {
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<MedicationEntry> =>
api.get(`/health/medications/${id}`);
export const createMedication = (
body: Record<string, unknown>,
): Promise<MedicationEntry> => api.post("/health/medications", body);
export const updateMedication = (
id: string,
body: Record<string, unknown>,
): Promise<MedicationEntry> => api.patch(`/health/medications/${id}`, body);
export const deleteMedication = (id: string): Promise<void> =>
api.del(`/health/medications/${id}`);
export const getMedicationUsages = (
medicationId: string,
): Promise<MedicationUsage[]> =>
api.get(`/health/medications/${medicationId}/usages`);
export const createMedicationUsage = (
medicationId: string,
body: Record<string, unknown>,
): Promise<MedicationUsage> =>
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<LabResult[]> {
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<LabResult> =>
api.get(`/health/lab-results/${id}`);
export const createLabResult = (
body: Record<string, unknown>,
): Promise<LabResult> => api.post("/health/lab-results", body);
export const updateLabResult = (
id: string,
body: Record<string, unknown>,
): Promise<LabResult> => api.patch(`/health/lab-results/${id}`, body);
export const deleteLabResult = (id: string): Promise<void> =>
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<SkinConditionSnapshot[]> {
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<SkinConditionSnapshot> =>
api.get(`/skincare/${id}`);
export const createSkinSnapshot = (
body: Record<string, unknown>,
): Promise<SkinConditionSnapshot> => api.post("/skincare", body);
export const updateSkinSnapshot = (
id: string,
body: Record<string, unknown>,
): Promise<SkinConditionSnapshot> => api.patch(`/skincare/${id}`, body);
export const deleteSkinSnapshot = (id: string): Promise<void> =>
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<SkinPhotoAnalysisResponse> {
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();
}