Files
kandev/apps/web/components/settings/unsaved-indicator.tsx
T
2026-09-22 21:24:43 +01:00

59 lines
1.6 KiB
TypeScript

"use client";
import { useTranslation } from "react-i18next";
import { IconAlertCircle } from "@tabler/icons-react";
import { Button } from "@kandev/ui/button";
import { RequestIndicator } from "@/components/request-indicator";
type UnsavedSaveButtonProps = {
isDirty: boolean;
isLoading: boolean;
status: "idle" | "loading" | "success" | "error";
onClick: () => void;
cleanLabel?: string;
disabled?: boolean;
};
const dirtyButtonClass = "border-success/60 text-success hover:bg-success/10";
export function UnsavedChangesBadge() {
const { t } = useTranslation();
return (
<span role="status" className="text-xs text-success">
{t("common:unsavedChanges")}
</span>
);
}
export function UnsavedSaveButton({
isDirty,
isLoading,
status,
onClick,
disabled,
cleanLabel,
}: UnsavedSaveButtonProps) {
const { t } = useTranslation();
// Defaulted here rather than in the parameter list: `t()` must resolve at
// render, and a default expression in the signature runs before the hook.
const resolvedCleanLabel = cleanLabel ?? t("common:save");
return (
<Button
type="button"
size="default"
variant={isDirty ? "secondary" : "outline"}
onClick={onClick}
disabled={isLoading || Boolean(disabled)}
className={isDirty ? dirtyButtonClass : "cursor-pointer"}
>
{isDirty && <IconAlertCircle className="h-4 w-4 mr-2" />}
{isDirty ? t("common:save") : resolvedCleanLabel}
{status !== "idle" && (
<span className="ml-2">
<RequestIndicator status={status} />
</span>
)}
</Button>
);
}