feat(frontend): responsive design for mobile (RWD)
- Layout: mobile hamburger + drawer nav (backdrop button + sibling nav), desktop sidebar hidden on small screens, p-4 md:p-8 main padding - Products: card list view on mobile, flex-wrap filters - Lab results: card list view on mobile - ProductForm: responsive grids (grid-cols-1 sm:grid-cols-2), skin profile checkboxes 2→3 cols, active ingredient row restructured (name+✕ in flex row, percent/strength/irritation in 3-col grid), section headers stack on mobile - Skin snapshots: date+icons on one row, badges on separate row below - Product [id] header: back link stacked above title, redundant badge removed - Routines header: flex-col on mobile, sm:flex-row Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c85ca355df
commit
679e4e81f4
8 changed files with 193 additions and 60 deletions
|
|
@ -451,7 +451,7 @@
|
|||
<Card>
|
||||
<CardHeader><CardTitle>{m["productForm_basicInfo"]()}</CardTitle></CardHeader>
|
||||
<CardContent class="space-y-4">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="name">{m["productForm_name"]()}</Label>
|
||||
<Input id="name" name="name" required placeholder={m["productForm_namePlaceholder"]()} bind:value={name} />
|
||||
|
|
@ -461,7 +461,7 @@
|
|||
<Input id="brand" name="brand" required placeholder={m["productForm_brandPlaceholder"]()} bind:value={brand} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="line_name">{m["productForm_lineName"]()}</Label>
|
||||
<Input id="line_name" name="line_name" placeholder={m["productForm_lineNamePlaceholder"]()} bind:value={lineName} />
|
||||
|
|
@ -471,7 +471,7 @@
|
|||
<Input id="url" name="url" type="url" placeholder="https://…" bind:value={url} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="sku">{m["productForm_sku"]()}</Label>
|
||||
<Input id="sku" name="sku" placeholder={m["productForm_skuPlaceholder"]()} bind:value={sku} />
|
||||
|
|
@ -488,7 +488,7 @@
|
|||
<Card>
|
||||
<CardHeader><CardTitle>{m["productForm_classification"]()}</CardTitle></CardHeader>
|
||||
<CardContent>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div class="col-span-2 space-y-2">
|
||||
<Label>{m["productForm_category"]()}</Label>
|
||||
<input type="hidden" name="category" value={category} />
|
||||
|
|
@ -564,7 +564,7 @@
|
|||
<CardContent class="space-y-4">
|
||||
<div class="space-y-2">
|
||||
<Label>{m["productForm_recommendedFor"]()}</Label>
|
||||
<div class="grid grid-cols-3 gap-2">
|
||||
<div class="grid grid-cols-2 gap-2 sm:grid-cols-3">
|
||||
{#each skinTypes as st}
|
||||
<label class="flex cursor-pointer items-center gap-2 text-sm">
|
||||
<input
|
||||
|
|
@ -588,7 +588,7 @@
|
|||
|
||||
<div class="space-y-2">
|
||||
<Label>{m["productForm_targetConcerns"]()}</Label>
|
||||
<div class="grid grid-cols-3 gap-2">
|
||||
<div class="grid grid-cols-2 gap-2 sm:grid-cols-3">
|
||||
{#each skinConcerns as sc}
|
||||
<label class="flex cursor-pointer items-center gap-2 text-sm">
|
||||
<input
|
||||
|
|
@ -641,7 +641,7 @@
|
|||
</div>
|
||||
|
||||
<div class="space-y-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
|
||||
<Label>{m["productForm_activeIngredients"]()}</Label>
|
||||
<Button type="button" variant="outline" size="sm" onclick={addActive}>{m["productForm_addActive"]()}</Button>
|
||||
</div>
|
||||
|
|
@ -650,14 +650,23 @@
|
|||
|
||||
{#each actives as active, i}
|
||||
<div class="rounded-md border border-border p-3 space-y-3">
|
||||
<div class="grid grid-cols-[1fr_100px_120px_120px_auto] gap-2 items-end">
|
||||
<div class="space-y-1">
|
||||
<div class="flex items-end gap-2">
|
||||
<div class="min-w-0 flex-1 space-y-1">
|
||||
<Label class="text-xs">{m["productForm_activeName"]()}</Label>
|
||||
<Input
|
||||
placeholder="e.g. Niacinamide"
|
||||
bind:value={active.name}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onclick={() => removeActive(i)}
|
||||
class="h-7 w-7 shrink-0 p-0 text-destructive hover:text-destructive"
|
||||
>✕</Button>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 gap-2">
|
||||
<div class="space-y-1">
|
||||
<Label class="text-xs">{m["productForm_activePercent"]()}</Label>
|
||||
<Input
|
||||
|
|
@ -687,18 +696,11 @@
|
|||
<option value="3">{m["productForm_strengthHigh"]()}</option>
|
||||
</select>
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onclick={() => removeActive(i)}
|
||||
class="text-destructive hover:text-destructive"
|
||||
>✕</Button>
|
||||
</div>
|
||||
|
||||
<div class="space-y-1">
|
||||
<Label class="text-xs text-muted-foreground">{m["productForm_activeFunctions"]()}</Label>
|
||||
<div class="grid grid-cols-4 gap-1">
|
||||
<div class="grid grid-cols-2 gap-1 sm:grid-cols-4">
|
||||
{#each ingFunctions as fn}
|
||||
<label class="flex cursor-pointer items-center gap-1.5 text-xs">
|
||||
<input
|
||||
|
|
@ -764,7 +766,7 @@
|
|||
</div>
|
||||
|
||||
<div class="space-y-3">
|
||||
<div class="flex items-center justify-between">
|
||||
<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"]()}
|
||||
|
|
@ -774,7 +776,7 @@
|
|||
<input type="hidden" name="incompatible_with_json" value={incompatibleJson} />
|
||||
|
||||
{#each incompatibleWith as row, i}
|
||||
<div class="grid grid-cols-[1fr_140px_1fr_auto] gap-2 items-end">
|
||||
<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} />
|
||||
|
|
@ -813,7 +815,7 @@
|
|||
<Card>
|
||||
<CardHeader><CardTitle>{m["productForm_contextRules"]()}</CardTitle></CardHeader>
|
||||
<CardContent>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label>{m["productForm_ctxAfterShaving"]()}</Label>
|
||||
<input type="hidden" name="ctx_safe_after_shaving" value={ctxAfterShaving} />
|
||||
|
|
@ -884,7 +886,7 @@
|
|||
<Card>
|
||||
<CardHeader><CardTitle>{m["productForm_productDetails"]()}</CardTitle></CardHeader>
|
||||
<CardContent class="space-y-4">
|
||||
<div class="grid grid-cols-3 gap-4">
|
||||
<div class="grid grid-cols-2 gap-4 sm:grid-cols-3">
|
||||
<div class="space-y-2">
|
||||
<Label>{m["productForm_priceTier"]()}</Label>
|
||||
<input type="hidden" name="price_tier" value={priceTier} />
|
||||
|
|
@ -919,7 +921,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="ph_min">{m["productForm_phMin"]()}</Label>
|
||||
<Input id="ph_min" name="ph_min" type="number" min="0" max="14" step="0.1" placeholder="e.g. 3.5" bind:value={phMin} />
|
||||
|
|
@ -948,7 +950,7 @@
|
|||
<Card>
|
||||
<CardHeader><CardTitle>{m["productForm_safetyFlags"]()}</CardTitle></CardHeader>
|
||||
<CardContent>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label>{m["productForm_fragranceFree"]()}</Label>
|
||||
<input type="hidden" name="fragrance_free" value={fragranceFree} />
|
||||
|
|
@ -1008,7 +1010,7 @@
|
|||
<Card>
|
||||
<CardHeader><CardTitle>{m["productForm_usageConstraints"]()}</CardTitle></CardHeader>
|
||||
<CardContent class="space-y-4">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="min_interval_hours">{m["productForm_minIntervalHours"]()}</Label>
|
||||
<Input id="min_interval_hours" name="min_interval_hours" type="number" min="0" placeholder="e.g. 24" bind:value={minIntervalHours} />
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue