"use client"; import { ArrowLeft, Check, ClipboardCheck, Globe } from "lucide-react"; import Link from "next/link"; import { startTransition, useOptimistic } from "react"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; import { StoreDot } from "@/features/prices/components/offers-list"; import { useAutoRefresh } from "@/hooks/use-auto-refresh"; import { tr } from "@/i18n/tr"; import { cn } from "@/lib/utils"; import { formatMoney } from "@/shared/format"; import { formatQuantity } from "@/shared/quantity"; import { SHOPPING_REFRESH_MS } from "@/shared/shopping"; import type { PlanLine } from "@/shared/shopping-plan"; import { setShoppingItemCheckedAction } from "../actions"; import type { ShoppingItemView, ShoppingListDetail } from "../service"; import { planHeadline, type PlanChoice } from "./plan-panel"; const t = tr.shopping; const m = t.mode; type Group = { key: string; title: string; slot: number | null; online: boolean; hint?: string; rows: { item: ShoppingItemView; line: PlanLine | null }[]; }; function Row({ item, line, product, canWrite, onToggle, }: { item: ShoppingItemView; line: PlanLine | null; product: { label: string; packageLabel: string } | undefined; canWrite: boolean; onToggle: () => void; }) { const sub = [ line ? line.packages > 1 ? t.plan.line(formatQuantity(line.packages * 1000), formatMoney(line.priceMinor)) : formatMoney(line.priceMinor) : null, line && product && item.kind === "type" ? `${product.label} · ${product.packageLabel}` : null, item.note || null, ].filter(Boolean); return (
  • ); } /** /alisveris/:id/mod — mağazaya göre gruplanmış, büyük dokunma alanlı kontrol listesi. */ export function ShoppingModeView({ data, choice, }: { data: ShoppingListDetail; choice: PlanChoice; }) { useAutoRefresh(SHOPPING_REFRESH_MS); const { list, plan, canWrite } = data; const [items, setOptimistic] = useOptimistic( data.items, (state, change: { id: string; checked: boolean }) => state.map((i) => (i.id === change.id ? { ...i, checked: change.checked } : i)), ); const option = (choice === "single" ? plan.single : null) ?? plan.recommended; const byId = new Map(items.map((i) => [i.id, i])); const groups: Group[] = []; const placed = new Set(); for (const g of option?.stores ?? []) { const store = plan.stores[g.storeId]; const rows = g.lines.flatMap((line) => { const item = byId.get(line.itemId); if (!item) return []; placed.add(item.id); return [{ item, line }]; }); groups.push({ key: g.storeId, title: store?.label ?? "—", slot: store?.slot ?? null, online: store?.kind === "online", rows, }); } const rest = items.filter((i) => !placed.has(i.id)); if (rest.length) groups.push({ key: "other", title: m.other, slot: null, online: false, hint: m.otherHint, rows: rest.map((item) => ({ item, line: null })), }); const done = items.filter((i) => i.checked).length; const pct = items.length ? Math.round((done / items.length) * 100) : 0; function toggle(item: ShoppingItemView) { const checked = !item.checked; startTransition(async () => { setOptimistic({ id: item.id, checked }); const r = await setShoppingItemCheckedAction(list.id, item.id, checked); if (!r.ok) toast.error(r.error); }); } const headline = planHeadline(plan); const differs = plan.single && plan.single.stores.map((s) => s.storeId).join() !== plan.recommended?.stores.map((s) => s.storeId).join(); return (

    {m.title}: {list.name}

    {headline ?

    {headline}

    : null} {differs ? ( ) : null}
    {m.progress(done, items.length)} {done === items.length && items.length ? ( {m.allDone} ) : null}
    {groups.map((g) => { const groupDone = g.rows.filter((r) => r.item.checked).length; return (

    {g.slot ? : null} {g.title} {g.online ? ( ) : null}

    {groupDone}/{g.rows.length}
    {g.hint ?

    {g.hint}

    : null}
      {g.rows.map(({ item, line }) => ( toggle(item)} /> ))}
    ); })} {canWrite && done > 0 ? ( ) : null}
    ); }