refactor(products): remove obsolete interaction fields across stack
This commit is contained in:
parent
1d8a8eafb8
commit
c5ea38880c
16 changed files with 32 additions and 278 deletions
|
|
@ -1,7 +1,7 @@
|
|||
<script lang="ts">
|
||||
import { untrack } from 'svelte';
|
||||
import type { Product } from '$lib/types';
|
||||
import type { IngredientFunction, InteractionScope } from '$lib/types';
|
||||
import type { IngredientFunction } from '$lib/types';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
|
|
@ -34,7 +34,6 @@
|
|||
'brightening', 'anti_acne', 'ceramide', 'niacinamide',
|
||||
'sunscreen', 'peptide', 'hair_growth_stimulant', 'prebiotic', 'vitamin_c', 'anti_aging'
|
||||
];
|
||||
const interactionScopes: InteractionScope[] = ['same_step', 'same_day', 'same_period'];
|
||||
|
||||
// ── Translated label maps ─────────────────────────────────────────────────
|
||||
|
||||
|
|
@ -125,12 +124,6 @@
|
|||
anti_aging: m["productForm_fnAntiAging"]()
|
||||
});
|
||||
|
||||
const scopeLabels = $derived<Record<string, string>>({
|
||||
same_step: m["productForm_scopeSameStep"](),
|
||||
same_day: m["productForm_scopeSameDay"](),
|
||||
same_period: m["productForm_scopeSamePeriod"]()
|
||||
});
|
||||
|
||||
const tristate = $derived([
|
||||
{ value: '', label: m.common_unknown() },
|
||||
{ value: 'true', label: m.common_yes() },
|
||||
|
|
@ -177,7 +170,6 @@
|
|||
let usageNotes = $state(untrack(() => product?.usage_notes ?? ''));
|
||||
let inciText = $state(untrack(() => product?.inci?.join('\n') ?? ''));
|
||||
let contraindicationsText = $state(untrack(() => product?.contraindications?.join('\n') ?? ''));
|
||||
let synergizesWithText = $state(untrack(() => product?.synergizes_with?.join('\n') ?? ''));
|
||||
|
||||
let recommendedFor = $state<string[]>(untrack(() => [...(product?.recommended_for ?? [])]));
|
||||
let targetConcerns = $state<string[]>(untrack(() => [...(product?.targets ?? [])]));
|
||||
|
|
@ -239,7 +231,6 @@
|
|||
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.synergizes_with?.length) synergizesWithText = r.synergizes_with.join('\n');
|
||||
if (r.actives?.length) {
|
||||
actives = r.actives.map((a) => ({
|
||||
name: a.name,
|
||||
|
|
@ -249,13 +240,6 @@
|
|||
irritation_potential: a.irritation_potential != null ? String(a.irritation_potential) : ''
|
||||
}));
|
||||
}
|
||||
if (r.incompatible_with?.length) {
|
||||
incompatibleWith = r.incompatible_with.map((i) => ({
|
||||
target: i.target,
|
||||
scope: i.scope,
|
||||
reason: i.reason ?? ''
|
||||
}));
|
||||
}
|
||||
if (r.product_effect_profile) {
|
||||
effectValues = { ...effectValues, ...r.product_effect_profile };
|
||||
}
|
||||
|
|
@ -380,40 +364,6 @@
|
|||
)
|
||||
);
|
||||
|
||||
// ── Dynamic incompatible_with ─────────────────────────────────────────────
|
||||
|
||||
type IncompatibleRow = { target: string; scope: string; reason: string };
|
||||
|
||||
let incompatibleWith: IncompatibleRow[] = $state(
|
||||
untrack(() =>
|
||||
product?.incompatible_with?.map((i) => ({
|
||||
target: i.target,
|
||||
scope: i.scope,
|
||||
reason: i.reason ?? ''
|
||||
})) ?? []
|
||||
)
|
||||
);
|
||||
|
||||
function addIncompatible() {
|
||||
incompatibleWith = [...incompatibleWith, { target: '', scope: '', reason: '' }];
|
||||
}
|
||||
|
||||
function removeIncompatible(i: number) {
|
||||
incompatibleWith = incompatibleWith.filter((_, idx) => idx !== i);
|
||||
}
|
||||
|
||||
let incompatibleJson = $derived(
|
||||
JSON.stringify(
|
||||
incompatibleWith
|
||||
.filter((i) => i.target.trim() && i.scope)
|
||||
.map((i) => ({
|
||||
target: i.target.trim(),
|
||||
scope: i.scope,
|
||||
...(i.reason.trim() ? { reason: i.reason.trim() } : {})
|
||||
}))
|
||||
)
|
||||
);
|
||||
|
||||
const textareaClass =
|
||||
'w-full rounded-md border border-input bg-background px-3 py-2 text-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2';
|
||||
|
||||
|
|
@ -749,68 +699,6 @@
|
|||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<!-- ── Interactions ───────────────────────────────────────────────────────── -->
|
||||
<Card>
|
||||
<CardHeader><CardTitle>{m["productForm_interactions"]()}</CardTitle></CardHeader>
|
||||
<CardContent class="space-y-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="synergizes_with">{m["productForm_synergizesWith"]()}</Label>
|
||||
<textarea
|
||||
id="synergizes_with"
|
||||
name="synergizes_with"
|
||||
rows="3"
|
||||
placeholder="Ceramides Niacinamide Retinoids"
|
||||
class={textareaClass}
|
||||
bind:value={synergizesWithText}
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<div class="space-y-3">
|
||||
<div class="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
|
||||
<Label>{m["productForm_incompatibleWith"]()}</Label>
|
||||
<Button type="button" variant="outline" size="sm" onclick={addIncompatible}>
|
||||
{m["productForm_addIncompatibility"]()}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="incompatible_with_json" value={incompatibleJson} />
|
||||
|
||||
{#each incompatibleWith as row, i}
|
||||
<div class="grid grid-cols-2 gap-2 items-end sm:grid-cols-[1fr_140px_1fr_auto]">
|
||||
<div class="space-y-1">
|
||||
<Label class="text-xs">{m["productForm_incompTarget"]()}</Label>
|
||||
<Input placeholder="e.g. Vitamin C" bind:value={row.target} />
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label class="text-xs">{m["productForm_incompScope"]()}</Label>
|
||||
<select class={selectClass} bind:value={row.scope}>
|
||||
<option value="">{m["productForm_incompScopeSelect"]()}</option>
|
||||
{#each interactionScopes as s}
|
||||
<option value={s}>{scopeLabels[s]}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label class="text-xs">{m["productForm_incompReason"]()}</Label>
|
||||
<Input placeholder={m["productForm_incompReasonPlaceholder"]()} bind:value={row.reason} />
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onclick={() => removeIncompatible(i)}
|
||||
class="text-destructive hover:text-destructive"
|
||||
>✕</Button>
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
{#if incompatibleWith.length === 0}
|
||||
<p class="text-sm text-muted-foreground">{m["productForm_noIncompatibilities"]()}</p>
|
||||
{/if}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<!-- ── Context rules ──────────────────────────────────────────────────────── -->
|
||||
<Card>
|
||||
<CardHeader><CardTitle>{m["productForm_contextRules"]()}</CardTitle></CardHeader>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue