chore(frontend): format files with prettier

This commit is contained in:
Piotr Oleszczyk 2026-03-03 20:51:34 +01:00
parent 0e7a39836f
commit 067e460dd2
20 changed files with 1615 additions and 1509 deletions

10
frontend/.mcp.json Normal file
View file

@ -0,0 +1,10 @@
{
"mcpServers": {
"svelte": {
"type": "stdio",
"command": "npx",
"env": {},
"args": ["-y", "@sveltejs/mcp"]
}
}
}

View file

@ -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 |

View file

@ -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));

View file

@ -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);

View file

@ -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 {