Files
claudemesh/apps/web/src/modules/common/hooks/use-ai-error.tsx
Alejandro Gutiérrez d3163a5bff feat(db): mesh data model — meshes, members, invites, audit log
- pgSchema "mesh" with 4 tables isolating the peer mesh domain
- Enums: visibility, transport, tier, role
- audit_log is metadata-only (E2E encryption enforced at broker/client)
- Cascade on mesh delete, soft-delete via archivedAt/revokedAt

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 21:19:32 +01:00

124 lines
3.2 KiB
TypeScript

import { toast } from "sonner";
import { isAPIError } from "@turbostarter/api/utils";
import { useTranslation } from "@turbostarter/i18n";
import { cn } from "@turbostarter/ui";
import { buttonVariants } from "@turbostarter/ui-web/button";
import { Icons } from "@turbostarter/ui-web/icons";
const InsufficientCredits = () => {
const { t } = useTranslation("common");
const list = [
t("credits.description", { count: 5 }),
t("credits.description2"),
t("credits.description3"),
];
return (
<div className="flex gap-2">
<Icons.BanknoteX className="size-5" />
<div className="flex flex-col gap-6">
<span className="leading-tight font-medium">
{t("error.insufficientCredits")}
</span>
<ul className="flex flex-col gap-2">
{list.map((item) => (
<li key={item} className="flex items-center gap-2.5">
<div className="bg-success size-2 rounded-full"></div>
<span className="leading-tight">{item}</span>
</li>
))}
</ul>
<a
href="https://turbostarter.dev/ai#pricing"
target="_blank"
className={cn(
buttonVariants({ variant: "outline" }),
"group/button gap-2",
)}
>
{t("credits.cta")}{" "}
<Icons.ArrowRight className="size-4 transition-all group-hover/button:translate-x-1" />
</a>
</div>
</div>
);
};
const RateLimit = () => {
const { t } = useTranslation("common");
const list = [
t("rateLimit.description"),
t("rateLimit.description2"),
t("rateLimit.description3"),
];
return (
<div className="flex gap-2">
<Icons.ClockAlert className="size-5" />
<div className="flex flex-col gap-6">
<span className="leading-tight font-medium">
{t("error.rateLimit")}
</span>
<ul className="flex flex-col gap-2">
{list.map((item) => (
<li key={item} className="flex items-center gap-2.5">
<div className="bg-success size-2 rounded-full"></div>
<span className="leading-tight">{item}</span>
</li>
))}
</ul>
<a
href="https://turbostarter.dev/ai#pricing"
target="_blank"
className={cn(
buttonVariants({ variant: "outline" }),
"group/button gap-2",
)}
>
{t("rateLimit.cta")}{" "}
<Icons.ArrowRight className="size-4 transition-all group-hover/button:translate-x-1" />
</a>
</div>
</div>
);
};
const parseError = (error: Error) => {
try {
const parsed = JSON.parse(error.message);
return parsed;
} catch {
return error.message;
}
};
export const useAIError = () => {
const { t } = useTranslation("common");
const onError = (error: Error) => {
console.error(error);
const parsed = parseError(error);
if (!isAPIError(parsed)) {
return toast.error(error.message || t("error.general"));
}
if (parsed.code === "error.insufficientCredits") {
return toast(<InsufficientCredits />);
}
if (parsed.code === "error.rateLimit") {
return toast(<RateLimit />);
}
};
return { onError };
};