diff --git a/app/portal/page.tsx b/app/portal/page.tsx
index db6afde..2914d3d 100644
--- a/app/portal/page.tsx
+++ b/app/portal/page.tsx
@@ -1,8 +1,9 @@
import { Card, CardContent, Badge } from "poyraz-ui/atoms";
-import { FolderKanban, CheckCircle2, Clock, BarChart, type LucideIcon } from "lucide-react";
+import { FolderKanban, CheckCircle2, Clock, BarChart } from "lucide-react";
import Link from "next/link";
import { format } from "date-fns";
import { tr } from "date-fns/locale";
+import { StatCard } from "@/components/system/stat-card";
import { requirePortalBackend } from "@/server/web/portal";
export default async function PortalDashboardPage() {
@@ -76,29 +77,3 @@ export default async function PortalDashboardPage() {
);
}
-
-function StatCard({ label, value, icon: Icon, tone }: {
- label: string;
- value: string;
- icon: LucideIcon;
- tone: "green" | "blue" | "amber";
-}) {
- const toneClass = {
- green: "bg-emerald-50 text-emerald-700",
- blue: "bg-blue-50 text-blue-700",
- amber: "bg-amber-50 text-amber-700",
- }[tone];
- return (
-
-
-
-
-
-
-
-
- );
-}
diff --git a/components/error-toaster.tsx b/components/error-toaster.tsx
index 816d580..ee4f356 100644
--- a/components/error-toaster.tsx
+++ b/components/error-toaster.tsx
@@ -6,7 +6,7 @@ import { toast } from "poyraz-ui/molecules";
export function ErrorToaster({ message }: { message: string }) {
useEffect(() => {
if (message) {
- toast.error(message);
+ toast.error(message, { id: `route-error:${message}` });
}
}, [message])
diff --git a/scripts/phase4-ui-boundary.mjs b/scripts/phase4-ui-boundary.mjs
index b476bd3..a9079e1 100644
--- a/scripts/phase4-ui-boundary.mjs
+++ b/scripts/phase4-ui-boundary.mjs
@@ -7,6 +7,10 @@ const repoRoot = process.cwd();
const packageJson = JSON.parse(fs.readFileSync(path.join(repoRoot, "package.json"), "utf8"));
const globalsCss = fs.readFileSync(path.join(repoRoot, "app/globals.css"), "utf8");
const appShell = fs.readFileSync(path.join(repoRoot, "components/layout/app-shell.tsx"), "utf8");
+const errorToaster = fs.readFileSync(
+ path.join(repoRoot, "components/error-toaster.tsx"),
+ "utf8",
+);
const loginActions = fs.readFileSync(path.join(repoRoot, "app/login/actions.ts"), "utf8");
const rootLayout = fs.readFileSync(path.join(repoRoot, "app/layout.tsx"), "utf8");
const brandingService = fs.readFileSync(
@@ -68,6 +72,11 @@ assert.doesNotMatch(
/]*\binteractiveMotion=/s,
"Poyraz UI 3.0.2 leaks DropdownMenuItem interactiveMotion to the DOM",
);
+assert.match(
+ errorToaster,
+ /toast\.error\(message,\s*\{\s*id:/,
+ "Route error toasts must use a stable id so React development effects cannot duplicate them",
+);
assert.doesNotMatch(
appShell,
/