Files
meezi/web/dashboard/src/components/demo/demo-data-banner.tsx
T
soroush.asadi 75d5bbc84a fix(i18n): localize API error messages by code (no more raw English)
Error toasts surfaced the raw English backend message. Added an errors namespace (fa/ar/en) keyed by error code + a useApiError() resolver that maps ApiClientError.code to the localized message (fallback to a localized generic). Wired into menu, tables, demo banner, and subscription checkout; hardened getErrorMessage so it never returns the raw backend message.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 00:04:48 +03:30

114 lines
3.7 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useState } from "react";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { Sparkles, Loader2 } from "lucide-react";
import { apiPost } from "@/lib/api/client";
import { notify } from "@/lib/notify";
import { useApiError } from "@/lib/use-api-error";
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 apiError = useApiError();
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 });
}
},
onError: (err) => {
notify.error(apiError(err));
},
});
if (!cafeId || (role !== "Owner" && role !== "Manager")) return null;
if (done && summary) {
const nothingAdded =
summary.categoriesAdded === 0 &&
summary.itemsAdded === 0 &&
summary.tablesAdded === 0 &&
summary.ingredientsAdded === 0 &&
!summary.taxCreated;
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>
{nothingAdded ? (
"همه داده‌های نمونه از قبل موجود بودند — موردی اضافه نشد."
) : (
<>
دادههای نمونه اضافه شد {summary.categoriesAdded} دسته،{" "}
{summary.itemsAdded} آیتم، {summary.tablesAdded} میز،{" "}
{summary.ingredientsAdded} ماده اولیه
{summary.taxCreated ? "، مالیات ۹٪" : ""}.
</>
)}
</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>
);
}