fix: cramped ingredient/step rows on mobile in recipe editor

Both rows packed 4+ inline fields (grip handle, quantity, unit, name,
note for ingredients; step number, textarea, timer, delete for steps)
into a single flex row with no mobile-aware wrap plan — flex-wrap on
ingredients caused the delete button to float disconnected from its
row, and steps had no wrap at all, squeezing the textarea down to a
sliver. Both now split into two grouped rows below sm: breakpoint
(core fields, then secondary field + delete) and collapse back to one
row on larger screens.

Not visually verified in a browser — this sandbox has no DB/dev
server running (Docker unavailable), so this is typecheck+lint
verified only.

v0.46.2
This commit is contained in:
Arnaud
2026-07-17 23:28:34 +02:00
parent 458b5f2b07
commit 21a3622e6c
5 changed files with 83 additions and 63 deletions
+5
View File
@@ -2,6 +2,11 @@
All notable changes to Epicure are documented here. This file is mirrored in-app at `/changelog` (and in the admin dashboard) via `apps/web/lib/changelog.ts` — update both together.
## 0.46.2 — 2026-07-17 19:20
### Fixed
- Recipe editor's ingredient and step rows were cramped on mobile — 4 inline fields plus a timer/delete button squeezed into one row with no real wrap plan. Both now stack sanely on narrow screens (name/quantity/unit on one line, note/timer + delete on the next) and stay a single row on larger screens.
## 0.46.1 — 2026-07-17 19:05
### Fixed
+68 -60
View File
@@ -691,40 +691,44 @@ export function RecipeForm({ recipeId, defaultValues }: RecipeFormProps) {
<Label>{t("ingredients")}</Label>
<div className="space-y-2">
{ingredients.map((ing, i) => (
<div key={ing.id} className="flex flex-wrap gap-2 items-start">
<GripVertical className="h-4 w-4 text-muted-foreground mt-2 cursor-grab shrink-0" />
<Input
value={ing.quantity}
onChange={(e) => updateIngredient(i, { quantity: e.target.value })}
placeholder={t("amount")}
className="w-14 sm:w-16 shrink-0"
/>
<Input
value={ing.unit}
onChange={(e) => updateIngredient(i, { unit: e.target.value })}
placeholder={t("unit")}
className="w-16 sm:w-24 shrink-0"
/>
<Input
value={ing.rawName}
onChange={(e) => updateIngredient(i, { rawName: e.target.value })}
placeholder={t("ingredient")}
className="flex-1 min-w-[100px]"
/>
<Input
value={ing.note}
onChange={(e) => updateIngredient(i, { note: e.target.value })}
placeholder={t("note")}
className="w-full sm:w-48 sm:shrink-0"
/>
<button
type="button"
onClick={() => removeIngredient(i)}
disabled={ingredients.length === 1}
className="mt-1.5 text-muted-foreground hover:text-destructive transition-colors disabled:opacity-30"
>
<Trash2 className="h-4 w-4" />
</button>
<div key={ing.id} className="flex flex-col sm:flex-row gap-2 sm:items-start">
<div className="flex gap-2 items-start">
<GripVertical className="h-4 w-4 text-muted-foreground mt-2 cursor-grab shrink-0" />
<Input
value={ing.quantity}
onChange={(e) => updateIngredient(i, { quantity: e.target.value })}
placeholder={t("amount")}
className="w-16 shrink-0"
/>
<Input
value={ing.unit}
onChange={(e) => updateIngredient(i, { unit: e.target.value })}
placeholder={t("unit")}
className="w-16 sm:w-24 shrink-0"
/>
<Input
value={ing.rawName}
onChange={(e) => updateIngredient(i, { rawName: e.target.value })}
placeholder={t("ingredient")}
className="flex-1 min-w-0"
/>
</div>
<div className="flex gap-2 items-start pl-6 sm:pl-0">
<Input
value={ing.note}
onChange={(e) => updateIngredient(i, { note: e.target.value })}
placeholder={t("note")}
className="flex-1 sm:w-48 sm:shrink-0"
/>
<button
type="button"
onClick={() => removeIngredient(i)}
disabled={ingredients.length === 1}
className="mt-2 text-muted-foreground hover:text-destructive transition-colors disabled:opacity-30 shrink-0"
>
<Trash2 className="h-4 w-4" />
</button>
</div>
</div>
))}
</div>
@@ -835,33 +839,37 @@ export function RecipeForm({ recipeId, defaultValues }: RecipeFormProps) {
<div className="space-y-3">
{steps.map((step, i) => (
<div key={step.id} className="space-y-1.5">
<div className="flex gap-3 items-start">
<div className="mt-2.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-muted text-xs font-bold text-muted-foreground">
{i + 1}
<div className="flex flex-col sm:flex-row gap-2 sm:gap-3">
<div className="flex gap-3 flex-1 min-w-0">
<div className="mt-2.5 flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-muted text-xs font-bold text-muted-foreground">
{i + 1}
</div>
<Textarea
value={step.instruction}
onChange={(e) => updateStep(i, { instruction: e.target.value })}
placeholder={t("stepPlaceholder", { n: i + 1 })}
rows={2}
className="flex-1 min-w-0"
/>
</div>
<div className="flex gap-2 items-start pl-9 sm:pl-0">
<Input
value={step.timerSeconds}
onChange={(e) => updateStep(i, { timerSeconds: e.target.value })}
placeholder={t("timerSeconds")}
type="number"
min={0}
className="w-28 shrink-0"
/>
<button
type="button"
onClick={() => removeStep(i)}
disabled={steps.length === 1}
className="mt-2 text-muted-foreground hover:text-destructive transition-colors disabled:opacity-30 shrink-0"
>
<Trash2 className="h-4 w-4" />
</button>
</div>
<Textarea
value={step.instruction}
onChange={(e) => updateStep(i, { instruction: e.target.value })}
placeholder={t("stepPlaceholder", { n: i + 1 })}
rows={2}
className="flex-1 min-w-0"
/>
<Input
value={step.timerSeconds}
onChange={(e) => updateStep(i, { timerSeconds: e.target.value })}
placeholder={t("timerSeconds")}
type="number"
min={0}
className="w-28 shrink-0"
/>
<button
type="button"
onClick={() => removeStep(i)}
disabled={steps.length === 1}
className="mt-2.5 text-muted-foreground hover:text-destructive transition-colors disabled:opacity-30"
>
<Trash2 className="h-4 w-4" />
</button>
</div>
{isBatchCook && dishes.some((d) => d.name.trim()) && (
<div className="flex flex-wrap gap-1.5 pl-9">
+8 -1
View File
@@ -1,5 +1,5 @@
// Mirrors CHANGELOG.md at the repo root — update both together.
export const APP_VERSION = "0.46.1";
export const APP_VERSION = "0.46.2";
export type ChangelogEntry = {
version: string;
@@ -11,6 +11,13 @@ export type ChangelogEntry = {
};
export const CHANGELOG: ChangelogEntry[] = [
{
version: "0.46.2",
date: "2026-07-17 19:20",
fixed: [
"Recipe editor's ingredient and step rows were cramped on mobile — 4 inline fields plus a timer/delete button squeezed into one row with no real wrap plan. Both now stack sanely on narrow screens (name/quantity/unit on one line, note/timer + delete on the next) and stay a single row on larger screens.",
],
},
{
version: "0.46.1",
date: "2026-07-17 19:05",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@epicure/web",
"version": "0.46.1",
"version": "0.46.2",
"private": true,
"scripts": {
"dev": "next dev",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "epicure",
"version": "0.46.1",
"version": "0.46.2",
"private": true,
"scripts": {
"dev": "pnpm --filter web dev",