"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}
);
}