From acb4d97870c8c2104d6301b455d02f48f9411db0 Mon Sep 17 00:00:00 2001 From: Lucas Winther Date: Wed, 2 Sep 2026 22:59:27 +0200 Subject: [PATCH] fix(month): align purchase rows, and reach their actions on a phone MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three defects in the purchase list, plus NativeSelect for the rule-kind picker. Alignment: `px-1` on the row indented every description 4px past its own date heading, so the list had two left edges 4px apart. `-mx-2 px-2` puts the text exactly under the heading and bleeds the hover wash outward, so the highlight has an inset instead of stopping flush against the first character. The hover-action reservation: a fixed `w-[7.5rem]` kept the amounts from shifting when edit/delete appeared, which is right on a desktop. On a phone it was doubly wrong — there is no hover, so `opacity-0` made these the hardest controls in the app to reach, and the reservation still spent 120px of a 350px row hiding them, truncating descriptions to "Weeken…", then "Bik…", and the financed row to nothing but its badge. So the row is one line from `sm:` up and two below it: description on its own line, amount and actions on the next. `sm:contents` dissolves the wrapper above `sm:`, so the desktop row is the same flat flex line it always was. This is the call PostTable already makes at that width, for the same reason. Also: the month steps wrapped mid-label into "← 2026-" / "08" on a phone, and the error alert's "dismiss" was an unpadded underlined word. Co-Authored-By: Claude Opus 5 (1M context) --- src/ui/routes/MonthRoute.tsx | 95 ++++++++++++++++++++++-------------- 1 file changed, 59 insertions(+), 36 deletions(-) diff --git a/src/ui/routes/MonthRoute.tsx b/src/ui/routes/MonthRoute.tsx index 7c9fd4c..9d04644 100644 --- a/src/ui/routes/MonthRoute.tsx +++ b/src/ui/routes/MonthRoute.tsx @@ -3,6 +3,7 @@ import { Link, useParams } from "react-router"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Button } from "@/components/ui/button"; +import { NativeSelect } from "@/components/ui/native-select"; import { useDataset } from "../hooks/useDataset.ts"; import { useMutate } from "../hooks/useMutate.ts"; import { monthView, type MonthViewModel } from "../../domain/views.ts"; @@ -42,9 +43,9 @@ export function MonthRoute() { className="flex items-center justify-between gap-3 rounded-lg border border-destructive/40 bg-destructive/5 p-3 text-sm" > Could not save: {error} - + )} @@ -190,41 +191,63 @@ function PurchaseRow({ }) { const slice = sliceAmountForMonth(purchase, monthId)!; return ( -
  • - + // `-mx-2 px-2` rather than `px-1`: the row's text now starts exactly where + // its own date heading does — `px-1` indented it 4px past the heading — + // and the hover wash bleeds 8px outward, so the highlight has an inset + // instead of stopping flush against the first character. + // + // One line from `sm:` up, two below it. At 390px this row's fixed parts — + // a mono amount, "edit", "delete" — take about 215px of a 350px card, + // which left the description roughly 110px: it truncated to "Weeken…", + // then "Bik…", and on the financed row to nothing but its badge. The + // description is the only part that says what the purchase WAS, so at that + // width it gets a line to itself and everything else gets the next one. + // The same call PostTable already makes, for the same reason. +
  • + {purchase.description} {purchase.note && ( {purchase.note} )} - {purchase.schedule && ( - - financed + + {/* `sm:contents` dissolves this wrapper from `sm:` up, so the desktop row + is exactly the flat flex line it has always been. Below `sm:` it is + the second line, and its `w-full` is what forces the wrap. */} + + {purchase.schedule && ( + + financed + + )} + + {formatMoney(slice.amount, slice.currency)} + + + {/* From `sm:` up: revealed on hover, and held at a fixed width so + revealing it cannot shift the amounts, which are the column being + read down. Below `sm:`: always visible. A phone has no hover, so + `opacity-0` had made these two the hardest controls in the app to + reach, while still reserving 120px of the row to hide them in. */} + + + edit + + } + /> + - )} - - {formatMoney(slice.amount, slice.currency)} - - {/* Held at a fixed width so revealing the actions on hover cannot shift - the amounts, which are the column being read down. */} - - - edit - - } - /> -
  • ); @@ -260,7 +283,7 @@ function MonthStep({ to, label, back = false }: { to: string; label: string; bac return ( {back ? `\u2190 ${label}` : `${label} \u2192`} @@ -307,12 +330,12 @@ function RuleFromMonth({ } return ( -
    +
    {post.name}: allocate from {monthId} onward - +