2026-06-01 00:27:34 +03:30
|
|
|
|
"use client";
|
|
|
|
|
|
|
|
|
|
|
|
import { useState } from "react";
|
|
|
|
|
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
|
|
|
|
import { Sparkles, Loader2 } from "lucide-react";
|
2026-06-01 23:23:39 +03:30
|
|
|
|
import { ApiClientError, apiPost } from "@/lib/api/client";
|
|
|
|
|
|
import { notify } from "@/lib/notify";
|
2026-06-01 00:27:34 +03:30
|
|
|
|
import { useAuthStore } from "@/lib/stores/auth.store";
|
|
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
|
|
|
|
import { cn } from "@/lib/utils";
|
|
|
|
|
|
|
|
|
|
|
|
interface DemoSeedResult {
|
|
|
|
|
|
categoriesAdded: number;
|
|
|
|
|
|
itemsAdded: number;
|
|
|
|
|
|
tablesAdded: number;
|
|
|
|
|
|
ingredientsAdded: number;
|
|
|
|
|
|
taxCreated: boolean;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
interface Props {
|
|
|
|
|
|
/** Which queries to invalidate after seeding. */
|
|
|
|
|
|
invalidateKeys: string[][];
|
|
|
|
|
|
className?: string;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export function DemoDataBanner({ invalidateKeys, className }: Props) {
|
|
|
|
|
|
const cafeId = useAuthStore((s) => s.user?.cafeId);
|
|
|
|
|
|
const role = useAuthStore((s) => s.user?.role);
|
|
|
|
|
|
const qc = useQueryClient();
|
|
|
|
|
|
const [done, setDone] = useState(false);
|
|
|
|
|
|
const [summary, setSummary] = useState<DemoSeedResult | null>(null);
|
|
|
|
|
|
|
|
|
|
|
|
const seed = useMutation({
|
|
|
|
|
|
mutationFn: () =>
|
|
|
|
|
|
apiPost<DemoSeedResult>(`/api/cafes/${cafeId}/demo/seed`, {}),
|
|
|
|
|
|
onSuccess: (result) => {
|
|
|
|
|
|
setSummary(result);
|
|
|
|
|
|
setDone(true);
|
|
|
|
|
|
for (const key of invalidateKeys) {
|
|
|
|
|
|
qc.invalidateQueries({ queryKey: key });
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
2026-06-01 23:23:39 +03:30
|
|
|
|
onError: (err) => {
|
|
|
|
|
|
notify.error(
|
|
|
|
|
|
err instanceof ApiClientError
|
|
|
|
|
|
? err.message
|
|
|
|
|
|
: "افزودن دادههای نمونه ناموفق بود. دوباره تلاش کنید."
|
|
|
|
|
|
);
|
|
|
|
|
|
},
|
2026-06-01 00:27:34 +03:30
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (!cafeId || (role !== "Owner" && role !== "Manager")) return null;
|
|
|
|
|
|
if (done && summary) {
|
2026-06-01 18:23:31 +03:30
|
|
|
|
const nothingAdded =
|
|
|
|
|
|
summary.categoriesAdded === 0 &&
|
|
|
|
|
|
summary.itemsAdded === 0 &&
|
|
|
|
|
|
summary.tablesAdded === 0 &&
|
|
|
|
|
|
summary.ingredientsAdded === 0 &&
|
|
|
|
|
|
!summary.taxCreated;
|
2026-06-01 00:27:34 +03:30
|
|
|
|
return (
|
|
|
|
|
|
<div
|
|
|
|
|
|
className={cn(
|
|
|
|
|
|
"flex items-center gap-3 rounded-xl border border-[#0F6E56]/30 bg-[#E1F5EE] px-4 py-3 text-sm text-[#0F6E56]",
|
|
|
|
|
|
className
|
|
|
|
|
|
)}
|
|
|
|
|
|
>
|
|
|
|
|
|
<Sparkles className="size-4 shrink-0" />
|
|
|
|
|
|
<span>
|
2026-06-01 18:23:31 +03:30
|
|
|
|
{nothingAdded ? (
|
|
|
|
|
|
"همه دادههای نمونه از قبل موجود بودند — موردی اضافه نشد."
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<>
|
|
|
|
|
|
دادههای نمونه اضافه شد — {summary.categoriesAdded} دسته،{" "}
|
|
|
|
|
|
{summary.itemsAdded} آیتم، {summary.tablesAdded} میز،{" "}
|
|
|
|
|
|
{summary.ingredientsAdded} ماده اولیه
|
|
|
|
|
|
{summary.taxCreated ? "، مالیات ۹٪" : ""}.
|
|
|
|
|
|
</>
|
|
|
|
|
|
)}
|
2026-06-01 00:27:34 +03:30
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div
|
|
|
|
|
|
className={cn(
|
|
|
|
|
|
"flex flex-col gap-3 rounded-xl border border-dashed border-[#0F6E56]/40 bg-[#E1F5EE]/40 px-5 py-4 sm:flex-row sm:items-center sm:justify-between",
|
|
|
|
|
|
className
|
|
|
|
|
|
)}
|
|
|
|
|
|
>
|
|
|
|
|
|
<div className="flex items-center gap-3">
|
|
|
|
|
|
<Sparkles className="size-5 shrink-0 text-[#0F6E56]" />
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<p className="text-sm font-semibold text-[#0F6E56]">شروع سریع با دادههای نمونه</p>
|
|
|
|
|
|
<p className="text-xs text-muted-foreground mt-0.5">
|
|
|
|
|
|
۷ دسته، ۵۹+ آیتم منو، ۱۰ میز، ۱۵ ماده اولیه و مالیات ۹٪ بهصورت خودکار اضافه میشود.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
size="sm"
|
|
|
|
|
|
className="shrink-0 bg-[#0F6E56] hover:bg-[#0c5a46]"
|
|
|
|
|
|
disabled={seed.isPending}
|
|
|
|
|
|
onClick={() => seed.mutate()}
|
|
|
|
|
|
>
|
|
|
|
|
|
{seed.isPending ? (
|
|
|
|
|
|
<Loader2 className="me-1.5 size-4 animate-spin" />
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<Sparkles className="me-1.5 size-4" />
|
|
|
|
|
|
)}
|
|
|
|
|
|
افزودن دادههای نمونه
|
|
|
|
|
|
</Button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|