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

@ -24,8 +24,8 @@ The backend must be running at `http://localhost:8000`. See `../backend/` for se
## Environment variables
| Variable | Description | Default |
|---|---|---|
| Variable | Description | Default |
| ----------------- | ------------------------------- | ----------------------- |
| `PUBLIC_API_BASE` | Base URL of the FastAPI backend | `http://localhost:8000` |
Set `PUBLIC_API_BASE` at **build time** for production:
@ -51,24 +51,24 @@ Or use the provided systemd service: `../systemd/innercontext-node.service`.
## Routes
| Route | Description |
|---|---|
| `/` | Dashboard |
| `/products` | Product list |
| `/products/new` | Add product |
| `/products/[id]` | Product detail / edit |
| `/routines` | Routine list |
| `/routines/new` | Create routine |
| `/routines/[id]` | Routine detail |
| `/health/medications` | Medications |
| `/health/lab-results` | Lab results |
| `/skin` | Skin condition snapshots |
| Route | Description |
| --------------------- | ------------------------ |
| `/` | Dashboard |
| `/products` | Product list |
| `/products/new` | Add product |
| `/products/[id]` | Product detail / edit |
| `/routines` | Routine list |
| `/routines/new` | Create routine |
| `/routines/[id]` | Routine detail |
| `/health/medications` | Medications |
| `/health/lab-results` | Lab results |
| `/skin` | Skin condition snapshots |
## Key files
| File | Purpose |
|---|---|
| `src/lib/api.ts` | API client (typed fetch wrappers) |
| `src/lib/types.ts` | Shared TypeScript types |
| `src/app.css` | Tailwind v4 theme + global styles |
| `svelte.config.js` | SvelteKit config (adapter-node) |
| File | Purpose |
| ------------------ | --------------------------------- |
| `src/lib/api.ts` | API client (typed fetch wrappers) |
| `src/lib/types.ts` | Shared TypeScript types |
| `src/app.css` | Tailwind v4 theme + global styles |
| `svelte.config.js` | SvelteKit config (adapter-node) |

View file

@ -1,17 +1,17 @@
{
"$schema": "https://shadcn-svelte.com/schema.json",
"style": "default",
"tailwind": {
"config": "",
"css": "src/app.css",
"baseColor": "zinc"
},
"aliases": {
"components": "$lib/components",
"utils": "$lib/utils",
"ui": "$lib/components/ui",
"hooks": "$lib/hooks",
"lib": "$lib"
},
"registry": "https://shadcn-svelte.com/registry"
"$schema": "https://shadcn-svelte.com/schema.json",
"style": "default",
"tailwind": {
"config": "",
"css": "src/app.css",
"baseColor": "zinc"
},
"aliases": {
"components": "$lib/components",
"utils": "$lib/utils",
"ui": "$lib/components/ui",
"hooks": "$lib/hooks",
"lib": "$lib"
},
"registry": "https://shadcn-svelte.com/registry"
}

View file

@ -1,455 +1,455 @@
{
"nav_dashboard": "Dashboard",
"nav_products": "Products",
"nav_routines": "Routines",
"nav_grooming": "Grooming",
"nav_medications": "Medications",
"nav_labResults": "Lab Results",
"nav_skin": "Skin",
"nav_appName": "innercontext",
"nav_appSubtitle": "personal health & skincare",
"nav_dashboard": "Dashboard",
"nav_products": "Products",
"nav_routines": "Routines",
"nav_grooming": "Grooming",
"nav_medications": "Medications",
"nav_labResults": "Lab Results",
"nav_skin": "Skin",
"nav_appName": "innercontext",
"nav_appSubtitle": "personal health & skincare",
"common_save": "Save",
"common_cancel": "Cancel",
"common_add": "Add",
"common_edit": "Edit",
"common_delete": "Delete",
"common_saved": "Saved.",
"common_select": "Select",
"common_unknown": "Unknown",
"common_yes": "Yes",
"common_no": "No",
"common_unknown_value": "Unknown",
"common_optional_notes": "optional",
"common_steps": "steps",
"common_save": "Save",
"common_cancel": "Cancel",
"common_add": "Add",
"common_edit": "Edit",
"common_delete": "Delete",
"common_saved": "Saved.",
"common_select": "Select",
"common_unknown": "Unknown",
"common_yes": "Yes",
"common_no": "No",
"common_unknown_value": "Unknown",
"common_optional_notes": "optional",
"common_steps": "steps",
"dashboard_title": "Dashboard",
"dashboard_subtitle": "Your recent health & skincare overview",
"dashboard_latestSnapshot": "Latest Skin Snapshot",
"dashboard_recentRoutines": "Recent Routines",
"dashboard_noSnapshots": "No skin snapshots yet.",
"dashboard_noRoutines": "No routines in the past 2 weeks.",
"dashboard_title": "Dashboard",
"dashboard_subtitle": "Your recent health & skincare overview",
"dashboard_latestSnapshot": "Latest Skin Snapshot",
"dashboard_recentRoutines": "Recent Routines",
"dashboard_noSnapshots": "No skin snapshots yet.",
"dashboard_noRoutines": "No routines in the past 2 weeks.",
"products_title": "Products",
"products_count": "{count} products",
"products_addNew": "+ Add product",
"products_suggest": "Suggest",
"products_suggestTitle": "Shopping suggestions",
"products_suggestSubtitle": "What to buy?",
"products_suggestDescription": "Based on your skin condition and products you own, I'll suggest product types that could complement your routine.",
"products_suggestGenerating": "Analyzing...",
"products_suggestBtn": "Generate suggestions",
"products_suggestResults": "Suggestions",
"products_suggestTime": "Time",
"products_suggestFrequency": "Frequency",
"products_suggestRegenerate": "Regenerate",
"products_suggestNoResults": "No suggestions.",
"products_noProducts": "No products found.",
"products_filterAll": "All",
"products_filterOwned": "Owned",
"products_filterUnowned": "Not owned",
"products_colName": "Name",
"products_colBrand": "Brand",
"products_colTargets": "Targets",
"products_colTime": "Time",
"products_newTitle": "New Product",
"products_backToList": "← Products",
"products_createProduct": "Create product",
"products_saveChanges": "Save changes",
"products_deleteProduct": "Delete product",
"products_confirmDelete": "Delete this product?",
"products_noInventory": "No inventory packages.",
"products_title": "Products",
"products_count": "{count} products",
"products_addNew": "+ Add product",
"products_suggest": "Suggest",
"products_suggestTitle": "Shopping suggestions",
"products_suggestSubtitle": "What to buy?",
"products_suggestDescription": "Based on your skin condition and products you own, I'll suggest product types that could complement your routine.",
"products_suggestGenerating": "Analyzing...",
"products_suggestBtn": "Generate suggestions",
"products_suggestResults": "Suggestions",
"products_suggestTime": "Time",
"products_suggestFrequency": "Frequency",
"products_suggestRegenerate": "Regenerate",
"products_suggestNoResults": "No suggestions.",
"products_noProducts": "No products found.",
"products_filterAll": "All",
"products_filterOwned": "Owned",
"products_filterUnowned": "Not owned",
"products_colName": "Name",
"products_colBrand": "Brand",
"products_colTargets": "Targets",
"products_colTime": "Time",
"products_newTitle": "New Product",
"products_backToList": "← Products",
"products_createProduct": "Create product",
"products_saveChanges": "Save changes",
"products_deleteProduct": "Delete product",
"products_confirmDelete": "Delete this product?",
"products_noInventory": "No inventory packages.",
"inventory_title": "Inventory packages ({count})",
"inventory_addPackage": "+ Add package",
"inventory_packageAdded": "Package added.",
"inventory_packageUpdated": "Package updated.",
"inventory_packageDeleted": "Package deleted.",
"inventory_alreadyOpened": "Already opened",
"inventory_openedDate": "Opened date",
"inventory_finishedDate": "Finished date",
"inventory_expiryDate": "Expiry date",
"inventory_currentWeight": "Current weight (g)",
"inventory_lastWeighed": "Last weighed",
"inventory_notes": "Notes",
"inventory_badgeOpen": "Open",
"inventory_badgeSealed": "Sealed",
"inventory_badgeFinished": "Finished",
"inventory_exp": "Exp:",
"inventory_opened": "Opened:",
"inventory_finished": "Finished:",
"inventory_remaining": "g remaining",
"inventory_weighed": "Weighed:",
"inventory_confirmDelete": "Delete this package?",
"inventory_title": "Inventory packages ({count})",
"inventory_addPackage": "+ Add package",
"inventory_packageAdded": "Package added.",
"inventory_packageUpdated": "Package updated.",
"inventory_packageDeleted": "Package deleted.",
"inventory_alreadyOpened": "Already opened",
"inventory_openedDate": "Opened date",
"inventory_finishedDate": "Finished date",
"inventory_expiryDate": "Expiry date",
"inventory_currentWeight": "Current weight (g)",
"inventory_lastWeighed": "Last weighed",
"inventory_notes": "Notes",
"inventory_badgeOpen": "Open",
"inventory_badgeSealed": "Sealed",
"inventory_badgeFinished": "Finished",
"inventory_exp": "Exp:",
"inventory_opened": "Opened:",
"inventory_finished": "Finished:",
"inventory_remaining": "g remaining",
"inventory_weighed": "Weighed:",
"inventory_confirmDelete": "Delete this package?",
"routines_title": "Routines",
"routines_count": "{count} routines (last 30 days)",
"routines_suggestAI": "Suggest AI routine",
"routines_addNew": "+ New routine",
"routines_noRoutines": "No routines found.",
"routines_newTitle": "New Routine",
"routines_backToList": "← Routines",
"routines_detailsTitle": "Routine details",
"routines_date": "Date *",
"routines_amOrPm": "AM or PM *",
"routines_notes": "Notes",
"routines_notesPlaceholder": "Optional notes",
"routines_createRoutine": "Create routine",
"routines_deleteRoutine": "Delete routine",
"routines_confirmDelete": "Delete this routine?",
"routines_steps": "Steps ({count})",
"routines_addStep": "+ Add step",
"routines_addStepTitle": "Add step",
"routines_product": "Product",
"routines_selectProduct": "Select product",
"routines_dose": "Dose",
"routines_dosePlaceholder": "e.g. 2 pumps",
"routines_region": "Region",
"routines_regionPlaceholder": "e.g. face",
"routines_addStepBtn": "Add step",
"routines_unknownStep": "Unknown step",
"routines_noSteps": "No steps yet.",
"routines_title": "Routines",
"routines_count": "{count} routines (last 30 days)",
"routines_suggestAI": "Suggest AI routine",
"routines_addNew": "+ New routine",
"routines_noRoutines": "No routines found.",
"routines_newTitle": "New Routine",
"routines_backToList": "← Routines",
"routines_detailsTitle": "Routine details",
"routines_date": "Date *",
"routines_amOrPm": "AM or PM *",
"routines_notes": "Notes",
"routines_notesPlaceholder": "Optional notes",
"routines_createRoutine": "Create routine",
"routines_deleteRoutine": "Delete routine",
"routines_confirmDelete": "Delete this routine?",
"routines_steps": "Steps ({count})",
"routines_addStep": "+ Add step",
"routines_addStepTitle": "Add step",
"routines_product": "Product",
"routines_selectProduct": "Select product",
"routines_dose": "Dose",
"routines_dosePlaceholder": "e.g. 2 pumps",
"routines_region": "Region",
"routines_regionPlaceholder": "e.g. face",
"routines_addStepBtn": "Add step",
"routines_unknownStep": "Unknown step",
"routines_noSteps": "No steps yet.",
"grooming_title": "Grooming Schedule",
"grooming_backToRoutines": "← Routines",
"grooming_addEntry": "+ Add entry",
"grooming_entryAdded": "Entry added.",
"grooming_entryUpdated": "Entry updated.",
"grooming_entryDeleted": "Entry deleted.",
"grooming_dayOfWeek": "Day of week",
"grooming_action": "Action",
"grooming_notesOptional": "Notes (optional)",
"grooming_notesPlaceholder": "e.g. every 2 weeks",
"grooming_noEntries": "No entries yet. Click \"+ Add entry\" to get started.",
"grooming_confirmDelete": "Delete this entry?",
"grooming_actionShavingRazor": "Razor shaving",
"grooming_actionShavingOneblade": "OneBlade shaving",
"grooming_actionDermarolling": "Dermarolling",
"grooming_dayMonday": "Monday",
"grooming_dayTuesday": "Tuesday",
"grooming_dayWednesday": "Wednesday",
"grooming_dayThursday": "Thursday",
"grooming_dayFriday": "Friday",
"grooming_daySaturday": "Saturday",
"grooming_daySunday": "Sunday",
"grooming_title": "Grooming Schedule",
"grooming_backToRoutines": "← Routines",
"grooming_addEntry": "+ Add entry",
"grooming_entryAdded": "Entry added.",
"grooming_entryUpdated": "Entry updated.",
"grooming_entryDeleted": "Entry deleted.",
"grooming_dayOfWeek": "Day of week",
"grooming_action": "Action",
"grooming_notesOptional": "Notes (optional)",
"grooming_notesPlaceholder": "e.g. every 2 weeks",
"grooming_noEntries": "No entries yet. Click \"+ Add entry\" to get started.",
"grooming_confirmDelete": "Delete this entry?",
"grooming_actionShavingRazor": "Razor shaving",
"grooming_actionShavingOneblade": "OneBlade shaving",
"grooming_actionDermarolling": "Dermarolling",
"grooming_dayMonday": "Monday",
"grooming_dayTuesday": "Tuesday",
"grooming_dayWednesday": "Wednesday",
"grooming_dayThursday": "Thursday",
"grooming_dayFriday": "Friday",
"grooming_daySaturday": "Saturday",
"grooming_daySunday": "Sunday",
"suggest_title": "AI Routine Suggestion",
"suggest_backToRoutines": "← Routines",
"suggest_singleTab": "Single routine",
"suggest_batchTab": "Batch / Vacation",
"suggest_singleParams": "Parameters",
"suggest_date": "Date",
"suggest_timeOfDay": "Time of day",
"suggest_contextLabel": "Additional context for AI",
"suggest_contextOptional": "(optional)",
"suggest_contextPlaceholder": "e.g. party night, focusing on hydration...",
"suggest_leavingHomeLabel": "Going outside today",
"suggest_leavingHomeHint": "Affects SPF selection — checked: SPF50+, unchecked: SPF30.",
"suggest_minoxidilToggleLabel": "Prioritize beard/mustache density (minoxidil)",
"suggest_minoxidilToggleHint": "When enabled, AI will explicitly consider minoxidil for beard/mustache areas if available.",
"suggest_generateBtn": "Generate suggestion",
"suggest_generating": "Generating…",
"suggest_proposalTitle": "Suggestion",
"suggest_saveRoutine": "Save routine",
"suggest_saving": "Saving…",
"suggest_regenerate": "Regenerate",
"suggest_batchRange": "Date range",
"suggest_fromDate": "From",
"suggest_toDate": "To (max 14 days)",
"suggest_batchContextLabel": "Context / trip purpose",
"suggest_batchContextPlaceholder": "e.g. sunny trip to Italy, active mountain vacation...",
"suggest_generatePlan": "Generate plan",
"suggest_generatingPlan": "Generating plan…",
"suggest_planTitle": "Plan ({count} days)",
"suggest_saveAllRoutines": "Save all routines",
"suggest_amSteps": "steps",
"suggest_pmSteps": "steps",
"suggest_noAmSteps": "No AM steps.",
"suggest_noPmSteps": "No PM steps.",
"suggest_errorDefault": "Error generating suggestion.",
"suggest_errorBatch": "Error generating plan.",
"suggest_errorSave": "Error saving.",
"suggest_amMorning": "AM (morning)",
"suggest_pmEvening": "PM (evening)",
"suggest_title": "AI Routine Suggestion",
"suggest_backToRoutines": "← Routines",
"suggest_singleTab": "Single routine",
"suggest_batchTab": "Batch / Vacation",
"suggest_singleParams": "Parameters",
"suggest_date": "Date",
"suggest_timeOfDay": "Time of day",
"suggest_contextLabel": "Additional context for AI",
"suggest_contextOptional": "(optional)",
"suggest_contextPlaceholder": "e.g. party night, focusing on hydration...",
"suggest_leavingHomeLabel": "Going outside today",
"suggest_leavingHomeHint": "Affects SPF selection — checked: SPF50+, unchecked: SPF30.",
"suggest_minoxidilToggleLabel": "Prioritize beard/mustache density (minoxidil)",
"suggest_minoxidilToggleHint": "When enabled, AI will explicitly consider minoxidil for beard/mustache areas if available.",
"suggest_generateBtn": "Generate suggestion",
"suggest_generating": "Generating…",
"suggest_proposalTitle": "Suggestion",
"suggest_saveRoutine": "Save routine",
"suggest_saving": "Saving…",
"suggest_regenerate": "Regenerate",
"suggest_batchRange": "Date range",
"suggest_fromDate": "From",
"suggest_toDate": "To (max 14 days)",
"suggest_batchContextLabel": "Context / trip purpose",
"suggest_batchContextPlaceholder": "e.g. sunny trip to Italy, active mountain vacation...",
"suggest_generatePlan": "Generate plan",
"suggest_generatingPlan": "Generating plan…",
"suggest_planTitle": "Plan ({count} days)",
"suggest_saveAllRoutines": "Save all routines",
"suggest_amSteps": "steps",
"suggest_pmSteps": "steps",
"suggest_noAmSteps": "No AM steps.",
"suggest_noPmSteps": "No PM steps.",
"suggest_errorDefault": "Error generating suggestion.",
"suggest_errorBatch": "Error generating plan.",
"suggest_errorSave": "Error saving.",
"suggest_amMorning": "AM (morning)",
"suggest_pmEvening": "PM (evening)",
"medications_title": "Medications",
"medications_count": "{count} entries",
"medications_addNew": "+ Add medication",
"medications_newTitle": "New medication",
"medications_kind": "Kind",
"medications_productName": "Product name *",
"medications_productNamePlaceholder": "e.g. Vitamin D3",
"medications_activeSubstance": "Active substance",
"medications_activeSubstancePlaceholder": "e.g. cholecalciferol",
"medications_notes": "Notes",
"medications_added": "Medication added.",
"medications_usages": "{count} usages",
"medications_noMedications": "No medications recorded.",
"medications_kindPrescription": "Prescription",
"medications_kindOtc": "OTC",
"medications_kindSupplement": "Supplement",
"medications_kindHerbal": "Herbal",
"medications_kindOther": "Other",
"medications_title": "Medications",
"medications_count": "{count} entries",
"medications_addNew": "+ Add medication",
"medications_newTitle": "New medication",
"medications_kind": "Kind",
"medications_productName": "Product name *",
"medications_productNamePlaceholder": "e.g. Vitamin D3",
"medications_activeSubstance": "Active substance",
"medications_activeSubstancePlaceholder": "e.g. cholecalciferol",
"medications_notes": "Notes",
"medications_added": "Medication added.",
"medications_usages": "{count} usages",
"medications_noMedications": "No medications recorded.",
"medications_kindPrescription": "Prescription",
"medications_kindOtc": "OTC",
"medications_kindSupplement": "Supplement",
"medications_kindHerbal": "Herbal",
"medications_kindOther": "Other",
"labResults_title": "Lab Results",
"labResults_count": "{count} results",
"labResults_addNew": "+ Add result",
"labResults_newTitle": "New lab result",
"labResults_flagFilter": "Flag:",
"labResults_flagAll": "All",
"labResults_flagNone": "None",
"labResults_date": "Date *",
"labResults_loincCode": "LOINC code *",
"labResults_testName": "Test name",
"labResults_testNamePlaceholder": "e.g. Hemoglobin",
"labResults_lab": "Lab",
"labResults_labPlaceholder": "e.g. LabCorp",
"labResults_value": "Value",
"labResults_unit": "Unit",
"labResults_unitPlaceholder": "e.g. g/dL",
"labResults_flag": "Flag",
"labResults_added": "Result added.",
"labResults_colDate": "Date",
"labResults_colTest": "Test",
"labResults_colLoinc": "LOINC",
"labResults_colValue": "Value",
"labResults_colFlag": "Flag",
"labResults_colLab": "Lab",
"labResults_noResults": "No lab results found.",
"labResults_title": "Lab Results",
"labResults_count": "{count} results",
"labResults_addNew": "+ Add result",
"labResults_newTitle": "New lab result",
"labResults_flagFilter": "Flag:",
"labResults_flagAll": "All",
"labResults_flagNone": "None",
"labResults_date": "Date *",
"labResults_loincCode": "LOINC code *",
"labResults_testName": "Test name",
"labResults_testNamePlaceholder": "e.g. Hemoglobin",
"labResults_lab": "Lab",
"labResults_labPlaceholder": "e.g. LabCorp",
"labResults_value": "Value",
"labResults_unit": "Unit",
"labResults_unitPlaceholder": "e.g. g/dL",
"labResults_flag": "Flag",
"labResults_added": "Result added.",
"labResults_colDate": "Date",
"labResults_colTest": "Test",
"labResults_colLoinc": "LOINC",
"labResults_colValue": "Value",
"labResults_colFlag": "Flag",
"labResults_colLab": "Lab",
"labResults_noResults": "No lab results found.",
"skin_title": "Skin Snapshots",
"skin_count": "{count} snapshots",
"skin_addNew": "+ Add snapshot",
"skin_aiAnalysisTitle": "AI analysis from photos",
"skin_aiUploadText": "Upload 13 photos of your skin. AI will pre-fill the form fields below.",
"skin_analyzePhotos": "Analyze photos",
"skin_analyzing": "Analyzing…",
"skin_newSnapshotTitle": "New skin snapshot",
"skin_date": "Date *",
"skin_overallState": "Overall state",
"skin_texture": "Texture",
"skin_skinType": "Skin type",
"skin_barrierState": "Barrier state",
"skin_hydration": "Hydration (15)",
"skin_sensitivity": "Sensitivity (15)",
"skin_sebumTzone": "Sebum T-zone (15)",
"skin_sebumCheeks": "Sebum cheeks (15)",
"skin_activeConcerns": "Active concerns (comma-separated)",
"skin_activeConcernsPlaceholder": "acne, redness, dehydration",
"skin_priorities": "Priorities (comma-separated)",
"skin_prioritiesPlaceholder": "strengthen barrier, reduce redness",
"skin_prioritiesLabel": "Priorities",
"skin_notes": "Notes",
"skin_addSnapshot": "Add snapshot",
"skin_snapshotAdded": "Snapshot added.",
"skin_snapshotUpdated": "Snapshot updated.",
"skin_snapshotDeleted": "Snapshot deleted.",
"skin_noSnapshots": "No skin snapshots yet.",
"skin_hydrationLabel": "Hydration",
"skin_sensitivityLabel": "Sensitivity",
"skin_barrierLabel": "Barrier",
"skin_stateExcellent": "excellent",
"skin_stateGood": "good",
"skin_stateFair": "fair",
"skin_statePoor": "poor",
"skin_textureSmooth": "smooth",
"skin_textureRough": "rough",
"skin_textureFlaky": "flaky",
"skin_textureBumpy": "bumpy",
"skin_barrierIntact": "intact",
"skin_barrierMildly": "mildly compromised",
"skin_barrierCompromised": "compromised",
"skin_typeDry": "dry",
"skin_typeOily": "oily",
"skin_typeCombination": "combination",
"skin_typeSensitive": "sensitive",
"skin_typeNormal": "normal",
"skin_typeAcneProne": "acne prone",
"skin_title": "Skin Snapshots",
"skin_count": "{count} snapshots",
"skin_addNew": "+ Add snapshot",
"skin_aiAnalysisTitle": "AI analysis from photos",
"skin_aiUploadText": "Upload 13 photos of your skin. AI will pre-fill the form fields below.",
"skin_analyzePhotos": "Analyze photos",
"skin_analyzing": "Analyzing…",
"skin_newSnapshotTitle": "New skin snapshot",
"skin_date": "Date *",
"skin_overallState": "Overall state",
"skin_texture": "Texture",
"skin_skinType": "Skin type",
"skin_barrierState": "Barrier state",
"skin_hydration": "Hydration (15)",
"skin_sensitivity": "Sensitivity (15)",
"skin_sebumTzone": "Sebum T-zone (15)",
"skin_sebumCheeks": "Sebum cheeks (15)",
"skin_activeConcerns": "Active concerns (comma-separated)",
"skin_activeConcernsPlaceholder": "acne, redness, dehydration",
"skin_priorities": "Priorities (comma-separated)",
"skin_prioritiesPlaceholder": "strengthen barrier, reduce redness",
"skin_prioritiesLabel": "Priorities",
"skin_notes": "Notes",
"skin_addSnapshot": "Add snapshot",
"skin_snapshotAdded": "Snapshot added.",
"skin_snapshotUpdated": "Snapshot updated.",
"skin_snapshotDeleted": "Snapshot deleted.",
"skin_noSnapshots": "No skin snapshots yet.",
"skin_hydrationLabel": "Hydration",
"skin_sensitivityLabel": "Sensitivity",
"skin_barrierLabel": "Barrier",
"skin_stateExcellent": "excellent",
"skin_stateGood": "good",
"skin_stateFair": "fair",
"skin_statePoor": "poor",
"skin_textureSmooth": "smooth",
"skin_textureRough": "rough",
"skin_textureFlaky": "flaky",
"skin_textureBumpy": "bumpy",
"skin_barrierIntact": "intact",
"skin_barrierMildly": "mildly compromised",
"skin_barrierCompromised": "compromised",
"skin_typeDry": "dry",
"skin_typeOily": "oily",
"skin_typeCombination": "combination",
"skin_typeSensitive": "sensitive",
"skin_typeNormal": "normal",
"skin_typeAcneProne": "acne prone",
"productForm_aiPrefill": "AI pre-fill",
"productForm_aiPrefillText": "Paste product description from a website, ingredient list, or other text. AI will fill in available fields — you can review and correct before saving.",
"productForm_pasteText": "Paste product description, INCI ingredients here...",
"productForm_parseWithAI": "Fill fields (AI)",
"productForm_parsing": "Processing…",
"productForm_basicInfo": "Basic info",
"productForm_name": "Name *",
"productForm_namePlaceholder": "e.g. Hydro Boost Water Gel",
"productForm_brand": "Brand *",
"productForm_brandPlaceholder": "e.g. Neutrogena",
"productForm_lineName": "Line / series",
"productForm_lineNamePlaceholder": "e.g. Hydro Boost",
"productForm_url": "URL",
"productForm_sku": "SKU",
"productForm_skuPlaceholder": "e.g. NTR-HB-50",
"productForm_barcode": "Barcode / EAN",
"productForm_barcodePlaceholder": "e.g. 3614273258975",
"productForm_classification": "Classification",
"productForm_category": "Category *",
"productForm_selectCategory": "Select category",
"productForm_time": "Time *",
"productForm_timeOptions": "AM / PM / Both",
"productForm_timeBoth": "Both",
"productForm_leaveOn": "Leave-on *",
"productForm_leaveOnYes": "Yes (leave-on)",
"productForm_leaveOnNo": "No (rinse-off)",
"productForm_texture": "Texture",
"productForm_selectTexture": "Select texture",
"productForm_absorptionSpeed": "Absorption speed",
"productForm_selectSpeed": "Select speed",
"productForm_skinProfile": "Skin profile",
"productForm_recommendedFor": "Recommended for skin types",
"productForm_targetConcerns": "Target concerns",
"productForm_contraindications": "Contraindications (one per line)",
"productForm_contraindicationsPlaceholder": "e.g. active rosacea flares",
"productForm_ingredients": "Ingredients",
"productForm_inciList": "INCI list (one ingredient per line)",
"productForm_activeIngredients": "Active ingredients",
"productForm_addActive": "+ Add active",
"productForm_noActives": "No actives added yet.",
"productForm_activeName": "Name",
"productForm_activePercent": "%",
"productForm_activeStrength": "Strength",
"productForm_activeIrritation": "Irritation",
"productForm_activeFunctions": "Functions",
"productForm_effectProfile": "Effect profile (05)",
"productForm_interactions": "Interactions",
"productForm_synergizesWith": "Synergizes with (one per line)",
"productForm_incompatibleWith": "Incompatible with",
"productForm_addIncompatibility": "+ Add incompatibility",
"productForm_noIncompatibilities": "No incompatibilities added.",
"productForm_incompTarget": "Target ingredient",
"productForm_incompScope": "Scope",
"productForm_incompReason": "Reason (optional)",
"productForm_incompReasonPlaceholder": "e.g. reduces efficacy",
"productForm_incompScopeSelect": "Select…",
"productForm_contextRules": "Context rules",
"productForm_ctxAfterShaving": "Safe after shaving",
"productForm_ctxAfterAcids": "Safe after acids",
"productForm_ctxAfterRetinoids": "Safe after retinoids",
"productForm_ctxCompromisedBarrier": "Safe with compromised barrier",
"productForm_ctxLowUvOnly": "Low UV only (evening/covered)",
"productForm_productDetails": "Product details",
"productForm_priceTier": "Price tier",
"productForm_selectTier": "Select tier",
"productForm_sizeMl": "Size (ml)",
"productForm_fullWeightG": "Full weight (g)",
"productForm_emptyWeightG": "Empty weight (g)",
"productForm_paoMonths": "PAO (months)",
"productForm_phMin": "pH min",
"productForm_phMax": "pH max",
"productForm_usageNotes": "Usage notes",
"productForm_usageNotesPlaceholder": "e.g. Apply to damp skin, avoid eye area",
"productForm_safetyFlags": "Safety flags",
"productForm_fragranceFree": "Fragrance-free",
"productForm_essentialOilsFree": "Essential oils-free",
"productForm_alcoholDenatFree": "Alcohol denat-free",
"productForm_pregnancySafe": "Pregnancy safe",
"productForm_usageConstraints": "Usage constraints",
"productForm_minIntervalHours": "Min interval (hours)",
"productForm_maxFrequencyPerWeek": "Max uses per week",
"productForm_isMedication": "Is medication",
"productForm_isTool": "Is tool (e.g. dermaroller)",
"productForm_needleLengthMm": "Needle length (mm, tools only)",
"productForm_personalNotes": "Personal notes",
"productForm_repurchaseIntent": "Repurchase intent",
"productForm_toleranceNotes": "Tolerance notes",
"productForm_toleranceNotesPlaceholder": "e.g. Causes mild stinging, fine after 2 weeks",
"productForm_aiPrefill": "AI pre-fill",
"productForm_aiPrefillText": "Paste product description from a website, ingredient list, or other text. AI will fill in available fields — you can review and correct before saving.",
"productForm_pasteText": "Paste product description, INCI ingredients here...",
"productForm_parseWithAI": "Fill fields (AI)",
"productForm_parsing": "Processing…",
"productForm_basicInfo": "Basic info",
"productForm_name": "Name *",
"productForm_namePlaceholder": "e.g. Hydro Boost Water Gel",
"productForm_brand": "Brand *",
"productForm_brandPlaceholder": "e.g. Neutrogena",
"productForm_lineName": "Line / series",
"productForm_lineNamePlaceholder": "e.g. Hydro Boost",
"productForm_url": "URL",
"productForm_sku": "SKU",
"productForm_skuPlaceholder": "e.g. NTR-HB-50",
"productForm_barcode": "Barcode / EAN",
"productForm_barcodePlaceholder": "e.g. 3614273258975",
"productForm_classification": "Classification",
"productForm_category": "Category *",
"productForm_selectCategory": "Select category",
"productForm_time": "Time *",
"productForm_timeOptions": "AM / PM / Both",
"productForm_timeBoth": "Both",
"productForm_leaveOn": "Leave-on *",
"productForm_leaveOnYes": "Yes (leave-on)",
"productForm_leaveOnNo": "No (rinse-off)",
"productForm_texture": "Texture",
"productForm_selectTexture": "Select texture",
"productForm_absorptionSpeed": "Absorption speed",
"productForm_selectSpeed": "Select speed",
"productForm_skinProfile": "Skin profile",
"productForm_recommendedFor": "Recommended for skin types",
"productForm_targetConcerns": "Target concerns",
"productForm_contraindications": "Contraindications (one per line)",
"productForm_contraindicationsPlaceholder": "e.g. active rosacea flares",
"productForm_ingredients": "Ingredients",
"productForm_inciList": "INCI list (one ingredient per line)",
"productForm_activeIngredients": "Active ingredients",
"productForm_addActive": "+ Add active",
"productForm_noActives": "No actives added yet.",
"productForm_activeName": "Name",
"productForm_activePercent": "%",
"productForm_activeStrength": "Strength",
"productForm_activeIrritation": "Irritation",
"productForm_activeFunctions": "Functions",
"productForm_effectProfile": "Effect profile (05)",
"productForm_interactions": "Interactions",
"productForm_synergizesWith": "Synergizes with (one per line)",
"productForm_incompatibleWith": "Incompatible with",
"productForm_addIncompatibility": "+ Add incompatibility",
"productForm_noIncompatibilities": "No incompatibilities added.",
"productForm_incompTarget": "Target ingredient",
"productForm_incompScope": "Scope",
"productForm_incompReason": "Reason (optional)",
"productForm_incompReasonPlaceholder": "e.g. reduces efficacy",
"productForm_incompScopeSelect": "Select…",
"productForm_contextRules": "Context rules",
"productForm_ctxAfterShaving": "Safe after shaving",
"productForm_ctxAfterAcids": "Safe after acids",
"productForm_ctxAfterRetinoids": "Safe after retinoids",
"productForm_ctxCompromisedBarrier": "Safe with compromised barrier",
"productForm_ctxLowUvOnly": "Low UV only (evening/covered)",
"productForm_productDetails": "Product details",
"productForm_priceTier": "Price tier",
"productForm_selectTier": "Select tier",
"productForm_sizeMl": "Size (ml)",
"productForm_fullWeightG": "Full weight (g)",
"productForm_emptyWeightG": "Empty weight (g)",
"productForm_paoMonths": "PAO (months)",
"productForm_phMin": "pH min",
"productForm_phMax": "pH max",
"productForm_usageNotes": "Usage notes",
"productForm_usageNotesPlaceholder": "e.g. Apply to damp skin, avoid eye area",
"productForm_safetyFlags": "Safety flags",
"productForm_fragranceFree": "Fragrance-free",
"productForm_essentialOilsFree": "Essential oils-free",
"productForm_alcoholDenatFree": "Alcohol denat-free",
"productForm_pregnancySafe": "Pregnancy safe",
"productForm_usageConstraints": "Usage constraints",
"productForm_minIntervalHours": "Min interval (hours)",
"productForm_maxFrequencyPerWeek": "Max uses per week",
"productForm_isMedication": "Is medication",
"productForm_isTool": "Is tool (e.g. dermaroller)",
"productForm_needleLengthMm": "Needle length (mm, tools only)",
"productForm_personalNotes": "Personal notes",
"productForm_repurchaseIntent": "Repurchase intent",
"productForm_toleranceNotes": "Tolerance notes",
"productForm_toleranceNotesPlaceholder": "e.g. Causes mild stinging, fine after 2 weeks",
"productForm_categoryCleanser": "Cleanser",
"productForm_categoryToner": "Toner",
"productForm_categoryEssence": "Essence",
"productForm_categorySerum": "Serum",
"productForm_categoryMoisturizer": "Moisturizer",
"productForm_categorySpf": "SPF",
"productForm_categoryMask": "Mask",
"productForm_categoryExfoliant": "Exfoliant",
"productForm_categoryHairTreatment": "Hair treatment",
"productForm_categoryTool": "Tool",
"productForm_categorySpotTreatment": "Spot treatment",
"productForm_categoryOil": "Oil",
"productForm_categoryCleanser": "Cleanser",
"productForm_categoryToner": "Toner",
"productForm_categoryEssence": "Essence",
"productForm_categorySerum": "Serum",
"productForm_categoryMoisturizer": "Moisturizer",
"productForm_categorySpf": "SPF",
"productForm_categoryMask": "Mask",
"productForm_categoryExfoliant": "Exfoliant",
"productForm_categoryHairTreatment": "Hair treatment",
"productForm_categoryTool": "Tool",
"productForm_categorySpotTreatment": "Spot treatment",
"productForm_categoryOil": "Oil",
"productForm_textureWatery": "Watery",
"productForm_textureGel": "Gel",
"productForm_textureEmulsion": "Emulsion",
"productForm_textureCream": "Cream",
"productForm_textureOil": "Oil",
"productForm_textureBalm": "Balm",
"productForm_textureFoam": "Foam",
"productForm_textureFluid": "Fluid",
"productForm_textureWatery": "Watery",
"productForm_textureGel": "Gel",
"productForm_textureEmulsion": "Emulsion",
"productForm_textureCream": "Cream",
"productForm_textureOil": "Oil",
"productForm_textureBalm": "Balm",
"productForm_textureFoam": "Foam",
"productForm_textureFluid": "Fluid",
"productForm_absorptionVeryFast": "Very fast",
"productForm_absorptionFast": "Fast",
"productForm_absorptionModerate": "Moderate",
"productForm_absorptionSlow": "Slow",
"productForm_absorptionVerySlow": "Very slow",
"productForm_absorptionVeryFast": "Very fast",
"productForm_absorptionFast": "Fast",
"productForm_absorptionModerate": "Moderate",
"productForm_absorptionSlow": "Slow",
"productForm_absorptionVerySlow": "Very slow",
"productForm_priceBudget": "Budget",
"productForm_priceMid": "Mid",
"productForm_pricePremium": "Premium",
"productForm_priceLuxury": "Luxury",
"productForm_priceBudget": "Budget",
"productForm_priceMid": "Mid",
"productForm_pricePremium": "Premium",
"productForm_priceLuxury": "Luxury",
"productForm_skinTypeDry": "dry",
"productForm_skinTypeOily": "oily",
"productForm_skinTypeCombination": "combination",
"productForm_skinTypeSensitive": "sensitive",
"productForm_skinTypeNormal": "normal",
"productForm_skinTypeAcneProne": "acne prone",
"productForm_skinTypeDry": "dry",
"productForm_skinTypeOily": "oily",
"productForm_skinTypeCombination": "combination",
"productForm_skinTypeSensitive": "sensitive",
"productForm_skinTypeNormal": "normal",
"productForm_skinTypeAcneProne": "acne prone",
"productForm_concernAcne": "acne",
"productForm_concernRosacea": "rosacea",
"productForm_concernHyperpigmentation": "hyperpigmentation",
"productForm_concernAging": "aging",
"productForm_concernDehydration": "dehydration",
"productForm_concernRedness": "redness",
"productForm_concernDamagedBarrier": "damaged barrier",
"productForm_concernPoreVisibility": "pore visibility",
"productForm_concernUnevenTexture": "uneven texture",
"productForm_concernHairGrowth": "hair growth",
"productForm_concernSebumExcess": "sebum excess",
"productForm_concernAcne": "acne",
"productForm_concernRosacea": "rosacea",
"productForm_concernHyperpigmentation": "hyperpigmentation",
"productForm_concernAging": "aging",
"productForm_concernDehydration": "dehydration",
"productForm_concernRedness": "redness",
"productForm_concernDamagedBarrier": "damaged barrier",
"productForm_concernPoreVisibility": "pore visibility",
"productForm_concernUnevenTexture": "uneven texture",
"productForm_concernHairGrowth": "hair growth",
"productForm_concernSebumExcess": "sebum excess",
"productForm_fnHumectant": "humectant",
"productForm_fnEmollient": "emollient",
"productForm_fnOcclusive": "occlusive",
"productForm_fnExfoliantAha": "AHA exfoliant",
"productForm_fnExfoliantBha": "BHA exfoliant",
"productForm_fnExfoliantPha": "PHA exfoliant",
"productForm_fnRetinoid": "retinoid",
"productForm_fnAntioxidant": "antioxidant",
"productForm_fnSoothing": "soothing",
"productForm_fnBarrierSupport": "barrier support",
"productForm_fnBrightening": "brightening",
"productForm_fnAntiAcne": "anti-acne",
"productForm_fnCeramide": "ceramide",
"productForm_fnNiacinamide": "niacinamide",
"productForm_fnSunscreen": "sunscreen",
"productForm_fnPeptide": "peptide",
"productForm_fnHairGrowth": "hair growth stimulant",
"productForm_fnPrebiotic": "prebiotic",
"productForm_fnVitaminC": "vitamin C",
"productForm_fnAntiAging": "anti-aging",
"productForm_fnHumectant": "humectant",
"productForm_fnEmollient": "emollient",
"productForm_fnOcclusive": "occlusive",
"productForm_fnExfoliantAha": "AHA exfoliant",
"productForm_fnExfoliantBha": "BHA exfoliant",
"productForm_fnExfoliantPha": "PHA exfoliant",
"productForm_fnRetinoid": "retinoid",
"productForm_fnAntioxidant": "antioxidant",
"productForm_fnSoothing": "soothing",
"productForm_fnBarrierSupport": "barrier support",
"productForm_fnBrightening": "brightening",
"productForm_fnAntiAcne": "anti-acne",
"productForm_fnCeramide": "ceramide",
"productForm_fnNiacinamide": "niacinamide",
"productForm_fnSunscreen": "sunscreen",
"productForm_fnPeptide": "peptide",
"productForm_fnHairGrowth": "hair growth stimulant",
"productForm_fnPrebiotic": "prebiotic",
"productForm_fnVitaminC": "vitamin C",
"productForm_fnAntiAging": "anti-aging",
"productForm_scopeSameStep": "same step",
"productForm_scopeSameDay": "same day",
"productForm_scopeSamePeriod": "same period",
"productForm_scopeSameStep": "same step",
"productForm_scopeSameDay": "same day",
"productForm_scopeSamePeriod": "same period",
"productForm_strengthLow": "1 Low",
"productForm_strengthMedium": "2 Medium",
"productForm_strengthHigh": "3 High",
"productForm_strengthLow": "1 Low",
"productForm_strengthMedium": "2 Medium",
"productForm_strengthHigh": "3 High",
"productForm_effectHydrationImmediate": "Hydration (immediate)",
"productForm_effectHydrationLongTerm": "Hydration (long term)",
"productForm_effectBarrierRepair": "Barrier repair",
"productForm_effectSoothing": "Soothing",
"productForm_effectExfoliation": "Exfoliation",
"productForm_effectRetinoid": "Retinoid activity",
"productForm_effectIrritation": "Irritation risk",
"productForm_effectComedogenic": "Comedogenic risk",
"productForm_effectBarrierDisruption": "Barrier disruption risk",
"productForm_effectDryness": "Dryness risk",
"productForm_effectBrightening": "Brightening",
"productForm_effectAntiAcne": "Anti-acne",
"productForm_effectAntiAging": "Anti-aging",
"productForm_effectHydrationImmediate": "Hydration (immediate)",
"productForm_effectHydrationLongTerm": "Hydration (long term)",
"productForm_effectBarrierRepair": "Barrier repair",
"productForm_effectSoothing": "Soothing",
"productForm_effectExfoliation": "Exfoliation",
"productForm_effectRetinoid": "Retinoid activity",
"productForm_effectIrritation": "Irritation risk",
"productForm_effectComedogenic": "Comedogenic risk",
"productForm_effectBarrierDisruption": "Barrier disruption risk",
"productForm_effectDryness": "Dryness risk",
"productForm_effectBrightening": "Brightening",
"productForm_effectAntiAcne": "Anti-acne",
"productForm_effectAntiAging": "Anti-aging",
"lang_pl": "PL",
"lang_en": "EN"
"lang_pl": "PL",
"lang_en": "EN"
}

View file

@ -1,455 +1,455 @@
{
"nav_dashboard": "Dashboard",
"nav_products": "Produkty",
"nav_routines": "Rutyny",
"nav_grooming": "Pielęgnacja",
"nav_medications": "Leki",
"nav_labResults": "Wyniki badań",
"nav_skin": "Skóra",
"nav_appName": "innercontext",
"nav_appSubtitle": "zdrowie & pielęgnacja",
"nav_dashboard": "Dashboard",
"nav_products": "Produkty",
"nav_routines": "Rutyny",
"nav_grooming": "Pielęgnacja",
"nav_medications": "Leki",
"nav_labResults": "Wyniki badań",
"nav_skin": "Skóra",
"nav_appName": "innercontext",
"nav_appSubtitle": "zdrowie & pielęgnacja",
"common_save": "Zapisz",
"common_cancel": "Anuluj",
"common_add": "Dodaj",
"common_edit": "Edytuj",
"common_delete": "Usuń",
"common_saved": "Zapisano.",
"common_select": "Wybierz",
"common_unknown": "Nieznane",
"common_yes": "Tak",
"common_no": "Nie",
"common_unknown_value": "Nieznane",
"common_optional_notes": "opcjonalnie",
"common_steps": "kroków",
"common_save": "Zapisz",
"common_cancel": "Anuluj",
"common_add": "Dodaj",
"common_edit": "Edytuj",
"common_delete": "Usuń",
"common_saved": "Zapisano.",
"common_select": "Wybierz",
"common_unknown": "Nieznane",
"common_yes": "Tak",
"common_no": "Nie",
"common_unknown_value": "Nieznane",
"common_optional_notes": "opcjonalnie",
"common_steps": "kroków",
"dashboard_title": "Dashboard",
"dashboard_subtitle": "Przegląd zdrowia i pielęgnacji",
"dashboard_latestSnapshot": "Ostatni stan skóry",
"dashboard_recentRoutines": "Ostatnie rutyny",
"dashboard_noSnapshots": "Brak wpisów o stanie skóry.",
"dashboard_noRoutines": "Brak rutyno w ciągu ostatnich 2 tygodni.",
"dashboard_title": "Dashboard",
"dashboard_subtitle": "Przegląd zdrowia i pielęgnacji",
"dashboard_latestSnapshot": "Ostatni stan skóry",
"dashboard_recentRoutines": "Ostatnie rutyny",
"dashboard_noSnapshots": "Brak wpisów o stanie skóry.",
"dashboard_noRoutines": "Brak rutyno w ciągu ostatnich 2 tygodni.",
"products_title": "Produkty",
"products_count": "{count} produktów",
"products_addNew": "+ Dodaj produkt",
"products_suggest": "Sugeruj",
"products_suggestTitle": "Sugestie zakupowe",
"products_suggestSubtitle": "Co warto kupić?",
"products_suggestDescription": "Na podstawie Twojego stanu skóry i posiadanych produktów zasugeruję typy produktów, które mogłyby uzupełnić Twoją rutynę.",
"products_suggestGenerating": "Analizuję...",
"products_suggestBtn": "Generuj sugestie",
"products_suggestResults": "Propozycje",
"products_suggestTime": "Pora",
"products_suggestFrequency": "Częstotliwość",
"products_suggestRegenerate": "Wygeneruj ponownie",
"products_suggestNoResults": "Brak propozycji.",
"products_noProducts": "Nie znaleziono produktów.",
"products_filterAll": "Wszystkie",
"products_filterOwned": "Posiadane",
"products_filterUnowned": "Nieposiadane",
"products_colName": "Nazwa",
"products_colBrand": "Marka",
"products_colTargets": "Cele",
"products_colTime": "Pora",
"products_newTitle": "Nowy produkt",
"products_backToList": "← Produkty",
"products_createProduct": "Utwórz produkt",
"products_saveChanges": "Zapisz zmiany",
"products_deleteProduct": "Usuń produkt",
"products_confirmDelete": "Usunąć ten produkt?",
"products_noInventory": "Brak opakowań w magazynie.",
"products_title": "Produkty",
"products_count": "{count} produktów",
"products_addNew": "+ Dodaj produkt",
"products_suggest": "Sugeruj",
"products_suggestTitle": "Sugestie zakupowe",
"products_suggestSubtitle": "Co warto kupić?",
"products_suggestDescription": "Na podstawie Twojego stanu skóry i posiadanych produktów zasugeruję typy produktów, które mogłyby uzupełnić Twoją rutynę.",
"products_suggestGenerating": "Analizuję...",
"products_suggestBtn": "Generuj sugestie",
"products_suggestResults": "Propozycje",
"products_suggestTime": "Pora",
"products_suggestFrequency": "Częstotliwość",
"products_suggestRegenerate": "Wygeneruj ponownie",
"products_suggestNoResults": "Brak propozycji.",
"products_noProducts": "Nie znaleziono produktów.",
"products_filterAll": "Wszystkie",
"products_filterOwned": "Posiadane",
"products_filterUnowned": "Nieposiadane",
"products_colName": "Nazwa",
"products_colBrand": "Marka",
"products_colTargets": "Cele",
"products_colTime": "Pora",
"products_newTitle": "Nowy produkt",
"products_backToList": "← Produkty",
"products_createProduct": "Utwórz produkt",
"products_saveChanges": "Zapisz zmiany",
"products_deleteProduct": "Usuń produkt",
"products_confirmDelete": "Usunąć ten produkt?",
"products_noInventory": "Brak opakowań w magazynie.",
"inventory_title": "Opakowania ({count})",
"inventory_addPackage": "+ Dodaj opakowanie",
"inventory_packageAdded": "Opakowanie dodane.",
"inventory_packageUpdated": "Opakowanie zaktualizowane.",
"inventory_packageDeleted": "Opakowanie usunięte.",
"inventory_alreadyOpened": "Już otwarte",
"inventory_openedDate": "Data otwarcia",
"inventory_finishedDate": "Data skończenia",
"inventory_expiryDate": "Data ważności",
"inventory_currentWeight": "Aktualna waga (g)",
"inventory_lastWeighed": "Ostatnie ważenie",
"inventory_notes": "Notatki",
"inventory_badgeOpen": "Otwarte",
"inventory_badgeSealed": "Zamknięte",
"inventory_badgeFinished": "Skończone",
"inventory_exp": "Wazność:",
"inventory_opened": "Otwarto:",
"inventory_finished": "Skończono:",
"inventory_remaining": "g pozostało",
"inventory_weighed": "Ważono:",
"inventory_confirmDelete": "Usunąć to opakowanie?",
"inventory_title": "Opakowania ({count})",
"inventory_addPackage": "+ Dodaj opakowanie",
"inventory_packageAdded": "Opakowanie dodane.",
"inventory_packageUpdated": "Opakowanie zaktualizowane.",
"inventory_packageDeleted": "Opakowanie usunięte.",
"inventory_alreadyOpened": "Już otwarte",
"inventory_openedDate": "Data otwarcia",
"inventory_finishedDate": "Data skończenia",
"inventory_expiryDate": "Data ważności",
"inventory_currentWeight": "Aktualna waga (g)",
"inventory_lastWeighed": "Ostatnie ważenie",
"inventory_notes": "Notatki",
"inventory_badgeOpen": "Otwarte",
"inventory_badgeSealed": "Zamknięte",
"inventory_badgeFinished": "Skończone",
"inventory_exp": "Wazność:",
"inventory_opened": "Otwarto:",
"inventory_finished": "Skończono:",
"inventory_remaining": "g pozostało",
"inventory_weighed": "Ważono:",
"inventory_confirmDelete": "Usunąć to opakowanie?",
"routines_title": "Rutyny",
"routines_count": "{count} rutyno (ostatnie 30 dni)",
"routines_suggestAI": "Zaproponuj rutynę AI",
"routines_addNew": "+ Nowa rutyna",
"routines_noRoutines": "Nie znaleziono rutyno.",
"routines_newTitle": "Nowa rutyna",
"routines_backToList": "← Rutyny",
"routines_detailsTitle": "Szczegóły rutyny",
"routines_date": "Data *",
"routines_amOrPm": "AM lub PM *",
"routines_notes": "Notatki",
"routines_notesPlaceholder": "Opcjonalne notatki",
"routines_createRoutine": "Utwórz rutynę",
"routines_deleteRoutine": "Usuń rutynę",
"routines_confirmDelete": "Usunąć tę rutynę?",
"routines_steps": "Kroki ({count})",
"routines_addStep": "+ Dodaj krok",
"routines_addStepTitle": "Dodaj krok",
"routines_product": "Produkt",
"routines_selectProduct": "Wybierz produkt",
"routines_dose": "Dawka",
"routines_dosePlaceholder": "np. 2 pompki",
"routines_region": "Okolica",
"routines_regionPlaceholder": "np. twarz",
"routines_addStepBtn": "Dodaj krok",
"routines_unknownStep": "Nieznany krok",
"routines_noSteps": "Brak kroków.",
"routines_title": "Rutyny",
"routines_count": "{count} rutyno (ostatnie 30 dni)",
"routines_suggestAI": "Zaproponuj rutynę AI",
"routines_addNew": "+ Nowa rutyna",
"routines_noRoutines": "Nie znaleziono rutyno.",
"routines_newTitle": "Nowa rutyna",
"routines_backToList": "← Rutyny",
"routines_detailsTitle": "Szczegóły rutyny",
"routines_date": "Data *",
"routines_amOrPm": "AM lub PM *",
"routines_notes": "Notatki",
"routines_notesPlaceholder": "Opcjonalne notatki",
"routines_createRoutine": "Utwórz rutynę",
"routines_deleteRoutine": "Usuń rutynę",
"routines_confirmDelete": "Usunąć tę rutynę?",
"routines_steps": "Kroki ({count})",
"routines_addStep": "+ Dodaj krok",
"routines_addStepTitle": "Dodaj krok",
"routines_product": "Produkt",
"routines_selectProduct": "Wybierz produkt",
"routines_dose": "Dawka",
"routines_dosePlaceholder": "np. 2 pompki",
"routines_region": "Okolica",
"routines_regionPlaceholder": "np. twarz",
"routines_addStepBtn": "Dodaj krok",
"routines_unknownStep": "Nieznany krok",
"routines_noSteps": "Brak kroków.",
"grooming_title": "Harmonogram pielęgnacji",
"grooming_backToRoutines": "← Rutyny",
"grooming_addEntry": "+ Dodaj wpis",
"grooming_entryAdded": "Wpis dodany.",
"grooming_entryUpdated": "Wpis zaktualizowany.",
"grooming_entryDeleted": "Wpis usunięty.",
"grooming_dayOfWeek": "Dzień tygodnia",
"grooming_action": "Czynność",
"grooming_notesOptional": "Notatki (opcjonalnie)",
"grooming_notesPlaceholder": "np. co 2 tygodnie",
"grooming_noEntries": "Brak wpisów. Kliknij \"+ Dodaj wpis\", aby zacząć.",
"grooming_confirmDelete": "Usunąć ten wpis?",
"grooming_actionShavingRazor": "Golenie maszynką",
"grooming_actionShavingOneblade": "Golenie OneBlade",
"grooming_actionDermarolling": "Dermarolling",
"grooming_dayMonday": "Poniedziałek",
"grooming_dayTuesday": "Wtorek",
"grooming_dayWednesday": "Środa",
"grooming_dayThursday": "Czwartek",
"grooming_dayFriday": "Piątek",
"grooming_daySaturday": "Sobota",
"grooming_daySunday": "Niedziela",
"grooming_title": "Harmonogram pielęgnacji",
"grooming_backToRoutines": "← Rutyny",
"grooming_addEntry": "+ Dodaj wpis",
"grooming_entryAdded": "Wpis dodany.",
"grooming_entryUpdated": "Wpis zaktualizowany.",
"grooming_entryDeleted": "Wpis usunięty.",
"grooming_dayOfWeek": "Dzień tygodnia",
"grooming_action": "Czynność",
"grooming_notesOptional": "Notatki (opcjonalnie)",
"grooming_notesPlaceholder": "np. co 2 tygodnie",
"grooming_noEntries": "Brak wpisów. Kliknij \"+ Dodaj wpis\", aby zacząć.",
"grooming_confirmDelete": "Usunąć ten wpis?",
"grooming_actionShavingRazor": "Golenie maszynką",
"grooming_actionShavingOneblade": "Golenie OneBlade",
"grooming_actionDermarolling": "Dermarolling",
"grooming_dayMonday": "Poniedziałek",
"grooming_dayTuesday": "Wtorek",
"grooming_dayWednesday": "Środa",
"grooming_dayThursday": "Czwartek",
"grooming_dayFriday": "Piątek",
"grooming_daySaturday": "Sobota",
"grooming_daySunday": "Niedziela",
"suggest_title": "Propozycja rutyny AI",
"suggest_backToRoutines": "← Rutyny",
"suggest_singleTab": "Jedna rutyna",
"suggest_batchTab": "Batch / Urlop",
"suggest_singleParams": "Parametry",
"suggest_date": "Data",
"suggest_timeOfDay": "Pora dnia",
"suggest_contextLabel": "Dodatkowy kontekst dla AI",
"suggest_contextOptional": "(opcjonalny)",
"suggest_contextPlaceholder": "np. wieczór imprezowy, skupiam się na nawilżeniu...",
"suggest_leavingHomeLabel": "Wychodzę dziś z domu",
"suggest_leavingHomeHint": "Wpływa na wybór SPF — zaznaczone: SPF50+, odznaczone: SPF30.",
"suggest_minoxidilToggleLabel": "Priorytet: gęstość brody/wąsów (minoksydyl)",
"suggest_minoxidilToggleHint": "Po włączeniu AI jawnie uwzględni minoksydyl dla obszaru brody/wąsów, jeśli jest dostępny.",
"suggest_generateBtn": "Generuj propozycję",
"suggest_generating": "Generuję…",
"suggest_proposalTitle": "Propozycja",
"suggest_saveRoutine": "Zapisz rutynę",
"suggest_saving": "Zapisuję…",
"suggest_regenerate": "Wygeneruj ponownie",
"suggest_batchRange": "Zakres dat",
"suggest_fromDate": "Od",
"suggest_toDate": "Do (max 14 dni)",
"suggest_batchContextLabel": "Kontekst / cel wyjazdu",
"suggest_batchContextPlaceholder": "np. słoneczna podróż do Włoch, aktywny urlop górski...",
"suggest_generatePlan": "Generuj plan",
"suggest_generatingPlan": "Generuję plan…",
"suggest_planTitle": "Plan ({count} dni)",
"suggest_saveAllRoutines": "Zapisz wszystkie rutyny",
"suggest_amSteps": "kroków",
"suggest_pmSteps": "kroków",
"suggest_noAmSteps": "Brak kroków AM.",
"suggest_noPmSteps": "Brak kroków PM.",
"suggest_errorDefault": "Błąd podczas generowania.",
"suggest_errorBatch": "Błąd podczas generowania planu.",
"suggest_errorSave": "Błąd podczas zapisywania.",
"suggest_amMorning": "AM (rano)",
"suggest_pmEvening": "PM (wieczór)",
"suggest_title": "Propozycja rutyny AI",
"suggest_backToRoutines": "← Rutyny",
"suggest_singleTab": "Jedna rutyna",
"suggest_batchTab": "Batch / Urlop",
"suggest_singleParams": "Parametry",
"suggest_date": "Data",
"suggest_timeOfDay": "Pora dnia",
"suggest_contextLabel": "Dodatkowy kontekst dla AI",
"suggest_contextOptional": "(opcjonalny)",
"suggest_contextPlaceholder": "np. wieczór imprezowy, skupiam się na nawilżeniu...",
"suggest_leavingHomeLabel": "Wychodzę dziś z domu",
"suggest_leavingHomeHint": "Wpływa na wybór SPF — zaznaczone: SPF50+, odznaczone: SPF30.",
"suggest_minoxidilToggleLabel": "Priorytet: gęstość brody/wąsów (minoksydyl)",
"suggest_minoxidilToggleHint": "Po włączeniu AI jawnie uwzględni minoksydyl dla obszaru brody/wąsów, jeśli jest dostępny.",
"suggest_generateBtn": "Generuj propozycję",
"suggest_generating": "Generuję…",
"suggest_proposalTitle": "Propozycja",
"suggest_saveRoutine": "Zapisz rutynę",
"suggest_saving": "Zapisuję…",
"suggest_regenerate": "Wygeneruj ponownie",
"suggest_batchRange": "Zakres dat",
"suggest_fromDate": "Od",
"suggest_toDate": "Do (max 14 dni)",
"suggest_batchContextLabel": "Kontekst / cel wyjazdu",
"suggest_batchContextPlaceholder": "np. słoneczna podróż do Włoch, aktywny urlop górski...",
"suggest_generatePlan": "Generuj plan",
"suggest_generatingPlan": "Generuję plan…",
"suggest_planTitle": "Plan ({count} dni)",
"suggest_saveAllRoutines": "Zapisz wszystkie rutyny",
"suggest_amSteps": "kroków",
"suggest_pmSteps": "kroków",
"suggest_noAmSteps": "Brak kroków AM.",
"suggest_noPmSteps": "Brak kroków PM.",
"suggest_errorDefault": "Błąd podczas generowania.",
"suggest_errorBatch": "Błąd podczas generowania planu.",
"suggest_errorSave": "Błąd podczas zapisywania.",
"suggest_amMorning": "AM (rano)",
"suggest_pmEvening": "PM (wieczór)",
"medications_title": "Leki",
"medications_count": "{count} wpisów",
"medications_addNew": "+ Dodaj lek",
"medications_newTitle": "Nowy lek",
"medications_kind": "Rodzaj",
"medications_productName": "Nazwa produktu *",
"medications_productNamePlaceholder": "np. Witamina D3",
"medications_activeSubstance": "Substancja czynna",
"medications_activeSubstancePlaceholder": "np. cholekalcyferol",
"medications_notes": "Notatki",
"medications_added": "Lek dodany.",
"medications_usages": "{count} użyć",
"medications_noMedications": "Brak leków.",
"medications_kindPrescription": "Na receptę",
"medications_kindOtc": "OTC (bez recepty)",
"medications_kindSupplement": "Suplement",
"medications_kindHerbal": "Zioła",
"medications_kindOther": "Inne",
"medications_title": "Leki",
"medications_count": "{count} wpisów",
"medications_addNew": "+ Dodaj lek",
"medications_newTitle": "Nowy lek",
"medications_kind": "Rodzaj",
"medications_productName": "Nazwa produktu *",
"medications_productNamePlaceholder": "np. Witamina D3",
"medications_activeSubstance": "Substancja czynna",
"medications_activeSubstancePlaceholder": "np. cholekalcyferol",
"medications_notes": "Notatki",
"medications_added": "Lek dodany.",
"medications_usages": "{count} użyć",
"medications_noMedications": "Brak leków.",
"medications_kindPrescription": "Na receptę",
"medications_kindOtc": "OTC (bez recepty)",
"medications_kindSupplement": "Suplement",
"medications_kindHerbal": "Zioła",
"medications_kindOther": "Inne",
"labResults_title": "Wyniki badań",
"labResults_count": "{count} wyników",
"labResults_addNew": "+ Dodaj wynik",
"labResults_newTitle": "Nowy wynik badania",
"labResults_flagFilter": "Flaga:",
"labResults_flagAll": "Wszystkie",
"labResults_flagNone": "Brak",
"labResults_date": "Data *",
"labResults_loincCode": "Kod LOINC *",
"labResults_testName": "Nazwa badania",
"labResults_testNamePlaceholder": "np. Hemoglobina",
"labResults_lab": "Laboratorium",
"labResults_labPlaceholder": "np. LabCorp",
"labResults_value": "Wartość",
"labResults_unit": "Jednostka",
"labResults_unitPlaceholder": "np. g/dL",
"labResults_flag": "Flaga",
"labResults_added": "Wynik dodany.",
"labResults_colDate": "Data",
"labResults_colTest": "Badanie",
"labResults_colLoinc": "LOINC",
"labResults_colValue": "Wartość",
"labResults_colFlag": "Flaga",
"labResults_colLab": "Lab",
"labResults_noResults": "Nie znaleziono wyników badań.",
"labResults_title": "Wyniki badań",
"labResults_count": "{count} wyników",
"labResults_addNew": "+ Dodaj wynik",
"labResults_newTitle": "Nowy wynik badania",
"labResults_flagFilter": "Flaga:",
"labResults_flagAll": "Wszystkie",
"labResults_flagNone": "Brak",
"labResults_date": "Data *",
"labResults_loincCode": "Kod LOINC *",
"labResults_testName": "Nazwa badania",
"labResults_testNamePlaceholder": "np. Hemoglobina",
"labResults_lab": "Laboratorium",
"labResults_labPlaceholder": "np. LabCorp",
"labResults_value": "Wartość",
"labResults_unit": "Jednostka",
"labResults_unitPlaceholder": "np. g/dL",
"labResults_flag": "Flaga",
"labResults_added": "Wynik dodany.",
"labResults_colDate": "Data",
"labResults_colTest": "Badanie",
"labResults_colLoinc": "LOINC",
"labResults_colValue": "Wartość",
"labResults_colFlag": "Flaga",
"labResults_colLab": "Lab",
"labResults_noResults": "Nie znaleziono wyników badań.",
"skin_title": "Stan skóry",
"skin_count": "{count} wpisów",
"skin_addNew": "+ Dodaj wpis",
"skin_aiAnalysisTitle": "Analiza AI ze zdjęć",
"skin_aiUploadText": "Prześlij 13 zdjęcia skóry. AI wypełni pola formularza poniżej.",
"skin_analyzePhotos": "Analizuj zdjęcia",
"skin_analyzing": "Analizuję…",
"skin_newSnapshotTitle": "Nowy wpis",
"skin_date": "Data *",
"skin_overallState": "Ogólny stan",
"skin_texture": "Tekstura",
"skin_skinType": "Typ skóry",
"skin_barrierState": "Stan bariery",
"skin_hydration": "Nawilżenie (15)",
"skin_sensitivity": "Wrażliwość (15)",
"skin_sebumTzone": "Sebum T-zone (15)",
"skin_sebumCheeks": "Sebum policzki (15)",
"skin_activeConcerns": "Aktywne problemy (przecinek)",
"skin_activeConcernsPlaceholder": "trądzik, zaczerwienienie, odwodnienie",
"skin_priorities": "Priorytety (przecinek)",
"skin_prioritiesPlaceholder": "wzmocnić barierę, redukować zaczerwienienie",
"skin_prioritiesLabel": "Priorytety",
"skin_notes": "Notatki",
"skin_addSnapshot": "Dodaj wpis",
"skin_snapshotAdded": "Wpis dodany.",
"skin_snapshotUpdated": "Wpis zaktualizowany.",
"skin_snapshotDeleted": "Wpis usunięty.",
"skin_noSnapshots": "Brak wpisów o stanie skóry.",
"skin_hydrationLabel": "Nawilżenie",
"skin_sensitivityLabel": "Wrażliwość",
"skin_barrierLabel": "Bariera",
"skin_stateExcellent": "doskonały",
"skin_stateGood": "dobry",
"skin_stateFair": "przeciętny",
"skin_statePoor": "zły",
"skin_textureSmooth": "gładka",
"skin_textureRough": "szorstka",
"skin_textureFlaky": "łuszcząca się",
"skin_textureBumpy": "nierówna",
"skin_barrierIntact": "nienaruszona",
"skin_barrierMildly": "lekko naruszona",
"skin_barrierCompromised": "naruszona",
"skin_typeDry": "sucha",
"skin_typeOily": "tłusta",
"skin_typeCombination": "mieszana",
"skin_typeSensitive": "wrażliwa",
"skin_typeNormal": "normalna",
"skin_typeAcneProne": "trądzikowa",
"skin_title": "Stan skóry",
"skin_count": "{count} wpisów",
"skin_addNew": "+ Dodaj wpis",
"skin_aiAnalysisTitle": "Analiza AI ze zdjęć",
"skin_aiUploadText": "Prześlij 13 zdjęcia skóry. AI wypełni pola formularza poniżej.",
"skin_analyzePhotos": "Analizuj zdjęcia",
"skin_analyzing": "Analizuję…",
"skin_newSnapshotTitle": "Nowy wpis",
"skin_date": "Data *",
"skin_overallState": "Ogólny stan",
"skin_texture": "Tekstura",
"skin_skinType": "Typ skóry",
"skin_barrierState": "Stan bariery",
"skin_hydration": "Nawilżenie (15)",
"skin_sensitivity": "Wrażliwość (15)",
"skin_sebumTzone": "Sebum T-zone (15)",
"skin_sebumCheeks": "Sebum policzki (15)",
"skin_activeConcerns": "Aktywne problemy (przecinek)",
"skin_activeConcernsPlaceholder": "trądzik, zaczerwienienie, odwodnienie",
"skin_priorities": "Priorytety (przecinek)",
"skin_prioritiesPlaceholder": "wzmocnić barierę, redukować zaczerwienienie",
"skin_prioritiesLabel": "Priorytety",
"skin_notes": "Notatki",
"skin_addSnapshot": "Dodaj wpis",
"skin_snapshotAdded": "Wpis dodany.",
"skin_snapshotUpdated": "Wpis zaktualizowany.",
"skin_snapshotDeleted": "Wpis usunięty.",
"skin_noSnapshots": "Brak wpisów o stanie skóry.",
"skin_hydrationLabel": "Nawilżenie",
"skin_sensitivityLabel": "Wrażliwość",
"skin_barrierLabel": "Bariera",
"skin_stateExcellent": "doskonały",
"skin_stateGood": "dobry",
"skin_stateFair": "przeciętny",
"skin_statePoor": "zły",
"skin_textureSmooth": "gładka",
"skin_textureRough": "szorstka",
"skin_textureFlaky": "łuszcząca się",
"skin_textureBumpy": "nierówna",
"skin_barrierIntact": "nienaruszona",
"skin_barrierMildly": "lekko naruszona",
"skin_barrierCompromised": "naruszona",
"skin_typeDry": "sucha",
"skin_typeOily": "tłusta",
"skin_typeCombination": "mieszana",
"skin_typeSensitive": "wrażliwa",
"skin_typeNormal": "normalna",
"skin_typeAcneProne": "trądzikowa",
"productForm_aiPrefill": "Uzupełnienie AI",
"productForm_aiPrefillText": "Wklej opis produktu ze strony, listę składników lub inny tekst. AI uzupełni dostępne pola — możesz je przejrzeć i poprawić przed zapisem.",
"productForm_pasteText": "Wklej tutaj opis produktu, składniki INCI...",
"productForm_parseWithAI": "Uzupełnij pola (AI)",
"productForm_parsing": "Przetwarzam…",
"productForm_basicInfo": "Informacje podstawowe",
"productForm_name": "Nazwa *",
"productForm_namePlaceholder": "np. Hydro Boost Water Gel",
"productForm_brand": "Marka *",
"productForm_brandPlaceholder": "np. Neutrogena",
"productForm_lineName": "Linia / seria",
"productForm_lineNamePlaceholder": "np. Hydro Boost",
"productForm_url": "URL",
"productForm_sku": "SKU",
"productForm_skuPlaceholder": "np. NTR-HB-50",
"productForm_barcode": "Kod kreskowy / EAN",
"productForm_barcodePlaceholder": "np. 3614273258975",
"productForm_classification": "Klasyfikacja",
"productForm_category": "Kategoria *",
"productForm_selectCategory": "Wybierz kategorię",
"productForm_time": "Pora *",
"productForm_timeOptions": "AM / PM / Oba",
"productForm_timeBoth": "Oba",
"productForm_leaveOn": "Leave-on *",
"productForm_leaveOnYes": "Tak (leave-on)",
"productForm_leaveOnNo": "Nie (rinse-off)",
"productForm_texture": "Tekstura",
"productForm_selectTexture": "Wybierz teksturę",
"productForm_absorptionSpeed": "Szybkość wchłaniania",
"productForm_selectSpeed": "Wybierz szybkość",
"productForm_skinProfile": "Profil skóry",
"productForm_recommendedFor": "Polecane dla typów skóry",
"productForm_targetConcerns": "Problemy docelowe",
"productForm_contraindications": "Przeciwwskazania (jedno na linię)",
"productForm_contraindicationsPlaceholder": "np. aktywna rosacea",
"productForm_ingredients": "Składniki",
"productForm_inciList": "Lista INCI (jeden składnik na linię)",
"productForm_activeIngredients": "Składniki aktywne",
"productForm_addActive": "+ Dodaj aktywny",
"productForm_noActives": "Brak składników aktywnych.",
"productForm_activeName": "Nazwa",
"productForm_activePercent": "%",
"productForm_activeStrength": "Siła",
"productForm_activeIrritation": "Podrażnienie",
"productForm_activeFunctions": "Funkcje",
"productForm_effectProfile": "Profil działania (05)",
"productForm_interactions": "Interakcje",
"productForm_synergizesWith": "Synergizuje z (jedno na linię)",
"productForm_incompatibleWith": "Niekompatybilny z",
"productForm_addIncompatibility": "+ Dodaj niekompatybilność",
"productForm_noIncompatibilities": "Brak niekompatybilności.",
"productForm_incompTarget": "Składnik docelowy",
"productForm_incompScope": "Zakres",
"productForm_incompReason": "Powód (opcjonalny)",
"productForm_incompReasonPlaceholder": "np. zmniejsza skuteczność",
"productForm_incompScopeSelect": "Wybierz…",
"productForm_contextRules": "Reguły kontekstu",
"productForm_ctxAfterShaving": "Bezpieczny po goleniu",
"productForm_ctxAfterAcids": "Bezpieczny po kwasach",
"productForm_ctxAfterRetinoids": "Bezpieczny po retinoidach",
"productForm_ctxCompromisedBarrier": "Bezpieczny przy naruszonej barierze",
"productForm_ctxLowUvOnly": "Tylko przy niskim UV (wieczór/zakrycie)",
"productForm_productDetails": "Szczegóły produktu",
"productForm_priceTier": "Przedział cenowy",
"productForm_selectTier": "Wybierz przedział",
"productForm_sizeMl": "Rozmiar (ml)",
"productForm_fullWeightG": "Waga pełna (g)",
"productForm_emptyWeightG": "Waga pustego (g)",
"productForm_paoMonths": "PAO (miesiące)",
"productForm_phMin": "pH min",
"productForm_phMax": "pH max",
"productForm_usageNotes": "Notatki o stosowaniu",
"productForm_usageNotesPlaceholder": "np. Nakładaj na wilgotną skórę, unikaj okolic oczu",
"productForm_safetyFlags": "Flagi bezpieczeństwa",
"productForm_fragranceFree": "Bez zapachów",
"productForm_essentialOilsFree": "Bez olejków eterycznych",
"productForm_alcoholDenatFree": "Bez alkoholu denat.",
"productForm_pregnancySafe": "Bezpieczny w ciąży",
"productForm_usageConstraints": "Ograniczenia stosowania",
"productForm_minIntervalHours": "Min. przerwa (godziny)",
"productForm_maxFrequencyPerWeek": "Max użyć na tydzień",
"productForm_isMedication": "To lek",
"productForm_isTool": "To narzędzie (np. dermaroller)",
"productForm_needleLengthMm": "Długość igły (mm, tylko narzędzia)",
"productForm_personalNotes": "Notatki osobiste",
"productForm_repurchaseIntent": "Zamiar ponownego zakupu",
"productForm_toleranceNotes": "Notatki o tolerancji",
"productForm_toleranceNotesPlaceholder": "np. Lekkie pieczenie, ustępuje po 2 tygodniach",
"productForm_aiPrefill": "Uzupełnienie AI",
"productForm_aiPrefillText": "Wklej opis produktu ze strony, listę składników lub inny tekst. AI uzupełni dostępne pola — możesz je przejrzeć i poprawić przed zapisem.",
"productForm_pasteText": "Wklej tutaj opis produktu, składniki INCI...",
"productForm_parseWithAI": "Uzupełnij pola (AI)",
"productForm_parsing": "Przetwarzam…",
"productForm_basicInfo": "Informacje podstawowe",
"productForm_name": "Nazwa *",
"productForm_namePlaceholder": "np. Hydro Boost Water Gel",
"productForm_brand": "Marka *",
"productForm_brandPlaceholder": "np. Neutrogena",
"productForm_lineName": "Linia / seria",
"productForm_lineNamePlaceholder": "np. Hydro Boost",
"productForm_url": "URL",
"productForm_sku": "SKU",
"productForm_skuPlaceholder": "np. NTR-HB-50",
"productForm_barcode": "Kod kreskowy / EAN",
"productForm_barcodePlaceholder": "np. 3614273258975",
"productForm_classification": "Klasyfikacja",
"productForm_category": "Kategoria *",
"productForm_selectCategory": "Wybierz kategorię",
"productForm_time": "Pora *",
"productForm_timeOptions": "AM / PM / Oba",
"productForm_timeBoth": "Oba",
"productForm_leaveOn": "Leave-on *",
"productForm_leaveOnYes": "Tak (leave-on)",
"productForm_leaveOnNo": "Nie (rinse-off)",
"productForm_texture": "Tekstura",
"productForm_selectTexture": "Wybierz teksturę",
"productForm_absorptionSpeed": "Szybkość wchłaniania",
"productForm_selectSpeed": "Wybierz szybkość",
"productForm_skinProfile": "Profil skóry",
"productForm_recommendedFor": "Polecane dla typów skóry",
"productForm_targetConcerns": "Problemy docelowe",
"productForm_contraindications": "Przeciwwskazania (jedno na linię)",
"productForm_contraindicationsPlaceholder": "np. aktywna rosacea",
"productForm_ingredients": "Składniki",
"productForm_inciList": "Lista INCI (jeden składnik na linię)",
"productForm_activeIngredients": "Składniki aktywne",
"productForm_addActive": "+ Dodaj aktywny",
"productForm_noActives": "Brak składników aktywnych.",
"productForm_activeName": "Nazwa",
"productForm_activePercent": "%",
"productForm_activeStrength": "Siła",
"productForm_activeIrritation": "Podrażnienie",
"productForm_activeFunctions": "Funkcje",
"productForm_effectProfile": "Profil działania (05)",
"productForm_interactions": "Interakcje",
"productForm_synergizesWith": "Synergizuje z (jedno na linię)",
"productForm_incompatibleWith": "Niekompatybilny z",
"productForm_addIncompatibility": "+ Dodaj niekompatybilność",
"productForm_noIncompatibilities": "Brak niekompatybilności.",
"productForm_incompTarget": "Składnik docelowy",
"productForm_incompScope": "Zakres",
"productForm_incompReason": "Powód (opcjonalny)",
"productForm_incompReasonPlaceholder": "np. zmniejsza skuteczność",
"productForm_incompScopeSelect": "Wybierz…",
"productForm_contextRules": "Reguły kontekstu",
"productForm_ctxAfterShaving": "Bezpieczny po goleniu",
"productForm_ctxAfterAcids": "Bezpieczny po kwasach",
"productForm_ctxAfterRetinoids": "Bezpieczny po retinoidach",
"productForm_ctxCompromisedBarrier": "Bezpieczny przy naruszonej barierze",
"productForm_ctxLowUvOnly": "Tylko przy niskim UV (wieczór/zakrycie)",
"productForm_productDetails": "Szczegóły produktu",
"productForm_priceTier": "Przedział cenowy",
"productForm_selectTier": "Wybierz przedział",
"productForm_sizeMl": "Rozmiar (ml)",
"productForm_fullWeightG": "Waga pełna (g)",
"productForm_emptyWeightG": "Waga pustego (g)",
"productForm_paoMonths": "PAO (miesiące)",
"productForm_phMin": "pH min",
"productForm_phMax": "pH max",
"productForm_usageNotes": "Notatki o stosowaniu",
"productForm_usageNotesPlaceholder": "np. Nakładaj na wilgotną skórę, unikaj okolic oczu",
"productForm_safetyFlags": "Flagi bezpieczeństwa",
"productForm_fragranceFree": "Bez zapachów",
"productForm_essentialOilsFree": "Bez olejków eterycznych",
"productForm_alcoholDenatFree": "Bez alkoholu denat.",
"productForm_pregnancySafe": "Bezpieczny w ciąży",
"productForm_usageConstraints": "Ograniczenia stosowania",
"productForm_minIntervalHours": "Min. przerwa (godziny)",
"productForm_maxFrequencyPerWeek": "Max użyć na tydzień",
"productForm_isMedication": "To lek",
"productForm_isTool": "To narzędzie (np. dermaroller)",
"productForm_needleLengthMm": "Długość igły (mm, tylko narzędzia)",
"productForm_personalNotes": "Notatki osobiste",
"productForm_repurchaseIntent": "Zamiar ponownego zakupu",
"productForm_toleranceNotes": "Notatki o tolerancji",
"productForm_toleranceNotesPlaceholder": "np. Lekkie pieczenie, ustępuje po 2 tygodniach",
"productForm_categoryCleanser": "Żel/pianka do mycia",
"productForm_categoryToner": "Tonik",
"productForm_categoryEssence": "Esencja",
"productForm_categorySerum": "Serum",
"productForm_categoryMoisturizer": "Krem",
"productForm_categorySpf": "SPF",
"productForm_categoryMask": "Maska",
"productForm_categoryExfoliant": "Peeling",
"productForm_categoryHairTreatment": "Pielęgnacja włosów",
"productForm_categoryTool": "Narzędzie",
"productForm_categorySpotTreatment": "Punkt leczenia",
"productForm_categoryOil": "Olejek",
"productForm_categoryCleanser": "Żel/pianka do mycia",
"productForm_categoryToner": "Tonik",
"productForm_categoryEssence": "Esencja",
"productForm_categorySerum": "Serum",
"productForm_categoryMoisturizer": "Krem",
"productForm_categorySpf": "SPF",
"productForm_categoryMask": "Maska",
"productForm_categoryExfoliant": "Peeling",
"productForm_categoryHairTreatment": "Pielęgnacja włosów",
"productForm_categoryTool": "Narzędzie",
"productForm_categorySpotTreatment": "Punkt leczenia",
"productForm_categoryOil": "Olejek",
"productForm_textureWatery": "Wodnista",
"productForm_textureGel": "Żel",
"productForm_textureEmulsion": "Emulsja",
"productForm_textureCream": "Krem",
"productForm_textureOil": "Olejek",
"productForm_textureBalm": "Balsam",
"productForm_textureFoam": "Pianka",
"productForm_textureFluid": "Fluid",
"productForm_textureWatery": "Wodnista",
"productForm_textureGel": "Żel",
"productForm_textureEmulsion": "Emulsja",
"productForm_textureCream": "Krem",
"productForm_textureOil": "Olejek",
"productForm_textureBalm": "Balsam",
"productForm_textureFoam": "Pianka",
"productForm_textureFluid": "Fluid",
"productForm_absorptionVeryFast": "Bardzo szybkie",
"productForm_absorptionFast": "Szybkie",
"productForm_absorptionModerate": "Umiarkowane",
"productForm_absorptionSlow": "Wolne",
"productForm_absorptionVerySlow": "Bardzo wolne",
"productForm_absorptionVeryFast": "Bardzo szybkie",
"productForm_absorptionFast": "Szybkie",
"productForm_absorptionModerate": "Umiarkowane",
"productForm_absorptionSlow": "Wolne",
"productForm_absorptionVerySlow": "Bardzo wolne",
"productForm_priceBudget": "Budżetowy",
"productForm_priceMid": "Średni",
"productForm_pricePremium": "Premium",
"productForm_priceLuxury": "Luksusowy",
"productForm_priceBudget": "Budżetowy",
"productForm_priceMid": "Średni",
"productForm_pricePremium": "Premium",
"productForm_priceLuxury": "Luksusowy",
"productForm_skinTypeDry": "sucha",
"productForm_skinTypeOily": "tłusta",
"productForm_skinTypeCombination": "mieszana",
"productForm_skinTypeSensitive": "wrażliwa",
"productForm_skinTypeNormal": "normalna",
"productForm_skinTypeAcneProne": "trądzikowa",
"productForm_skinTypeDry": "sucha",
"productForm_skinTypeOily": "tłusta",
"productForm_skinTypeCombination": "mieszana",
"productForm_skinTypeSensitive": "wrażliwa",
"productForm_skinTypeNormal": "normalna",
"productForm_skinTypeAcneProne": "trądzikowa",
"productForm_concernAcne": "trądzik",
"productForm_concernRosacea": "rosacea",
"productForm_concernHyperpigmentation": "przebarwienia",
"productForm_concernAging": "starzenie",
"productForm_concernDehydration": "odwodnienie",
"productForm_concernRedness": "zaczerwienienie",
"productForm_concernDamagedBarrier": "naruszona bariera",
"productForm_concernPoreVisibility": "widoczność porów",
"productForm_concernUnevenTexture": "nierówna tekstura",
"productForm_concernHairGrowth": "wzrost włosów",
"productForm_concernSebumExcess": "nadmiar sebum",
"productForm_concernAcne": "trądzik",
"productForm_concernRosacea": "rosacea",
"productForm_concernHyperpigmentation": "przebarwienia",
"productForm_concernAging": "starzenie",
"productForm_concernDehydration": "odwodnienie",
"productForm_concernRedness": "zaczerwienienie",
"productForm_concernDamagedBarrier": "naruszona bariera",
"productForm_concernPoreVisibility": "widoczność porów",
"productForm_concernUnevenTexture": "nierówna tekstura",
"productForm_concernHairGrowth": "wzrost włosów",
"productForm_concernSebumExcess": "nadmiar sebum",
"productForm_fnHumectant": "humektant",
"productForm_fnEmollient": "emolient",
"productForm_fnOcclusive": "okluzja",
"productForm_fnExfoliantAha": "peeling AHA",
"productForm_fnExfoliantBha": "peeling BHA",
"productForm_fnExfoliantPha": "peeling PHA",
"productForm_fnRetinoid": "retinoid",
"productForm_fnAntioxidant": "antyoksydant",
"productForm_fnSoothing": "łagodzący",
"productForm_fnBarrierSupport": "wsparcie bariery",
"productForm_fnBrightening": "rozjaśniający",
"productForm_fnAntiAcne": "przeciwtrądzikowy",
"productForm_fnCeramide": "ceramid",
"productForm_fnNiacinamide": "niacynamid",
"productForm_fnSunscreen": "filtr UV",
"productForm_fnPeptide": "peptyd",
"productForm_fnHairGrowth": "stymulator wzrostu włosów",
"productForm_fnPrebiotic": "prebiotyk",
"productForm_fnVitaminC": "witamina C",
"productForm_fnAntiAging": "przeciwstarzeniowy",
"productForm_fnHumectant": "humektant",
"productForm_fnEmollient": "emolient",
"productForm_fnOcclusive": "okluzja",
"productForm_fnExfoliantAha": "peeling AHA",
"productForm_fnExfoliantBha": "peeling BHA",
"productForm_fnExfoliantPha": "peeling PHA",
"productForm_fnRetinoid": "retinoid",
"productForm_fnAntioxidant": "antyoksydant",
"productForm_fnSoothing": "łagodzący",
"productForm_fnBarrierSupport": "wsparcie bariery",
"productForm_fnBrightening": "rozjaśniający",
"productForm_fnAntiAcne": "przeciwtrądzikowy",
"productForm_fnCeramide": "ceramid",
"productForm_fnNiacinamide": "niacynamid",
"productForm_fnSunscreen": "filtr UV",
"productForm_fnPeptide": "peptyd",
"productForm_fnHairGrowth": "stymulator wzrostu włosów",
"productForm_fnPrebiotic": "prebiotyk",
"productForm_fnVitaminC": "witamina C",
"productForm_fnAntiAging": "przeciwstarzeniowy",
"productForm_scopeSameStep": "ten sam krok",
"productForm_scopeSameDay": "ten sam dzień",
"productForm_scopeSamePeriod": "ten sam okres",
"productForm_scopeSameStep": "ten sam krok",
"productForm_scopeSameDay": "ten sam dzień",
"productForm_scopeSamePeriod": "ten sam okres",
"productForm_strengthLow": "1 Niskie",
"productForm_strengthMedium": "2 Średnie",
"productForm_strengthHigh": "3 Wysokie",
"productForm_strengthLow": "1 Niskie",
"productForm_strengthMedium": "2 Średnie",
"productForm_strengthHigh": "3 Wysokie",
"productForm_effectHydrationImmediate": "Nawilżenie (natychmiastowe)",
"productForm_effectHydrationLongTerm": "Nawilżenie (długoterminowe)",
"productForm_effectBarrierRepair": "Naprawa bariery",
"productForm_effectSoothing": "Łagodzenie",
"productForm_effectExfoliation": "Złuszczanie",
"productForm_effectRetinoid": "Aktywność retinoidu",
"productForm_effectIrritation": "Ryzyko podrażnienia",
"productForm_effectComedogenic": "Ryzyko komedogenności",
"productForm_effectBarrierDisruption": "Ryzyko naruszenia bariery",
"productForm_effectDryness": "Ryzyko przesuszenia",
"productForm_effectBrightening": "Rozjaśnienie",
"productForm_effectAntiAcne": "Działanie przeciwtrądzikowe",
"productForm_effectAntiAging": "Działanie przeciwstarzeniowe",
"productForm_effectHydrationImmediate": "Nawilżenie (natychmiastowe)",
"productForm_effectHydrationLongTerm": "Nawilżenie (długoterminowe)",
"productForm_effectBarrierRepair": "Naprawa bariery",
"productForm_effectSoothing": "Łagodzenie",
"productForm_effectExfoliation": "Złuszczanie",
"productForm_effectRetinoid": "Aktywność retinoidu",
"productForm_effectIrritation": "Ryzyko podrażnienia",
"productForm_effectComedogenic": "Ryzyko komedogenności",
"productForm_effectBarrierDisruption": "Ryzyko naruszenia bariery",
"productForm_effectDryness": "Ryzyko przesuszenia",
"productForm_effectBrightening": "Rozjaśnienie",
"productForm_effectAntiAcne": "Działanie przeciwtrądzikowe",
"productForm_effectAntiAging": "Działanie przeciwstarzeniowe",
"lang_pl": "PL",
"lang_en": "EN"
"lang_pl": "PL",
"lang_en": "EN"
}

View file

@ -1,12 +1,12 @@
{
"$schema": "https://inlang.com/schema/project-settings",
"baseLocale": "pl",
"locales": ["pl", "en"],
"modules": [
"https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@4/dist/index.js",
"https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@2/dist/index.js"
],
"plugin.inlang.messageFormat": {
"pathPattern": "./messages/{locale}.json"
}
"$schema": "https://inlang.com/schema/project-settings",
"baseLocale": "pl",
"locales": ["pl", "en"],
"modules": [
"https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@4/dist/index.js",
"https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@2/dist/index.js"
],
"plugin.inlang.messageFormat": {
"pathPattern": "./messages/{locale}.json"
}
}

View file

@ -5,85 +5,85 @@
/* ── CSS variable definitions (light / dark) ─────────────────────────────── */
:root {
--background: hsl(0 0% 100%);
--foreground: hsl(240 10% 3.9%);
--card: hsl(0 0% 100%);
--card-foreground: hsl(240 10% 3.9%);
--popover: hsl(0 0% 100%);
--popover-foreground: hsl(240 10% 3.9%);
--primary: hsl(240 5.9% 10%);
--primary-foreground: hsl(0 0% 98%);
--secondary: hsl(240 4.8% 95.9%);
--secondary-foreground: hsl(240 5.9% 10%);
--muted: hsl(240 4.8% 95.9%);
--muted-foreground: hsl(240 3.8% 46.1%);
--accent: hsl(240 4.8% 95.9%);
--accent-foreground: hsl(240 5.9% 10%);
--destructive: hsl(0 84.2% 60.2%);
--destructive-foreground: hsl(0 0% 98%);
--border: hsl(240 5.9% 90%);
--input: hsl(240 5.9% 90%);
--ring: hsl(240 5.9% 10%);
--radius: 0.5rem;
--background: hsl(0 0% 100%);
--foreground: hsl(240 10% 3.9%);
--card: hsl(0 0% 100%);
--card-foreground: hsl(240 10% 3.9%);
--popover: hsl(0 0% 100%);
--popover-foreground: hsl(240 10% 3.9%);
--primary: hsl(240 5.9% 10%);
--primary-foreground: hsl(0 0% 98%);
--secondary: hsl(240 4.8% 95.9%);
--secondary-foreground: hsl(240 5.9% 10%);
--muted: hsl(240 4.8% 95.9%);
--muted-foreground: hsl(240 3.8% 46.1%);
--accent: hsl(240 4.8% 95.9%);
--accent-foreground: hsl(240 5.9% 10%);
--destructive: hsl(0 84.2% 60.2%);
--destructive-foreground: hsl(0 0% 98%);
--border: hsl(240 5.9% 90%);
--input: hsl(240 5.9% 90%);
--ring: hsl(240 5.9% 10%);
--radius: 0.5rem;
}
.dark {
--background: hsl(240 10% 3.9%);
--foreground: hsl(0 0% 98%);
--card: hsl(240 10% 3.9%);
--card-foreground: hsl(0 0% 98%);
--popover: hsl(240 10% 3.9%);
--popover-foreground: hsl(0 0% 98%);
--primary: hsl(0 0% 98%);
--primary-foreground: hsl(240 5.9% 10%);
--secondary: hsl(240 3.7% 15.9%);
--secondary-foreground: hsl(0 0% 98%);
--muted: hsl(240 3.7% 15.9%);
--muted-foreground: hsl(240 5% 64.9%);
--accent: hsl(240 3.7% 15.9%);
--accent-foreground: hsl(0 0% 98%);
--destructive: hsl(0 62.8% 30.6%);
--destructive-foreground: hsl(0 0% 98%);
--border: hsl(240 3.7% 15.9%);
--input: hsl(240 3.7% 15.9%);
--ring: hsl(240 4.9% 83.9%);
--background: hsl(240 10% 3.9%);
--foreground: hsl(0 0% 98%);
--card: hsl(240 10% 3.9%);
--card-foreground: hsl(0 0% 98%);
--popover: hsl(240 10% 3.9%);
--popover-foreground: hsl(0 0% 98%);
--primary: hsl(0 0% 98%);
--primary-foreground: hsl(240 5.9% 10%);
--secondary: hsl(240 3.7% 15.9%);
--secondary-foreground: hsl(0 0% 98%);
--muted: hsl(240 3.7% 15.9%);
--muted-foreground: hsl(240 5% 64.9%);
--accent: hsl(240 3.7% 15.9%);
--accent-foreground: hsl(0 0% 98%);
--destructive: hsl(0 62.8% 30.6%);
--destructive-foreground: hsl(0 0% 98%);
--border: hsl(240 3.7% 15.9%);
--input: hsl(240 3.7% 15.9%);
--ring: hsl(240 4.9% 83.9%);
}
/* ── Map CSS vars → Tailwind v4 design tokens ────────────────────────────── */
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-destructive-foreground: var(--destructive-foreground);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) + 4px);
}
/* ── Base resets ─────────────────────────────────────────────────────────── */
* {
border-color: var(--border);
border-color: var(--border);
}
body {
background-color: var(--background);
color: var(--foreground);
background-color: var(--background);
color: var(--foreground);
}

14
frontend/src/app.d.ts vendored
View file

@ -1,13 +1,13 @@
// See https://svelte.dev/docs/kit/types#app.d.ts
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface PageState {}
// interface Platform {}
}
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface PageState {}
// interface Platform {}
}
}
export {};

View file

@ -1,11 +1,11 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>

View file

@ -1,6 +1,6 @@
import { paraglideMiddleware } from '$lib/paraglide/server.js';
import type { Handle } from '@sveltejs/kit';
import { paraglideMiddleware } from "$lib/paraglide/server.js";
import type { Handle } from "@sveltejs/kit";
export const handle: Handle = async ({ event, resolve }) => {
return paraglideMiddleware(event.request, () => resolve(event));
return paraglideMiddleware(event.request, () => resolve(event));
};

View file

@ -1,280 +1,349 @@
import { browser } from '$app/environment';
import { PUBLIC_API_BASE } from '$env/static/public';
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';
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();
// 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' })
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;
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 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 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`);
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}`);
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;
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 });
api.post("/products/parse-text", { text });
// ─── Routines ────────────────────────────────────────────────────────────────
export interface RoutineListParams {
from_date?: string;
to_date?: string;
part_of_day?: string;
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 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 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 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}`);
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);
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);
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);
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}`);
api.del(`/routines/grooming-schedule/${id}`);
// ─── Health Medications ────────────────────────────────────────────────────
export interface MedicationListParams {
kind?: string;
product_name?: string;
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 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);
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>
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}`);
api.del(`/health/medications/${id}`);
export const getMedicationUsages = (medicationId: string): Promise<MedicationUsage[]> =>
api.get(`/health/medications/${medicationId}/usages`);
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);
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;
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 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);
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}`);
api.del(`/health/lab-results/${id}`);
// ─── Skin ────────────────────────────────────────────────────────────────────
export interface SnapshotListParams {
from_date?: string;
to_date?: string;
overall_state?: string;
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 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);
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>
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 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;
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();
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();
}

View file

@ -1,17 +1,17 @@
import Root, {
type ButtonProps,
type ButtonSize,
type ButtonVariant,
buttonVariants,
type ButtonProps,
type ButtonSize,
type ButtonVariant,
buttonVariants,
} from "./button.svelte";
export {
Root,
type ButtonProps as Props,
//
Root as Button,
buttonVariants,
type ButtonProps,
type ButtonSize,
type ButtonVariant,
Root,
type ButtonProps as Props,
//
Root as Button,
buttonVariants,
type ButtonProps,
type ButtonSize,
type ButtonVariant,
};

View file

@ -7,19 +7,19 @@ import Title from "./card-title.svelte";
import Action from "./card-action.svelte";
export {
Root,
Content,
Description,
Footer,
Header,
Title,
Action,
//
Root as Card,
Content as CardContent,
Description as CardDescription,
Footer as CardFooter,
Header as CardHeader,
Title as CardTitle,
Action as CardAction,
Root,
Content,
Description,
Footer,
Header,
Title,
Action,
//
Root as Card,
Content as CardContent,
Description as CardDescription,
Footer as CardFooter,
Header as CardHeader,
Title as CardTitle,
Action as CardAction,
};

View file

@ -1,7 +1,7 @@
import Root from "./input.svelte";
export {
Root,
//
Root as Input,
Root,
//
Root as Input,
};

View file

@ -1,7 +1,7 @@
import Root from "./label.svelte";
export {
Root,
//
Root as Label,
Root,
//
Root as Label,
};

View file

@ -11,27 +11,27 @@ import GroupHeading from "./select-group-heading.svelte";
import Portal from "./select-portal.svelte";
export {
Root,
Group,
Label,
Item,
Content,
Trigger,
Separator,
ScrollDownButton,
ScrollUpButton,
GroupHeading,
Portal,
//
Root as Select,
Group as SelectGroup,
Label as SelectLabel,
Item as SelectItem,
Content as SelectContent,
Trigger as SelectTrigger,
Separator as SelectSeparator,
ScrollDownButton as SelectScrollDownButton,
ScrollUpButton as SelectScrollUpButton,
GroupHeading as SelectGroupHeading,
Portal as SelectPortal,
Root,
Group,
Label,
Item,
Content,
Trigger,
Separator,
ScrollDownButton,
ScrollUpButton,
GroupHeading,
Portal,
//
Root as Select,
Group as SelectGroup,
Label as SelectLabel,
Item as SelectItem,
Content as SelectContent,
Trigger as SelectTrigger,
Separator as SelectSeparator,
ScrollDownButton as SelectScrollDownButton,
ScrollUpButton as SelectScrollUpButton,
GroupHeading as SelectGroupHeading,
Portal as SelectPortal,
};

View file

@ -1,7 +1,7 @@
import Root from "./separator.svelte";
export {
Root,
//
Root as Separator,
Root,
//
Root as Separator,
};

View file

@ -8,21 +8,21 @@ import Header from "./table-header.svelte";
import Row from "./table-row.svelte";
export {
Root,
Body,
Caption,
Cell,
Footer,
Head,
Header,
Row,
//
Root as Table,
Body as TableBody,
Caption as TableCaption,
Cell as TableCell,
Footer as TableFooter,
Head as TableHead,
Header as TableHeader,
Row as TableRow,
Root,
Body,
Caption,
Cell,
Footer,
Head,
Header,
Row,
//
Root as Table,
Body as TableBody,
Caption as TableCaption,
Cell as TableCell,
Footer as TableFooter,
Head as TableHead,
Header as TableHeader,
Row as TableRow,
};

View file

@ -4,13 +4,13 @@ import List from "./tabs-list.svelte";
import Trigger from "./tabs-trigger.svelte";
export {
Root,
Content,
List,
Trigger,
//
Root as Tabs,
Content as TabsContent,
List as TabsList,
Trigger as TabsTrigger,
Root,
Content,
List,
Trigger,
//
Root as Tabs,
Content as TabsContent,
List as TabsList,
Trigger as TabsTrigger,
};

View file

@ -1,308 +1,335 @@
// ─── Enums ──────────────────────────────────────────────────────────────────
export type AbsorptionSpeed = 'very_fast' | 'fast' | '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 AbsorptionSpeed =
| "very_fast"
| "fast"
| "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 =
| 'humectant'
| 'emollient'
| 'occlusive'
| 'exfoliant_aha'
| 'exfoliant_bha'
| 'exfoliant_pha'
| 'retinoid'
| 'antioxidant'
| 'soothing'
| 'barrier_support'
| 'brightening'
| 'anti_acne'
| 'ceramide'
| 'niacinamide'
| 'sunscreen'
| 'peptide'
| 'hair_growth_stimulant'
| 'prebiotic'
| 'vitamin_c'
| 'anti_aging';
export type InteractionScope = 'same_step' | 'same_day' | 'same_period';
export type MedicationKind = 'prescription' | 'otc' | 'supplement' | 'herbal' | 'other';
export type OverallSkinState = 'excellent' | 'good' | 'fair' | 'poor';
export type PartOfDay = 'am' | 'pm';
export type PriceTier = 'budget' | 'mid' | 'premium' | 'luxury';
| "humectant"
| "emollient"
| "occlusive"
| "exfoliant_aha"
| "exfoliant_bha"
| "exfoliant_pha"
| "retinoid"
| "antioxidant"
| "soothing"
| "barrier_support"
| "brightening"
| "anti_acne"
| "ceramide"
| "niacinamide"
| "sunscreen"
| "peptide"
| "hair_growth_stimulant"
| "prebiotic"
| "vitamin_c"
| "anti_aging";
export type InteractionScope = "same_step" | "same_day" | "same_period";
export type MedicationKind =
| "prescription"
| "otc"
| "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 =
| 'cleanser'
| 'toner'
| 'essence'
| 'serum'
| 'moisturizer'
| 'spf'
| 'mask'
| 'exfoliant'
| 'hair_treatment'
| 'tool'
| 'spot_treatment'
| 'oil';
export type ResultFlag = 'N' | 'ABN' | 'POS' | 'NEG' | 'L' | 'H';
| "cleanser"
| "toner"
| "essence"
| "serum"
| "moisturizer"
| "spf"
| "mask"
| "exfoliant"
| "hair_treatment"
| "tool"
| "spot_treatment"
| "oil";
export type ResultFlag = "N" | "ABN" | "POS" | "NEG" | "L" | "H";
export type SkinConcern =
| 'acne'
| 'rosacea'
| 'hyperpigmentation'
| 'aging'
| 'dehydration'
| 'redness'
| 'damaged_barrier'
| 'pore_visibility'
| 'uneven_texture'
| 'hair_growth'
| 'sebum_excess';
export type SkinTexture = 'smooth' | 'rough' | 'flaky' | 'bumpy';
export type SkinType = 'dry' | 'oily' | 'combination' | 'sensitive' | 'normal' | 'acne_prone';
| "acne"
| "rosacea"
| "hyperpigmentation"
| "aging"
| "dehydration"
| "redness"
| "damaged_barrier"
| "pore_visibility"
| "uneven_texture"
| "hair_growth"
| "sebum_excess";
export type SkinTexture = "smooth" | "rough" | "flaky" | "bumpy";
export type SkinType =
| "dry"
| "oily"
| "combination"
| "sensitive"
| "normal"
| "acne_prone";
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 ───────────────────────────────────────────────────────────
export interface ActiveIngredient {
name: string;
percent?: number;
functions: IngredientFunction[];
strength_level?: StrengthLevel;
irritation_potential?: StrengthLevel;
name: string;
percent?: number;
functions: IngredientFunction[];
strength_level?: StrengthLevel;
irritation_potential?: StrengthLevel;
}
export interface ProductEffectProfile {
hydration_immediate: number;
hydration_long_term: number;
barrier_repair_strength: number;
soothing_strength: number;
exfoliation_strength: number;
retinoid_strength: number;
irritation_risk: number;
comedogenic_risk: number;
barrier_disruption_risk: number;
dryness_risk: number;
brightening_strength: number;
anti_acne_strength: number;
anti_aging_strength: number;
hydration_immediate: number;
hydration_long_term: number;
barrier_repair_strength: number;
soothing_strength: number;
exfoliation_strength: number;
retinoid_strength: number;
irritation_risk: number;
comedogenic_risk: number;
barrier_disruption_risk: number;
dryness_risk: number;
brightening_strength: number;
anti_acne_strength: number;
anti_aging_strength: number;
}
export interface ProductInteraction {
target: string;
scope: InteractionScope;
reason?: string;
target: string;
scope: InteractionScope;
reason?: string;
}
export interface ProductContext {
safe_after_shaving?: boolean;
safe_after_acids?: boolean;
safe_after_retinoids?: boolean;
safe_with_compromised_barrier?: boolean;
low_uv_only?: boolean;
safe_after_shaving?: boolean;
safe_after_acids?: boolean;
safe_after_retinoids?: boolean;
safe_with_compromised_barrier?: boolean;
low_uv_only?: boolean;
}
export interface ProductInventory {
id: string;
product_id: string;
is_opened: boolean;
opened_at?: string;
finished_at?: string;
expiry_date?: string;
current_weight_g?: number;
last_weighed_at?: string;
notes?: string;
created_at: string;
product?: Product;
id: string;
product_id: string;
is_opened: boolean;
opened_at?: string;
finished_at?: string;
expiry_date?: string;
current_weight_g?: number;
last_weighed_at?: string;
notes?: string;
created_at: string;
product?: Product;
}
export interface Product {
id: string;
name: string;
brand: string;
line_name?: string;
sku?: string;
url?: string;
barcode?: string;
category: ProductCategory;
recommended_time: DayTime;
texture?: TextureType;
absorption_speed?: AbsorptionSpeed;
leave_on: boolean;
price_tier?: PriceTier;
size_ml?: number;
full_weight_g?: number;
empty_weight_g?: number;
pao_months?: number;
inci: string[];
actives?: ActiveIngredient[];
recommended_for: SkinType[];
targets: SkinConcern[];
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;
personal_tolerance_notes?: string;
personal_repurchase_intent?: boolean;
created_at: string;
updated_at: string;
inventory: ProductInventory[];
id: string;
name: string;
brand: string;
line_name?: string;
sku?: string;
url?: string;
barcode?: string;
category: ProductCategory;
recommended_time: DayTime;
texture?: TextureType;
absorption_speed?: AbsorptionSpeed;
leave_on: boolean;
price_tier?: PriceTier;
size_ml?: number;
full_weight_g?: number;
empty_weight_g?: number;
pao_months?: number;
inci: string[];
actives?: ActiveIngredient[];
recommended_for: SkinType[];
targets: SkinConcern[];
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;
personal_tolerance_notes?: string;
personal_repurchase_intent?: boolean;
created_at: string;
updated_at: string;
inventory: ProductInventory[];
}
// ─── Routine types ───────────────────────────────────────────────────────────
export interface RoutineStep {
id: string;
routine_id: string;
product_id?: string;
order_index: number;
action_type?: GroomingAction;
action_notes?: string;
dose?: string;
region?: string;
product?: Product;
id: string;
routine_id: string;
product_id?: string;
order_index: number;
action_type?: GroomingAction;
action_notes?: string;
dose?: string;
region?: string;
product?: Product;
}
export interface Routine {
id: string;
routine_date: string;
part_of_day: PartOfDay;
notes?: string;
created_at: string;
updated_at: string;
steps?: RoutineStep[];
id: string;
routine_date: string;
part_of_day: PartOfDay;
notes?: string;
created_at: string;
updated_at: string;
steps?: RoutineStep[];
}
export interface GroomingSchedule {
id: string;
day_of_week: number;
action: GroomingAction;
notes?: string;
id: string;
day_of_week: number;
action: GroomingAction;
notes?: string;
}
export interface SuggestedStep {
product_id?: string;
action_type?: GroomingAction;
action_notes?: string;
dose?: string;
region?: string;
product_id?: string;
action_type?: GroomingAction;
action_notes?: string;
dose?: string;
region?: string;
}
export interface RoutineSuggestion {
steps: SuggestedStep[];
reasoning: string;
steps: SuggestedStep[];
reasoning: string;
}
export interface DayPlan {
date: string;
am_steps: SuggestedStep[];
pm_steps: SuggestedStep[];
reasoning: string;
date: string;
am_steps: SuggestedStep[];
pm_steps: SuggestedStep[];
reasoning: string;
}
export interface BatchSuggestion {
days: DayPlan[];
overall_reasoning: string;
days: DayPlan[];
overall_reasoning: string;
}
// ─── Shopping suggestion types ───────────────────────────────────────────────
export interface ProductSuggestion {
category: string;
product_type: string;
key_ingredients: string[];
target_concerns: string[];
why_needed: string;
recommended_time: string;
frequency: string;
category: string;
product_type: string;
key_ingredients: string[];
target_concerns: string[];
why_needed: string;
recommended_time: string;
frequency: string;
}
export interface ShoppingSuggestionResponse {
suggestions: ProductSuggestion[];
reasoning: string;
suggestions: ProductSuggestion[];
reasoning: string;
}
// ─── Health types ────────────────────────────────────────────────────────────
export interface MedicationUsage {
record_id: string;
medication_record_id: string;
dose_value?: number;
dose_unit?: string;
frequency?: string;
schedule_text?: string;
as_needed: boolean;
valid_from: string;
valid_to?: string;
source_file?: string;
notes?: string;
created_at: string;
updated_at: string;
record_id: string;
medication_record_id: string;
dose_value?: number;
dose_unit?: string;
frequency?: string;
schedule_text?: string;
as_needed: boolean;
valid_from: string;
valid_to?: string;
source_file?: string;
notes?: string;
created_at: string;
updated_at: string;
}
export interface MedicationEntry {
record_id: string;
kind: MedicationKind;
product_name: string;
active_substance?: string;
formulation?: string;
route?: string;
source_file?: string;
notes?: string;
created_at: string;
updated_at: string;
usage_history: MedicationUsage[];
record_id: string;
kind: MedicationKind;
product_name: string;
active_substance?: string;
formulation?: string;
route?: string;
source_file?: string;
notes?: string;
created_at: string;
updated_at: string;
usage_history: MedicationUsage[];
}
export interface LabResult {
record_id: string;
collected_at: string;
test_code: string;
test_name_original?: string;
test_name_loinc?: string;
value_num?: number;
value_text?: string;
value_bool?: boolean;
unit_original?: string;
unit_ucum?: string;
ref_low?: number;
ref_high?: number;
ref_text?: string;
flag?: ResultFlag;
lab?: string;
source_file?: string;
notes?: string;
created_at: string;
updated_at: string;
record_id: string;
collected_at: string;
test_code: string;
test_name_original?: string;
test_name_loinc?: string;
value_num?: number;
value_text?: string;
value_bool?: boolean;
unit_original?: string;
unit_ucum?: string;
ref_low?: number;
ref_high?: number;
ref_text?: string;
flag?: ResultFlag;
lab?: string;
source_file?: string;
notes?: string;
created_at: string;
updated_at: string;
}
// ─── Skin types ──────────────────────────────────────────────────────────────
export interface SkinConditionSnapshot {
id: string;
snapshot_date: string;
overall_state?: OverallSkinState;
skin_type?: SkinType;
texture?: SkinTexture;
hydration_level?: number;
sebum_tzone?: number;
sebum_cheeks?: number;
sensitivity_level?: number;
barrier_state?: BarrierState;
active_concerns: SkinConcern[];
risks: string[];
priorities: string[];
notes?: string;
created_at: string;
id: string;
snapshot_date: string;
overall_state?: OverallSkinState;
skin_type?: SkinType;
texture?: SkinTexture;
hydration_level?: number;
sebum_tzone?: number;
sebum_cheeks?: number;
sensitivity_level?: number;
barrier_state?: BarrierState;
active_concerns: SkinConcern[];
risks: string[];
priorities: string[];
notes?: string;
created_at: string;
}