chore(frontend): format files with prettier
This commit is contained in:
parent
0e7a39836f
commit
067e460dd2
20 changed files with 1615 additions and 1509 deletions
10
frontend/.mcp.json
Normal file
10
frontend/.mcp.json
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"svelte": {
|
||||||
|
"type": "stdio",
|
||||||
|
"command": "npx",
|
||||||
|
"env": {},
|
||||||
|
"args": ["-y", "@sveltejs/mcp"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -25,7 +25,7 @@ The backend must be running at `http://localhost:8000`. See `../backend/` for se
|
||||||
## Environment variables
|
## Environment variables
|
||||||
|
|
||||||
| Variable | Description | Default |
|
| Variable | Description | Default |
|
||||||
|---|---|---|
|
| ----------------- | ------------------------------- | ----------------------- |
|
||||||
| `PUBLIC_API_BASE` | Base URL of the FastAPI backend | `http://localhost:8000` |
|
| `PUBLIC_API_BASE` | Base URL of the FastAPI backend | `http://localhost:8000` |
|
||||||
|
|
||||||
Set `PUBLIC_API_BASE` at **build time** for production:
|
Set `PUBLIC_API_BASE` at **build time** for production:
|
||||||
|
|
@ -52,7 +52,7 @@ Or use the provided systemd service: `../systemd/innercontext-node.service`.
|
||||||
## Routes
|
## Routes
|
||||||
|
|
||||||
| Route | Description |
|
| Route | Description |
|
||||||
|---|---|
|
| --------------------- | ------------------------ |
|
||||||
| `/` | Dashboard |
|
| `/` | Dashboard |
|
||||||
| `/products` | Product list |
|
| `/products` | Product list |
|
||||||
| `/products/new` | Add product |
|
| `/products/new` | Add product |
|
||||||
|
|
@ -67,7 +67,7 @@ Or use the provided systemd service: `../systemd/innercontext-node.service`.
|
||||||
## Key files
|
## Key files
|
||||||
|
|
||||||
| File | Purpose |
|
| File | Purpose |
|
||||||
|---|---|
|
| ------------------ | --------------------------------- |
|
||||||
| `src/lib/api.ts` | API client (typed fetch wrappers) |
|
| `src/lib/api.ts` | API client (typed fetch wrappers) |
|
||||||
| `src/lib/types.ts` | Shared TypeScript types |
|
| `src/lib/types.ts` | Shared TypeScript types |
|
||||||
| `src/app.css` | Tailwind v4 theme + global styles |
|
| `src/app.css` | Tailwind v4 theme + global styles |
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { paraglideMiddleware } from '$lib/paraglide/server.js';
|
import { paraglideMiddleware } from "$lib/paraglide/server.js";
|
||||||
import type { Handle } from '@sveltejs/kit';
|
import type { Handle } from "@sveltejs/kit";
|
||||||
|
|
||||||
export const handle: Handle = async ({ event, resolve }) => {
|
export const handle: Handle = async ({ event, resolve }) => {
|
||||||
return paraglideMiddleware(event.request, () => resolve(event));
|
return paraglideMiddleware(event.request, () => resolve(event));
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { browser } from '$app/environment';
|
import { browser } from "$app/environment";
|
||||||
import { PUBLIC_API_BASE } from '$env/static/public';
|
import { PUBLIC_API_BASE } from "$env/static/public";
|
||||||
import type {
|
import type {
|
||||||
ActiveIngredient,
|
ActiveIngredient,
|
||||||
BatchSuggestion,
|
BatchSuggestion,
|
||||||
|
|
@ -16,19 +16,19 @@ import type {
|
||||||
Routine,
|
Routine,
|
||||||
RoutineSuggestion,
|
RoutineSuggestion,
|
||||||
RoutineStep,
|
RoutineStep,
|
||||||
SkinConditionSnapshot
|
SkinConditionSnapshot,
|
||||||
} from './types';
|
} from "./types";
|
||||||
|
|
||||||
// ─── Core fetch helpers ──────────────────────────────────────────────────────
|
// ─── Core fetch helpers ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
async function request<T>(path: string, init: RequestInit = {}): Promise<T> {
|
async function request<T>(path: string, init: RequestInit = {}): Promise<T> {
|
||||||
// Server-side uses PUBLIC_API_BASE (e.g. http://localhost:8000).
|
// Server-side uses PUBLIC_API_BASE (e.g. http://localhost:8000).
|
||||||
// Browser-side uses /api so nginx proxies the request on the correct host.
|
// Browser-side uses /api so nginx proxies the request on the correct host.
|
||||||
const base = browser ? '/api' : PUBLIC_API_BASE;
|
const base = browser ? "/api" : PUBLIC_API_BASE;
|
||||||
const url = `${base}${path}`;
|
const url = `${base}${path}`;
|
||||||
const res = await fetch(url, {
|
const res = await fetch(url, {
|
||||||
headers: { 'Content-Type': 'application/json', ...init.headers },
|
headers: { "Content-Type": "application/json", ...init.headers },
|
||||||
...init
|
...init,
|
||||||
});
|
});
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
const detail = await res.json().catch(() => ({ detail: res.statusText }));
|
const detail = await res.json().catch(() => ({ detail: res.statusText }));
|
||||||
|
|
@ -41,10 +41,10 @@ async function request<T>(path: string, init: RequestInit = {}): Promise<T> {
|
||||||
export const api = {
|
export const api = {
|
||||||
get: <T>(path: string) => request<T>(path),
|
get: <T>(path: string) => request<T>(path),
|
||||||
post: <T>(path: string, body: unknown) =>
|
post: <T>(path: string, body: unknown) =>
|
||||||
request<T>(path, { method: 'POST', body: JSON.stringify(body) }),
|
request<T>(path, { method: "POST", body: JSON.stringify(body) }),
|
||||||
patch: <T>(path: string, body: unknown) =>
|
patch: <T>(path: string, body: unknown) =>
|
||||||
request<T>(path, { method: 'PATCH', body: JSON.stringify(body) }),
|
request<T>(path, { method: "PATCH", body: JSON.stringify(body) }),
|
||||||
del: (path: string) => request<void>(path, { method: 'DELETE' })
|
del: (path: string) => request<void>(path, { method: "DELETE" }),
|
||||||
};
|
};
|
||||||
|
|
||||||
// ─── Products ────────────────────────────────────────────────────────────────
|
// ─── Products ────────────────────────────────────────────────────────────────
|
||||||
|
|
@ -57,54 +57,88 @@ export interface ProductListParams {
|
||||||
is_tool?: boolean;
|
is_tool?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getProducts(params: ProductListParams = {}): Promise<Product[]> {
|
export function getProducts(
|
||||||
|
params: ProductListParams = {},
|
||||||
|
): Promise<Product[]> {
|
||||||
const q = new URLSearchParams();
|
const q = new URLSearchParams();
|
||||||
if (params.category) q.set('category', params.category);
|
if (params.category) q.set("category", params.category);
|
||||||
if (params.brand) q.set('brand', params.brand);
|
if (params.brand) q.set("brand", params.brand);
|
||||||
if (params.targets) params.targets.forEach((t) => q.append('targets', t));
|
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_medication != null)
|
||||||
if (params.is_tool != null) q.set('is_tool', String(params.is_tool));
|
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();
|
const qs = q.toString();
|
||||||
return api.get(`/products${qs ? `?${qs}` : ''}`);
|
return api.get(`/products${qs ? `?${qs}` : ""}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getProduct = (id: string): Promise<Product> => api.get(`/products/${id}`);
|
export const getProduct = (id: string): Promise<Product> =>
|
||||||
export const createProduct = (body: Record<string, unknown>): Promise<Product> =>
|
api.get(`/products/${id}`);
|
||||||
api.post('/products', body);
|
export const createProduct = (
|
||||||
export const updateProduct = (id: string, body: Record<string, unknown>): Promise<Product> =>
|
body: Record<string, unknown>,
|
||||||
api.patch(`/products/${id}`, body);
|
): Promise<Product> => api.post("/products", body);
|
||||||
export const deleteProduct = (id: string): Promise<void> => api.del(`/products/${id}`);
|
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[]> =>
|
export const getInventory = (productId: string): Promise<ProductInventory[]> =>
|
||||||
api.get(`/products/${productId}/inventory`);
|
api.get(`/products/${productId}/inventory`);
|
||||||
export const createInventory = (
|
export const createInventory = (
|
||||||
productId: string,
|
productId: string,
|
||||||
body: Record<string, unknown>
|
body: Record<string, unknown>,
|
||||||
): Promise<ProductInventory> => api.post(`/products/${productId}/inventory`, body);
|
): Promise<ProductInventory> =>
|
||||||
export const updateInventory = (id: string, body: Record<string, unknown>): Promise<ProductInventory> =>
|
api.post(`/products/${productId}/inventory`, body);
|
||||||
api.patch(`/inventory/${id}`, body);
|
export const updateInventory = (
|
||||||
export const deleteInventory = (id: string): Promise<void> => api.del(`/inventory/${id}`);
|
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 {
|
export interface ProductParseResponse {
|
||||||
name?: string; brand?: string; line_name?: string; sku?: string; url?: string; barcode?: string;
|
name?: string;
|
||||||
category?: string; recommended_time?: string; texture?: string; absorption_speed?: string;
|
brand?: string;
|
||||||
leave_on?: boolean; price_tier?: string;
|
line_name?: string;
|
||||||
size_ml?: number; full_weight_g?: number; empty_weight_g?: number; pao_months?: number;
|
sku?: string;
|
||||||
inci?: string[]; actives?: ActiveIngredient[];
|
url?: string;
|
||||||
recommended_for?: string[]; targets?: string[];
|
barcode?: string;
|
||||||
contraindications?: string[]; usage_notes?: string;
|
category?: string;
|
||||||
fragrance_free?: boolean; essential_oils_free?: boolean;
|
recommended_time?: string;
|
||||||
alcohol_denat_free?: boolean; pregnancy_safe?: boolean;
|
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;
|
product_effect_profile?: ProductEffectProfile;
|
||||||
ph_min?: number; ph_max?: number;
|
ph_min?: number;
|
||||||
incompatible_with?: ProductInteraction[]; synergizes_with?: string[];
|
ph_max?: number;
|
||||||
|
incompatible_with?: ProductInteraction[];
|
||||||
|
synergizes_with?: string[];
|
||||||
context_rules?: ProductContext;
|
context_rules?: ProductContext;
|
||||||
min_interval_hours?: number; max_frequency_per_week?: number;
|
min_interval_hours?: number;
|
||||||
is_medication?: boolean; is_tool?: boolean; needle_length_mm?: number;
|
max_frequency_per_week?: number;
|
||||||
|
is_medication?: boolean;
|
||||||
|
is_tool?: boolean;
|
||||||
|
needle_length_mm?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const parseProductText = (text: string): Promise<ProductParseResponse> =>
|
export const parseProductText = (text: string): Promise<ProductParseResponse> =>
|
||||||
api.post('/products/parse-text', { text });
|
api.post("/products/parse-text", { text });
|
||||||
|
|
||||||
// ─── Routines ────────────────────────────────────────────────────────────────
|
// ─── Routines ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
@ -114,26 +148,37 @@ export interface RoutineListParams {
|
||||||
part_of_day?: string;
|
part_of_day?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getRoutines(params: RoutineListParams = {}): Promise<Routine[]> {
|
export function getRoutines(
|
||||||
|
params: RoutineListParams = {},
|
||||||
|
): Promise<Routine[]> {
|
||||||
const q = new URLSearchParams();
|
const q = new URLSearchParams();
|
||||||
if (params.from_date) q.set('from_date', params.from_date);
|
if (params.from_date) q.set("from_date", params.from_date);
|
||||||
if (params.to_date) q.set('to_date', params.to_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);
|
if (params.part_of_day) q.set("part_of_day", params.part_of_day);
|
||||||
const qs = q.toString();
|
const qs = q.toString();
|
||||||
return api.get(`/routines${qs ? `?${qs}` : ''}`);
|
return api.get(`/routines${qs ? `?${qs}` : ""}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getRoutine = (id: string): Promise<Routine> => api.get(`/routines/${id}`);
|
export const getRoutine = (id: string): Promise<Routine> =>
|
||||||
export const createRoutine = (body: Record<string, unknown>): Promise<Routine> =>
|
api.get(`/routines/${id}`);
|
||||||
api.post('/routines', body);
|
export const createRoutine = (
|
||||||
export const updateRoutine = (id: string, body: Record<string, unknown>): Promise<Routine> =>
|
body: Record<string, unknown>,
|
||||||
api.patch(`/routines/${id}`, body);
|
): Promise<Routine> => api.post("/routines", body);
|
||||||
export const deleteRoutine = (id: string): Promise<void> => api.del(`/routines/${id}`);
|
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> =>
|
export const addRoutineStep = (
|
||||||
api.post(`/routines/${routineId}/steps`, body);
|
routineId: string,
|
||||||
export const updateRoutineStep = (stepId: string, body: Record<string, unknown>): Promise<RoutineStep> =>
|
body: Record<string, unknown>,
|
||||||
api.patch(`/routines/steps/${stepId}`, body);
|
): 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> =>
|
export const deleteRoutineStep = (stepId: string): Promise<void> =>
|
||||||
api.del(`/routines/steps/${stepId}`);
|
api.del(`/routines/steps/${stepId}`);
|
||||||
|
|
||||||
|
|
@ -143,7 +188,7 @@ export const suggestRoutine = (body: {
|
||||||
notes?: string;
|
notes?: string;
|
||||||
include_minoxidil_beard?: boolean;
|
include_minoxidil_beard?: boolean;
|
||||||
leaving_home?: boolean;
|
leaving_home?: boolean;
|
||||||
}): Promise<RoutineSuggestion> => api.post('/routines/suggest', body);
|
}): Promise<RoutineSuggestion> => api.post("/routines/suggest", body);
|
||||||
|
|
||||||
export const suggestBatch = (body: {
|
export const suggestBatch = (body: {
|
||||||
from_date: string;
|
from_date: string;
|
||||||
|
|
@ -151,13 +196,17 @@ export const suggestBatch = (body: {
|
||||||
notes?: string;
|
notes?: string;
|
||||||
include_minoxidil_beard?: boolean;
|
include_minoxidil_beard?: boolean;
|
||||||
minimize_products?: boolean;
|
minimize_products?: boolean;
|
||||||
}): Promise<BatchSuggestion> => api.post('/routines/suggest-batch', body);
|
}): Promise<BatchSuggestion> => api.post("/routines/suggest-batch", body);
|
||||||
|
|
||||||
export const getGroomingSchedule = (): Promise<GroomingSchedule[]> =>
|
export const getGroomingSchedule = (): Promise<GroomingSchedule[]> =>
|
||||||
api.get('/routines/grooming-schedule');
|
api.get("/routines/grooming-schedule");
|
||||||
export const createGroomingScheduleEntry = (body: Record<string, unknown>): Promise<GroomingSchedule> =>
|
export const createGroomingScheduleEntry = (
|
||||||
api.post('/routines/grooming-schedule', body);
|
body: Record<string, unknown>,
|
||||||
export const updateGroomingScheduleEntry = (id: string, body: Record<string, unknown>): Promise<GroomingSchedule> =>
|
): 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);
|
api.patch(`/routines/grooming-schedule/${id}`, body);
|
||||||
export const deleteGroomingScheduleEntry = (id: string): Promise<void> =>
|
export const deleteGroomingScheduleEntry = (id: string): Promise<void> =>
|
||||||
api.del(`/routines/grooming-schedule/${id}`);
|
api.del(`/routines/grooming-schedule/${id}`);
|
||||||
|
|
@ -169,31 +218,37 @@ export interface MedicationListParams {
|
||||||
product_name?: string;
|
product_name?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMedications(params: MedicationListParams = {}): Promise<MedicationEntry[]> {
|
export function getMedications(
|
||||||
|
params: MedicationListParams = {},
|
||||||
|
): Promise<MedicationEntry[]> {
|
||||||
const q = new URLSearchParams();
|
const q = new URLSearchParams();
|
||||||
if (params.kind) q.set('kind', params.kind);
|
if (params.kind) q.set("kind", params.kind);
|
||||||
if (params.product_name) q.set('product_name', params.product_name);
|
if (params.product_name) q.set("product_name", params.product_name);
|
||||||
const qs = q.toString();
|
const qs = q.toString();
|
||||||
return api.get(`/health/medications${qs ? `?${qs}` : ''}`);
|
return api.get(`/health/medications${qs ? `?${qs}` : ""}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getMedication = (id: string): Promise<MedicationEntry> =>
|
export const getMedication = (id: string): Promise<MedicationEntry> =>
|
||||||
api.get(`/health/medications/${id}`);
|
api.get(`/health/medications/${id}`);
|
||||||
export const createMedication = (body: Record<string, unknown>): Promise<MedicationEntry> =>
|
export const createMedication = (
|
||||||
api.post('/health/medications', body);
|
body: Record<string, unknown>,
|
||||||
|
): Promise<MedicationEntry> => api.post("/health/medications", body);
|
||||||
export const updateMedication = (
|
export const updateMedication = (
|
||||||
id: string,
|
id: string,
|
||||||
body: Record<string, unknown>
|
body: Record<string, unknown>,
|
||||||
): Promise<MedicationEntry> => api.patch(`/health/medications/${id}`, body);
|
): Promise<MedicationEntry> => api.patch(`/health/medications/${id}`, body);
|
||||||
export const deleteMedication = (id: string): Promise<void> =>
|
export const deleteMedication = (id: string): Promise<void> =>
|
||||||
api.del(`/health/medications/${id}`);
|
api.del(`/health/medications/${id}`);
|
||||||
|
|
||||||
export const getMedicationUsages = (medicationId: string): Promise<MedicationUsage[]> =>
|
export const getMedicationUsages = (
|
||||||
|
medicationId: string,
|
||||||
|
): Promise<MedicationUsage[]> =>
|
||||||
api.get(`/health/medications/${medicationId}/usages`);
|
api.get(`/health/medications/${medicationId}/usages`);
|
||||||
export const createMedicationUsage = (
|
export const createMedicationUsage = (
|
||||||
medicationId: string,
|
medicationId: string,
|
||||||
body: Record<string, unknown>
|
body: Record<string, unknown>,
|
||||||
): Promise<MedicationUsage> => api.post(`/health/medications/${medicationId}/usages`, body);
|
): Promise<MedicationUsage> =>
|
||||||
|
api.post(`/health/medications/${medicationId}/usages`, body);
|
||||||
|
|
||||||
// ─── Health – Lab results ────────────────────────────────────────────────────
|
// ─── Health – Lab results ────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
@ -205,23 +260,28 @@ export interface LabResultListParams {
|
||||||
to_date?: string;
|
to_date?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLabResults(params: LabResultListParams = {}): Promise<LabResult[]> {
|
export function getLabResults(
|
||||||
|
params: LabResultListParams = {},
|
||||||
|
): Promise<LabResult[]> {
|
||||||
const q = new URLSearchParams();
|
const q = new URLSearchParams();
|
||||||
if (params.test_code) q.set('test_code', params.test_code);
|
if (params.test_code) q.set("test_code", params.test_code);
|
||||||
if (params.flag) q.set('flag', params.flag);
|
if (params.flag) q.set("flag", params.flag);
|
||||||
if (params.lab) q.set('lab', params.lab);
|
if (params.lab) q.set("lab", params.lab);
|
||||||
if (params.from_date) q.set('from_date', params.from_date);
|
if (params.from_date) q.set("from_date", params.from_date);
|
||||||
if (params.to_date) q.set('to_date', params.to_date);
|
if (params.to_date) q.set("to_date", params.to_date);
|
||||||
const qs = q.toString();
|
const qs = q.toString();
|
||||||
return api.get(`/health/lab-results${qs ? `?${qs}` : ''}`);
|
return api.get(`/health/lab-results${qs ? `?${qs}` : ""}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getLabResult = (id: string): Promise<LabResult> =>
|
export const getLabResult = (id: string): Promise<LabResult> =>
|
||||||
api.get(`/health/lab-results/${id}`);
|
api.get(`/health/lab-results/${id}`);
|
||||||
export const createLabResult = (body: Record<string, unknown>): Promise<LabResult> =>
|
export const createLabResult = (
|
||||||
api.post('/health/lab-results', body);
|
body: Record<string, unknown>,
|
||||||
export const updateLabResult = (id: string, body: Record<string, unknown>): Promise<LabResult> =>
|
): Promise<LabResult> => api.post("/health/lab-results", body);
|
||||||
api.patch(`/health/lab-results/${id}`, 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> =>
|
export const deleteLabResult = (id: string): Promise<void> =>
|
||||||
api.del(`/health/lab-results/${id}`);
|
api.del(`/health/lab-results/${id}`);
|
||||||
|
|
||||||
|
|
@ -233,24 +293,28 @@ export interface SnapshotListParams {
|
||||||
overall_state?: string;
|
overall_state?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getSkinSnapshots(params: SnapshotListParams = {}): Promise<SkinConditionSnapshot[]> {
|
export function getSkinSnapshots(
|
||||||
|
params: SnapshotListParams = {},
|
||||||
|
): Promise<SkinConditionSnapshot[]> {
|
||||||
const q = new URLSearchParams();
|
const q = new URLSearchParams();
|
||||||
if (params.from_date) q.set('from_date', params.from_date);
|
if (params.from_date) q.set("from_date", params.from_date);
|
||||||
if (params.to_date) q.set('to_date', params.to_date);
|
if (params.to_date) q.set("to_date", params.to_date);
|
||||||
if (params.overall_state) q.set('overall_state', params.overall_state);
|
if (params.overall_state) q.set("overall_state", params.overall_state);
|
||||||
const qs = q.toString();
|
const qs = q.toString();
|
||||||
return api.get(`/skincare${qs ? `?${qs}` : ''}`);
|
return api.get(`/skincare${qs ? `?${qs}` : ""}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getSkinSnapshot = (id: string): Promise<SkinConditionSnapshot> =>
|
export const getSkinSnapshot = (id: string): Promise<SkinConditionSnapshot> =>
|
||||||
api.get(`/skincare/${id}`);
|
api.get(`/skincare/${id}`);
|
||||||
export const createSkinSnapshot = (body: Record<string, unknown>): Promise<SkinConditionSnapshot> =>
|
export const createSkinSnapshot = (
|
||||||
api.post('/skincare', body);
|
body: Record<string, unknown>,
|
||||||
|
): Promise<SkinConditionSnapshot> => api.post("/skincare", body);
|
||||||
export const updateSkinSnapshot = (
|
export const updateSkinSnapshot = (
|
||||||
id: string,
|
id: string,
|
||||||
body: Record<string, unknown>
|
body: Record<string, unknown>,
|
||||||
): Promise<SkinConditionSnapshot> => api.patch(`/skincare/${id}`, body);
|
): Promise<SkinConditionSnapshot> => api.patch(`/skincare/${id}`, body);
|
||||||
export const deleteSkinSnapshot = (id: string): Promise<void> => api.del(`/skincare/${id}`);
|
export const deleteSkinSnapshot = (id: string): Promise<void> =>
|
||||||
|
api.del(`/skincare/${id}`);
|
||||||
|
|
||||||
export interface SkinPhotoAnalysisResponse {
|
export interface SkinPhotoAnalysisResponse {
|
||||||
overall_state?: string;
|
overall_state?: string;
|
||||||
|
|
@ -267,11 +331,16 @@ export interface SkinPhotoAnalysisResponse {
|
||||||
notes?: string;
|
notes?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function analyzeSkinPhotos(files: File[]): Promise<SkinPhotoAnalysisResponse> {
|
export async function analyzeSkinPhotos(
|
||||||
|
files: File[],
|
||||||
|
): Promise<SkinPhotoAnalysisResponse> {
|
||||||
const body = new FormData();
|
const body = new FormData();
|
||||||
for (const file of files) body.append('photos', file);
|
for (const file of files) body.append("photos", file);
|
||||||
const base = browser ? '/api' : PUBLIC_API_BASE;
|
const base = browser ? "/api" : PUBLIC_API_BASE;
|
||||||
const res = await fetch(`${base}/skincare/analyze-photos`, { method: 'POST', body });
|
const res = await fetch(`${base}/skincare/analyze-photos`, {
|
||||||
|
method: "POST",
|
||||||
|
body,
|
||||||
|
});
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
const detail = await res.json().catch(() => ({ detail: res.statusText }));
|
const detail = await res.json().catch(() => ({ detail: res.statusText }));
|
||||||
throw new Error(detail?.detail ?? res.statusText);
|
throw new Error(detail?.detail ?? res.statusText);
|
||||||
|
|
|
||||||
|
|
@ -1,65 +1,92 @@
|
||||||
// ─── Enums ──────────────────────────────────────────────────────────────────
|
// ─── Enums ──────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
export type AbsorptionSpeed = 'very_fast' | 'fast' | 'moderate' | 'slow' | 'very_slow';
|
export type AbsorptionSpeed =
|
||||||
export type BarrierState = 'intact' | 'mildly_compromised' | 'compromised';
|
| "very_fast"
|
||||||
export type DayTime = 'am' | 'pm' | 'both';
|
| "fast"
|
||||||
export type GroomingAction = 'shaving_razor' | 'shaving_oneblade' | 'dermarolling';
|
| "moderate"
|
||||||
|
| "slow"
|
||||||
|
| "very_slow";
|
||||||
|
export type BarrierState = "intact" | "mildly_compromised" | "compromised";
|
||||||
|
export type DayTime = "am" | "pm" | "both";
|
||||||
|
export type GroomingAction =
|
||||||
|
| "shaving_razor"
|
||||||
|
| "shaving_oneblade"
|
||||||
|
| "dermarolling";
|
||||||
export type IngredientFunction =
|
export type IngredientFunction =
|
||||||
| 'humectant'
|
| "humectant"
|
||||||
| 'emollient'
|
| "emollient"
|
||||||
| 'occlusive'
|
| "occlusive"
|
||||||
| 'exfoliant_aha'
|
| "exfoliant_aha"
|
||||||
| 'exfoliant_bha'
|
| "exfoliant_bha"
|
||||||
| 'exfoliant_pha'
|
| "exfoliant_pha"
|
||||||
| 'retinoid'
|
| "retinoid"
|
||||||
| 'antioxidant'
|
| "antioxidant"
|
||||||
| 'soothing'
|
| "soothing"
|
||||||
| 'barrier_support'
|
| "barrier_support"
|
||||||
| 'brightening'
|
| "brightening"
|
||||||
| 'anti_acne'
|
| "anti_acne"
|
||||||
| 'ceramide'
|
| "ceramide"
|
||||||
| 'niacinamide'
|
| "niacinamide"
|
||||||
| 'sunscreen'
|
| "sunscreen"
|
||||||
| 'peptide'
|
| "peptide"
|
||||||
| 'hair_growth_stimulant'
|
| "hair_growth_stimulant"
|
||||||
| 'prebiotic'
|
| "prebiotic"
|
||||||
| 'vitamin_c'
|
| "vitamin_c"
|
||||||
| 'anti_aging';
|
| "anti_aging";
|
||||||
export type InteractionScope = 'same_step' | 'same_day' | 'same_period';
|
export type InteractionScope = "same_step" | "same_day" | "same_period";
|
||||||
export type MedicationKind = 'prescription' | 'otc' | 'supplement' | 'herbal' | 'other';
|
export type MedicationKind =
|
||||||
export type OverallSkinState = 'excellent' | 'good' | 'fair' | 'poor';
|
| "prescription"
|
||||||
export type PartOfDay = 'am' | 'pm';
|
| "otc"
|
||||||
export type PriceTier = 'budget' | 'mid' | 'premium' | 'luxury';
|
| "supplement"
|
||||||
|
| "herbal"
|
||||||
|
| "other";
|
||||||
|
export type OverallSkinState = "excellent" | "good" | "fair" | "poor";
|
||||||
|
export type PartOfDay = "am" | "pm";
|
||||||
|
export type PriceTier = "budget" | "mid" | "premium" | "luxury";
|
||||||
export type ProductCategory =
|
export type ProductCategory =
|
||||||
| 'cleanser'
|
| "cleanser"
|
||||||
| 'toner'
|
| "toner"
|
||||||
| 'essence'
|
| "essence"
|
||||||
| 'serum'
|
| "serum"
|
||||||
| 'moisturizer'
|
| "moisturizer"
|
||||||
| 'spf'
|
| "spf"
|
||||||
| 'mask'
|
| "mask"
|
||||||
| 'exfoliant'
|
| "exfoliant"
|
||||||
| 'hair_treatment'
|
| "hair_treatment"
|
||||||
| 'tool'
|
| "tool"
|
||||||
| 'spot_treatment'
|
| "spot_treatment"
|
||||||
| 'oil';
|
| "oil";
|
||||||
export type ResultFlag = 'N' | 'ABN' | 'POS' | 'NEG' | 'L' | 'H';
|
export type ResultFlag = "N" | "ABN" | "POS" | "NEG" | "L" | "H";
|
||||||
export type SkinConcern =
|
export type SkinConcern =
|
||||||
| 'acne'
|
| "acne"
|
||||||
| 'rosacea'
|
| "rosacea"
|
||||||
| 'hyperpigmentation'
|
| "hyperpigmentation"
|
||||||
| 'aging'
|
| "aging"
|
||||||
| 'dehydration'
|
| "dehydration"
|
||||||
| 'redness'
|
| "redness"
|
||||||
| 'damaged_barrier'
|
| "damaged_barrier"
|
||||||
| 'pore_visibility'
|
| "pore_visibility"
|
||||||
| 'uneven_texture'
|
| "uneven_texture"
|
||||||
| 'hair_growth'
|
| "hair_growth"
|
||||||
| 'sebum_excess';
|
| "sebum_excess";
|
||||||
export type SkinTexture = 'smooth' | 'rough' | 'flaky' | 'bumpy';
|
export type SkinTexture = "smooth" | "rough" | "flaky" | "bumpy";
|
||||||
export type SkinType = 'dry' | 'oily' | 'combination' | 'sensitive' | 'normal' | 'acne_prone';
|
export type SkinType =
|
||||||
|
| "dry"
|
||||||
|
| "oily"
|
||||||
|
| "combination"
|
||||||
|
| "sensitive"
|
||||||
|
| "normal"
|
||||||
|
| "acne_prone";
|
||||||
export type StrengthLevel = 1 | 2 | 3;
|
export type StrengthLevel = 1 | 2 | 3;
|
||||||
export type TextureType = 'watery' | 'gel' | 'emulsion' | 'cream' | 'oil' | 'balm' | 'foam' | 'fluid';
|
export type TextureType =
|
||||||
|
| "watery"
|
||||||
|
| "gel"
|
||||||
|
| "emulsion"
|
||||||
|
| "cream"
|
||||||
|
| "oil"
|
||||||
|
| "balm"
|
||||||
|
| "foam"
|
||||||
|
| "fluid";
|
||||||
// ─── Product types ───────────────────────────────────────────────────────────
|
// ─── Product types ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
export interface ActiveIngredient {
|
export interface ActiveIngredient {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue