fa70168622
- /alisveris, /alisveris/:id ve /alisveris/:id/mod sayfaları; odak ve 30 sn yenileme - stok önerileri paneli, kalem ekle/düzenle/taşı, mağazaya gruplu kontrol listesi - dashboard Alışveriş kartı, kenar menü, ürün tipi formunda hacimli işareti Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
116 lines
4.1 KiB
TypeScript
116 lines
4.1 KiB
TypeScript
"use client";
|
|
|
|
import { CheckCircle2, ListChecks, Plus, Sparkles } from "lucide-react";
|
|
import Link from "next/link";
|
|
import { useState } from "react";
|
|
import { Button } from "@/components/ui/button";
|
|
import { useAutoRefresh } from "@/hooks/use-auto-refresh";
|
|
import { tr } from "@/i18n/tr";
|
|
import { formatDate } from "@/shared/format";
|
|
import { SHOPPING_REFRESH_MS } from "@/shared/shopping";
|
|
import type { ShoppingListSummary } from "../service";
|
|
import { ShoppingListDialog } from "./list-dialog";
|
|
|
|
const t = tr.shopping;
|
|
|
|
function ListCard({ list }: { list: ShoppingListSummary }) {
|
|
const done = list.status === "completed";
|
|
const Icon = done ? CheckCircle2 : ListChecks;
|
|
return (
|
|
<li>
|
|
<Link
|
|
href={`/alisveris/${list.id}`}
|
|
className="flex h-full flex-col gap-2 rounded-xl border bg-card p-4 transition-colors hover:bg-accent/40 focus-visible:ring-2 focus-visible:ring-ring focus-visible:outline-none"
|
|
>
|
|
<div className="flex items-start gap-3">
|
|
<span className="flex size-9 shrink-0 items-center justify-center rounded-lg bg-muted">
|
|
<Icon className="size-4.5 text-muted-foreground" aria-hidden />
|
|
</span>
|
|
<div className="min-w-0 flex-1">
|
|
<p className="truncate font-medium">{list.name}</p>
|
|
<p className="text-xs text-muted-foreground tabular-nums">
|
|
{t.itemCount(list.itemCount)}
|
|
{list.checkedCount && !done
|
|
? ` · ${t.checkedCount(list.checkedCount, list.itemCount)}`
|
|
: ""}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
{list.preview.length ? (
|
|
<p className="line-clamp-2 text-sm text-muted-foreground">
|
|
{list.preview.join(", ")}
|
|
{list.itemCount > list.preview.length ? "…" : ""}
|
|
</p>
|
|
) : (
|
|
<p className="text-sm text-muted-foreground">{t.emptyList}</p>
|
|
)}
|
|
<p className="mt-auto text-xs text-muted-foreground">
|
|
{done && list.completedAt
|
|
? t.completedAt(formatDate(list.completedAt))
|
|
: t.updated(formatDate(list.updatedAt))}
|
|
</p>
|
|
</Link>
|
|
</li>
|
|
);
|
|
}
|
|
|
|
/** /alisveris: aktif ve tamamlanan listeler, yeni liste, bekleyen stok önerileri. */
|
|
export function ShoppingListsView({
|
|
active,
|
|
completed,
|
|
suggestions,
|
|
canWrite,
|
|
}: {
|
|
active: ShoppingListSummary[];
|
|
completed: ShoppingListSummary[];
|
|
suggestions: number;
|
|
canWrite: boolean;
|
|
}) {
|
|
const [creating, setCreating] = useState(false);
|
|
useAutoRefresh(SHOPPING_REFRESH_MS);
|
|
|
|
return (
|
|
<div className="flex flex-col gap-6">
|
|
{suggestions > 0 ? (
|
|
<p className="flex items-start gap-2 rounded-lg border border-amber-500/40 bg-amber-500/5 p-3 text-sm">
|
|
<Sparkles className="mt-0.5 size-4 shrink-0 text-amber-600" aria-hidden />
|
|
{t.suggestionsBanner(suggestions)}
|
|
</p>
|
|
) : null}
|
|
<section className="flex flex-col gap-3">
|
|
<div className="flex items-center gap-3">
|
|
<h2 className="text-sm font-medium text-muted-foreground">{t.active}</h2>
|
|
{canWrite ? (
|
|
<Button size="sm" className="ml-auto" onClick={() => setCreating(true)}>
|
|
<Plus />
|
|
{t.newList}
|
|
</Button>
|
|
) : null}
|
|
</div>
|
|
{active.length ? (
|
|
<ul className="grid gap-3 md:grid-cols-2 xl:grid-cols-3">
|
|
{active.map((l) => (
|
|
<ListCard key={l.id} list={l} />
|
|
))}
|
|
</ul>
|
|
) : (
|
|
<p className="rounded-lg border border-dashed p-6 text-center text-sm text-muted-foreground">
|
|
{t.emptyActive}
|
|
</p>
|
|
)}
|
|
</section>
|
|
{completed.length ? (
|
|
<section className="flex flex-col gap-3">
|
|
<h2 className="text-sm font-medium text-muted-foreground">{t.completed}</h2>
|
|
<ul className="grid gap-3 md:grid-cols-2 xl:grid-cols-3">
|
|
{completed.map((l) => (
|
|
<ListCard key={l.id} list={l} />
|
|
))}
|
|
</ul>
|
|
</section>
|
|
) : null}
|
|
{creating ? <ShoppingListDialog list={null} onClose={() => setCreating(false)} /> : null}
|
|
</div>
|
|
);
|
|
}
|