From 013492ec2b16e0b068c7279939affe5bb7d752d4 Mon Sep 17 00:00:00 2001 From: Piotr Oleszczyk Date: Thu, 5 Mar 2026 10:11:24 +0100 Subject: [PATCH] refactor(products): remove usage notes and contraindications fields --- ...tes_and_contraindications_from_products.py | 37 +++++++++++++ backend/innercontext/api/products.py | 49 +++-------------- backend/innercontext/api/routines.py | 54 +++---------------- backend/innercontext/models/product.py | 15 ------ backend/tests/test_products.py | 2 - backend/tests/test_products_helpers.py | 6 --- backend/tests/test_routines.py | 1 - backend/tests/test_routines_helpers.py | 9 ---- frontend/messages/en.json | 2 - frontend/messages/pl.json | 2 - frontend/src/lib/api.ts | 2 - .../src/lib/components/ProductForm.svelte | 19 ------- .../ProductFormDetailsSection.svelte | 15 ------ frontend/src/lib/types.ts | 2 - .../src/routes/products/[id]/+page.server.ts | 9 +--- .../src/routes/products/new/+page.server.ts | 9 +--- 16 files changed, 54 insertions(+), 179 deletions(-) create mode 100644 backend/alembic/versions/8e4c1b7a9d2f_drop_usage_notes_and_contraindications_from_products.py diff --git a/backend/alembic/versions/8e4c1b7a9d2f_drop_usage_notes_and_contraindications_from_products.py b/backend/alembic/versions/8e4c1b7a9d2f_drop_usage_notes_and_contraindications_from_products.py new file mode 100644 index 0000000..cb88045 --- /dev/null +++ b/backend/alembic/versions/8e4c1b7a9d2f_drop_usage_notes_and_contraindications_from_products.py @@ -0,0 +1,37 @@ +"""drop_usage_notes_and_contraindications_from_products + +Revision ID: 8e4c1b7a9d2f +Revises: f1a2b3c4d5e6 +Create Date: 2026-03-04 00:00:00.000000 + +""" + +from typing import Sequence, Union + +import sqlalchemy as sa + +from alembic import op + +revision: str = "8e4c1b7a9d2f" +down_revision: Union[str, None] = "f1a2b3c4d5e6" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + op.drop_column("products", "contraindications") + op.drop_column("products", "usage_notes") + + +def downgrade() -> None: + op.add_column( + "products", + sa.Column( + "contraindications", + sa.JSON(), + nullable=False, + server_default=sa.text("'[]'::json"), + ), + ) + op.alter_column("products", "contraindications", server_default=None) + op.add_column("products", sa.Column("usage_notes", sa.String(), nullable=True)) diff --git a/backend/innercontext/api/products.py b/backend/innercontext/api/products.py index c6dde0a..b8c6630 100644 --- a/backend/innercontext/api/products.py +++ b/backend/innercontext/api/products.py @@ -87,8 +87,6 @@ class ProductUpdate(SQLModel): recommended_for: Optional[list[SkinType]] = None targets: Optional[list[SkinConcern]] = None - contraindications: Optional[list[str]] = None - usage_notes: Optional[str] = None fragrance_free: Optional[bool] = None essential_oils_free: Optional[bool] = None @@ -139,8 +137,6 @@ class ProductParseResponse(SQLModel): actives: Optional[list[ActiveIngredient]] = None recommended_for: Optional[list[SkinType]] = None targets: Optional[list[SkinConcern]] = None - contraindications: Optional[list[str]] = None - usage_notes: Optional[str] = None fragrance_free: Optional[bool] = None essential_oils_free: Optional[bool] = None alcohol_denat_free: Optional[bool] = None @@ -553,8 +549,6 @@ OUTPUT SCHEMA (all fields optional — omit what you cannot determine): ], "recommended_for": [string, ...], "targets": [string, ...], - "contraindications": [string, ...], - "usage_notes": string, "fragrance_free": boolean, "essential_oils_free": boolean, "alcohol_denat_free": boolean, @@ -972,23 +966,12 @@ def _build_actives_tool_handler(products: list[Product]): return _build_product_details_tool_handler(products, mapper=_mapper) -def _build_usage_notes_tool_handler(products: list[Product]): - def _mapper(product: Product, pid: str) -> dict[str, object]: - notes = " ".join(str(product.usage_notes or "").split()) - if len(notes) > 500: - notes = notes[:497] + "..." - return {"id": pid, "name": product.name, "usage_notes": notes} - - return _build_product_details_tool_handler(products, mapper=_mapper) - - def _build_safety_rules_tool_handler(products: list[Product]): def _mapper(product: Product, pid: str) -> dict[str, object]: ctx = product.to_llm_context() return { "id": pid, "name": product.name, - "contraindications": (ctx.get("contraindications") or [])[:24], "context_rules": ctx.get("context_rules") or {}, "safety": ctx.get("safety") or {}, "min_interval_hours": ctx.get("min_interval_hours"), @@ -1020,8 +1003,12 @@ _INCI_FUNCTION_DECLARATION = genai_types.FunctionDeclaration( _SAFETY_RULES_FUNCTION_DECLARATION = genai_types.FunctionDeclaration( name="get_product_safety_rules", description=( - "Return safety and compatibility rules for selected product UUIDs, " - "including contraindications, context_rules and safety flags." + "Return structured safety metadata for selected product UUIDs. " + "context_rules explain when a product should be avoided in a routine " + "(for example after acids, after retinoids, or with a compromised barrier). " + "safety flags describe formulation-level constraints " + "(for example whether the formula is fragrance-free, whether it contains denatured alcohol, and whether it is pregnancy-safe). " + "Also includes min_interval_hours and max_frequency_per_week." ), parameters=genai_types.Schema( type=genai_types.Type.OBJECT, @@ -1055,26 +1042,6 @@ _ACTIVES_FUNCTION_DECLARATION = genai_types.FunctionDeclaration( ), ) -_USAGE_NOTES_FUNCTION_DECLARATION = genai_types.FunctionDeclaration( - name="get_product_usage_notes", - description=( - "Return compact usage notes for selected product UUIDs " - "(timing, application method and cautions)." - ), - parameters=genai_types.Schema( - type=genai_types.Type.OBJECT, - properties={ - "product_ids": genai_types.Schema( - type=genai_types.Type.ARRAY, - items=genai_types.Schema(type=genai_types.Type.STRING), - description="Product UUIDs from POSIADANE PRODUKTY.", - ) - }, - required=["product_ids"], - ), -) - - _SHOPPING_SYSTEM_PROMPT = """Jesteś asystentem zakupowym w dziedzinie pielęgnacji skóry. Twoim zadaniem jest przeanalizować stan skóry użytkownika oraz produkty, które już posiada, a następnie zasugerować TYPY produktów (bez marek), które mogłyby uzupełnić ich rutynę. @@ -1108,7 +1075,7 @@ def suggest_shopping(session: Session = Depends(get_session)): f"mogłyby uzupełnić rutynę pielęgnacyjną użytkownika.\n\n" f"{context}\n\n" "NARZEDZIA:\n" - "- Masz dostep do funkcji: get_product_inci, get_product_safety_rules, get_product_actives, get_product_usage_notes.\n" + "- Masz dostep do funkcji: get_product_inci, get_product_safety_rules, get_product_actives.\n" "- Wywoluj narzedzia tylko, gdy potrzebujesz detali do oceny konfliktow skladnikow lub ryzyka podraznien.\n" "- Grupuj UUID: staraj sie pobierac dane dla wielu produktow jednym wywolaniem.\n" f"Zwróć wyłącznie JSON zgodny ze schematem." @@ -1126,7 +1093,6 @@ def suggest_shopping(session: Session = Depends(get_session)): _INCI_FUNCTION_DECLARATION, _SAFETY_RULES_FUNCTION_DECLARATION, _ACTIVES_FUNCTION_DECLARATION, - _USAGE_NOTES_FUNCTION_DECLARATION, ] ) ], @@ -1142,7 +1108,6 @@ def suggest_shopping(session: Session = Depends(get_session)): "get_product_inci": _build_inci_tool_handler(shopping_products), "get_product_safety_rules": _build_safety_rules_tool_handler(shopping_products), "get_product_actives": _build_actives_tool_handler(shopping_products), - "get_product_usage_notes": _build_usage_notes_tool_handler(shopping_products), } try: diff --git a/backend/innercontext/api/routines.py b/backend/innercontext/api/routines.py index 0cd860e..df97f9c 100644 --- a/backend/innercontext/api/routines.py +++ b/backend/innercontext/api/routines.py @@ -201,8 +201,6 @@ def _is_minoxidil_product(product: Product) -> bool: return True if _contains_minoxidil_text(product.line_name): return True - if _contains_minoxidil_text(product.usage_notes): - return True if any(_contains_minoxidil_text(i) for i in (product.inci or [])): return True @@ -378,8 +376,6 @@ def _build_products_context( notable = {k: v for k, v in profile.items() if v and v > 0} if notable: entry += f" effects={notable}" - if ctx.get("contraindications"): - entry += f" contraindications={ctx['contraindications']}" if ctx.get("context_rules"): entry += f" context_rules={ctx['context_rules']}" safety = ctx.get("safety") or {} @@ -475,22 +471,6 @@ def _build_actives_tool_handler( return _build_product_details_tool_handler(products, mapper=_mapper) -def _build_usage_notes_tool_handler( - products: list[Product], -): - def _mapper(product: Product, pid: str) -> dict[str, object]: - notes = " ".join(str(product.usage_notes or "").split()) - if len(notes) > 500: - notes = notes[:497] + "..." - return { - "id": pid, - "name": product.name, - "usage_notes": notes, - } - - return _build_product_details_tool_handler(products, mapper=_mapper) - - def _build_safety_rules_tool_handler( products: list[Product], ): @@ -499,7 +479,6 @@ def _build_safety_rules_tool_handler( return { "id": pid, "name": product.name, - "contraindications": (ctx.get("contraindications") or [])[:24], "context_rules": ctx.get("context_rules") or {}, "safety": ctx.get("safety") or {}, "min_interval_hours": ctx.get("min_interval_hours"), @@ -604,30 +583,15 @@ _ACTIVES_FUNCTION_DECLARATION = genai_types.FunctionDeclaration( ), ) -_USAGE_NOTES_FUNCTION_DECLARATION = genai_types.FunctionDeclaration( - name="get_product_usage_notes", - description=( - "Return compact usage notes for selected product UUIDs (application method, " - "timing, and cautions)." - ), - parameters=genai_types.Schema( - type=genai_types.Type.OBJECT, - properties={ - "product_ids": genai_types.Schema( - type=genai_types.Type.ARRAY, - items=genai_types.Schema(type=genai_types.Type.STRING), - description="Product UUIDs from AVAILABLE PRODUCTS.", - ) - }, - required=["product_ids"], - ), -) - _SAFETY_RULES_FUNCTION_DECLARATION = genai_types.FunctionDeclaration( name="get_product_safety_rules", description=( - "Return safety and compatibility rules for selected product UUIDs: " - "contraindications, context_rules and safety flags." + "Return structured safety metadata for selected product UUIDs. " + "context_rules explain when a product should be avoided in a routine " + "(for example after acids, after retinoids, or with a compromised barrier). " + "safety flags describe formulation-level constraints " + "(for example whether the formula is fragrance-free, whether it contains denatured alcohol, and whether it is pregnancy-safe). " + "Also includes min_interval_hours and max_frequency_per_week." ), parameters=genai_types.Schema( type=genai_types.Type.OBJECT, @@ -685,7 +649,7 @@ WYMAGANIA ODPOWIEDZI: ZASADY PLANOWANIA: - Kolejność warstw: cleanser -> toner -> essence -> serum -> moisturizer -> [SPF dla AM]. -- Respektuj: context_rules, min_interval_hours, max_frequency_per_week, usage_notes. +- Respektuj: context_rules, min_interval_hours, max_frequency_per_week. - Zarządzanie inwentarzem: - Preferuj produkty już otwarte (miękka preferencja). - Unikaj funkcjonalnej redundancji (np. wielokrotne źródła panthenolu, ceramidów lub niacynamidu w tej samej rutynie), @@ -821,7 +785,7 @@ def suggest_routine( "INPUT DATA:\n" f"{skin_ctx}\n{grooming_ctx}\n{history_ctx}\n{day_ctx}\n{products_ctx}\n{objectives_ctx}" "\nNARZEDZIA:\n" - "- Masz dostep do funkcji: get_product_inci, get_product_safety_rules, get_product_actives, get_product_usage_notes.\n" + "- Masz dostep do funkcji: get_product_inci, get_product_safety_rules, get_product_actives.\n" "- Wywoluj narzedzia tylko, gdy potrzebujesz detali do decyzji klinicznej/bezpieczenstwa.\n" "- Staraj sie grupowac zapytania: podawaj wszystkie potrzebne UUID w jednym wywolaniu narzedzia.\n" "- Nie zgaduj detali skladu i zasad bezpieczenstwa; jesli potrzebujesz szczegolow, wywolaj odpowiednie narzedzie.\n" @@ -842,7 +806,6 @@ def suggest_routine( _INCI_FUNCTION_DECLARATION, _SAFETY_RULES_FUNCTION_DECLARATION, _ACTIVES_FUNCTION_DECLARATION, - _USAGE_NOTES_FUNCTION_DECLARATION, ], ) ], @@ -860,7 +823,6 @@ def suggest_routine( available_products ), "get_product_actives": _build_actives_tool_handler(available_products), - "get_product_usage_notes": _build_usage_notes_tool_handler(available_products), } try: diff --git a/backend/innercontext/models/product.py b/backend/innercontext/models/product.py index ac74054..c6c4a81 100644 --- a/backend/innercontext/models/product.py +++ b/backend/innercontext/models/product.py @@ -107,9 +107,6 @@ class ProductBase(SQLModel): recommended_for: list[SkinType] = Field(default_factory=list) targets: list[SkinConcern] = Field(default_factory=list) - contraindications: list[str] = Field(default_factory=list) - usage_notes: str | None = None - fragrance_free: bool | None = None essential_oils_free: bool | None = None alcohol_denat_free: bool | None = None @@ -161,9 +158,6 @@ class Product(ProductBase, table=True): targets: list[SkinConcern] = Field( default_factory=list, sa_column=Column(JSON, nullable=False) ) - contraindications: list[str] = Field( - default_factory=list, sa_column=Column(JSON, nullable=False) - ) product_effect_profile: ProductEffectProfile = Field( default_factory=ProductEffectProfile, @@ -217,9 +211,6 @@ class Product(ProductBase, table=True): if self.category == ProductCategory.SPF and not self.leave_on: raise ValueError("SPF products must be leave-on") - if self.is_medication and not self.usage_notes: - raise ValueError("Medication products must have usage_notes") - if self.price_currency is not None: self.price_currency = self.price_currency.upper() @@ -267,9 +258,6 @@ class Product(ProductBase, table=True): ctx["recommended_for"] = [_ev(s) for s in self.recommended_for] if self.targets: ctx["targets"] = [_ev(s) for s in self.targets] - if self.contraindications: - ctx["contraindications"] = self.contraindications - if self.actives: actives_ctx = [] for a in self.actives: @@ -337,9 +325,6 @@ class Product(ProductBase, table=True): ctx["is_tool"] = True if self.needle_length_mm is not None: ctx["needle_length_mm"] = self.needle_length_mm - if self.usage_notes: - ctx["usage_notes"] = self.usage_notes - if self.personal_tolerance_notes: ctx["personal_tolerance_notes"] = self.personal_tolerance_notes if self.personal_repurchase_intent is not None: diff --git a/backend/tests/test_products.py b/backend/tests/test_products.py index 956cc75..e8d2655 100644 --- a/backend/tests/test_products.py +++ b/backend/tests/test_products.py @@ -95,14 +95,12 @@ def test_list_filter_is_medication(client): "category": "serum", } client.post("/products", json={**base, "name": "Normal", "is_medication": False}) - # is_medication=True requires usage_notes (model validator) client.post( "/products", json={ **base, "name": "Med", "is_medication": True, - "usage_notes": "Apply pea-sized amount", }, ) diff --git a/backend/tests/test_products_helpers.py b/backend/tests/test_products_helpers.py index c4046f9..dde845e 100644 --- a/backend/tests/test_products_helpers.py +++ b/backend/tests/test_products_helpers.py @@ -9,7 +9,6 @@ from innercontext.api.products import ( _build_inci_tool_handler, _build_safety_rules_tool_handler, _build_shopping_context, - _build_usage_notes_tool_handler, _extract_requested_product_ids, ) from innercontext.models import Product, ProductInventory, SkinConditionSnapshot @@ -96,7 +95,6 @@ def test_suggest_shopping(client, session): assert "get_product_inci" in kwargs["function_handlers"] assert "get_product_safety_rules" in kwargs["function_handlers"] assert "get_product_actives" in kwargs["function_handlers"] - assert "get_product_usage_notes" in kwargs["function_handlers"] def test_shopping_context_medication_skip(session: Session): @@ -133,7 +131,6 @@ def test_shopping_tool_handlers_return_payloads(session: Session): category="serum", recommended_time="both", leave_on=True, - usage_notes="Use AM and PM on clean skin.", inci=["Water", "Niacinamide"], actives=[{"name": "Niacinamide", "percent": 5, "functions": ["niacinamide"]}], context_rules={"safe_after_shaving": True}, @@ -148,8 +145,5 @@ def test_shopping_tool_handlers_return_payloads(session: Session): actives_data = _build_actives_tool_handler([product])(payload) assert actives_data["products"][0]["actives"][0]["name"] == "Niacinamide" - notes_data = _build_usage_notes_tool_handler([product])(payload) - assert notes_data["products"][0]["usage_notes"] == "Use AM and PM on clean skin." - safety_data = _build_safety_rules_tool_handler([product])(payload) assert "context_rules" in safety_data["products"][0] diff --git a/backend/tests/test_routines.py b/backend/tests/test_routines.py index 8f04045..8e7d54a 100644 --- a/backend/tests/test_routines.py +++ b/backend/tests/test_routines.py @@ -252,7 +252,6 @@ def test_suggest_routine(client, session): assert "get_product_inci" in kwargs["function_handlers"] assert "get_product_safety_rules" in kwargs["function_handlers"] assert "get_product_actives" in kwargs["function_handlers"] - assert "get_product_usage_notes" in kwargs["function_handlers"] def test_suggest_batch(client, session): diff --git a/backend/tests/test_routines_helpers.py b/backend/tests/test_routines_helpers.py index 9e0ec09..5e2a0b1 100644 --- a/backend/tests/test_routines_helpers.py +++ b/backend/tests/test_routines_helpers.py @@ -13,7 +13,6 @@ from innercontext.api.routines import ( _build_recent_history, _build_safety_rules_tool_handler, _build_skin_context, - _build_usage_notes_tool_handler, _contains_minoxidil_text, _ev, _extract_active_names, @@ -56,10 +55,6 @@ def test_is_minoxidil_product(): assert _is_minoxidil_product(p) is True p.line_name = None - p.usage_notes = "Use minoxidil daily" - assert _is_minoxidil_product(p) is True - - p.usage_notes = None p.inci = ["water", "minoxidil"] assert _is_minoxidil_product(p) is True @@ -386,7 +381,6 @@ def test_additional_tool_handlers_return_product_payloads(session: Session): brand="Test", recommended_time="both", leave_on=True, - usage_notes="Apply morning and evening.", actives=[{"name": "Niacinamide", "percent": 5, "functions": ["niacinamide"]}], context_rules={"safe_after_shaving": True}, product_effect_profile={}, @@ -397,8 +391,5 @@ def test_additional_tool_handlers_return_product_payloads(session: Session): actives_out = _build_actives_tool_handler([p])(ids_payload) assert actives_out["products"][0]["actives"][0]["name"] == "Niacinamide" - notes_out = _build_usage_notes_tool_handler([p])(ids_payload) - assert notes_out["products"][0]["usage_notes"] == "Apply morning and evening." - safety_out = _build_safety_rules_tool_handler([p])(ids_payload) assert "context_rules" in safety_out["products"][0] diff --git a/frontend/messages/en.json b/frontend/messages/en.json index 61873e5..e1369fb 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -380,8 +380,6 @@ "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_inciPlaceholder": "Aqua\nGlycerin\nNiacinamide", diff --git a/frontend/messages/pl.json b/frontend/messages/pl.json index 1e50b13..6fc1d44 100644 --- a/frontend/messages/pl.json +++ b/frontend/messages/pl.json @@ -394,8 +394,6 @@ "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_inciPlaceholder": "Aqua\nGlycerin\nNiacinamide", diff --git a/frontend/src/lib/api.ts b/frontend/src/lib/api.ts index 1651605..c046723 100644 --- a/frontend/src/lib/api.ts +++ b/frontend/src/lib/api.ts @@ -133,8 +133,6 @@ export interface ProductParseResponse { actives?: ActiveIngredient[]; recommended_for?: string[]; targets?: string[]; - contraindications?: string[]; - usage_notes?: string; fragrance_free?: boolean; essential_oils_free?: boolean; alcohol_denat_free?: boolean; diff --git a/frontend/src/lib/components/ProductForm.svelte b/frontend/src/lib/components/ProductForm.svelte index fda6c4c..3c4314c 100644 --- a/frontend/src/lib/components/ProductForm.svelte +++ b/frontend/src/lib/components/ProductForm.svelte @@ -173,9 +173,7 @@ let minIntervalHours = $state(untrack(() => (product?.min_interval_hours != null ? String(product.min_interval_hours) : ''))); let maxFrequencyPerWeek = $state(untrack(() => (product?.max_frequency_per_week != null ? String(product.max_frequency_per_week) : ''))); let needleLengthMm = $state(untrack(() => (product?.needle_length_mm != null ? String(product.needle_length_mm) : ''))); - let usageNotes = $state(untrack(() => product?.usage_notes ?? '')); let inciText = $state(untrack(() => product?.inci?.join('\n') ?? '')); - let contraindicationsText = $state(untrack(() => product?.contraindications?.join('\n') ?? '')); let personalToleranceNotes = $state(untrack(() => product?.personal_tolerance_notes ?? '')); let recommendedFor = $state(untrack(() => [...(product?.recommended_for ?? [])])); @@ -215,7 +213,6 @@ if (r.url) url = r.url; if (r.sku) sku = r.sku; if (r.barcode) barcode = r.barcode; - if (r.usage_notes) usageNotes = r.usage_notes; if (r.category) category = r.category; if (r.recommended_time) recommendedTime = r.recommended_time; if (r.texture) texture = r.texture; @@ -241,7 +238,6 @@ if (r.is_medication != null) isMedication = r.is_medication; if (r.is_tool != null) isTool = r.is_tool; if (r.inci?.length) inciText = r.inci.join('\n'); - if (r.contraindications?.length) contraindicationsText = r.contraindications.join('\n'); if (r.actives?.length) { actives = r.actives.map((a) => ({ name: a.name, @@ -415,9 +411,7 @@ minIntervalHours, maxFrequencyPerWeek, needleLengthMm, - usageNotes, inciText, - contraindicationsText, personalToleranceNotes, recommendedFor, targetConcerns, @@ -589,17 +583,6 @@ -
- - -
@@ -737,7 +720,6 @@ {@const DetailsSection = mod.default} -
- - -
diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts index 9cbdd82..bc223a9 100644 --- a/frontend/src/lib/types.ts +++ b/frontend/src/lib/types.ts @@ -161,8 +161,6 @@ export interface Product { actives?: ActiveIngredient[]; recommended_for: SkinType[]; targets: SkinConcern[]; - contraindications: string[]; - usage_notes?: string; fragrance_free?: boolean; essential_oils_free?: boolean; alcohol_denat_free?: boolean; diff --git a/frontend/src/routes/products/[id]/+page.server.ts b/frontend/src/routes/products/[id]/+page.server.ts index 29a95ca..40fe21b 100644 --- a/frontend/src/routes/products/[id]/+page.server.ts +++ b/frontend/src/routes/products/[id]/+page.server.ts @@ -41,11 +41,6 @@ function parseOptionalString(v: string | null): string | undefined { return s || undefined; } -function parseTextList(v: string | null): string[] { - if (!v?.trim()) return []; - return v.split(/\n/).map((s) => s.trim()).filter(Boolean); -} - function parseEffectProfile(form: FormData): Record { const keys = [ 'hydration_immediate', 'hydration_long_term', @@ -98,7 +93,6 @@ export const actions: Actions = { const leave_on = form.get('leave_on') === 'true'; const recommended_for = form.getAll('recommended_for') as string[]; const targets = form.getAll('targets') as string[]; - const contraindications = parseTextList(form.get('contraindications') as string | null); const inci_raw = form.get('inci') as string; const inci = inci_raw @@ -113,13 +107,12 @@ export const actions: Actions = { leave_on, recommended_for, targets, - contraindications, inci, product_effect_profile: parseEffectProfile(form) }; // Optional strings - for (const field of ['line_name', 'url', 'sku', 'barcode', 'usage_notes', 'personal_tolerance_notes', 'price_currency']) { + for (const field of ['line_name', 'url', 'sku', 'barcode', 'personal_tolerance_notes', 'price_currency']) { const v = parseOptionalString(form.get(field) as string | null); body[field] = v ?? null; } diff --git a/frontend/src/routes/products/new/+page.server.ts b/frontend/src/routes/products/new/+page.server.ts index 32bf0f4..3e7df6d 100644 --- a/frontend/src/routes/products/new/+page.server.ts +++ b/frontend/src/routes/products/new/+page.server.ts @@ -29,11 +29,6 @@ function parseOptionalString(v: string | null): string | undefined { return s || undefined; } -function parseTextList(v: string | null): string[] { - if (!v?.trim()) return []; - return v.split(/\n/).map((s) => s.trim()).filter(Boolean); -} - function parseEffectProfile(form: FormData): Record { const keys = [ 'hydration_immediate', 'hydration_long_term', @@ -86,7 +81,6 @@ export const actions: Actions = { const leave_on = form.get('leave_on') === 'true'; const recommended_for = form.getAll('recommended_for') as string[]; const targets = form.getAll('targets') as string[]; - const contraindications = parseTextList(form.get('contraindications') as string | null); const inci_raw = form.get('inci') as string; const inci = inci_raw @@ -101,13 +95,12 @@ export const actions: Actions = { leave_on, recommended_for, targets, - contraindications, inci, product_effect_profile: parseEffectProfile(form) }; // Optional strings - for (const field of ['line_name', 'url', 'sku', 'barcode', 'usage_notes', 'personal_tolerance_notes', 'price_currency']) { + for (const field of ['line_name', 'url', 'sku', 'barcode', 'personal_tolerance_notes', 'price_currency']) { const v = parseOptionalString(form.get(field) as string | null); if (v !== undefined) payload[field] = v; }