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

100 lines
3.5 KiB
TypeScript

"use client";
import { useTranslation } from "react-i18next";
import { Label } from "@kandev/ui/label";
import { RadioGroup, RadioGroupItem } from "@kandev/ui/radio-group";
import type { StartupPage } from "@/lib/types/http";
import { GENERAL_SETTINGS_TARGETS } from "@/lib/settings-discovery/catalog/preferences";
import { useSettingsTargetRegistration } from "./settings-target-provider";
/**
* Holds catalog KEYS, not copy. A module-scope table is evaluated once at import,
* so a `t()` call here would freeze at the boot locale and never update on a
* switch — and the lint guard cannot see literals in a SCREAMING_CASE constant,
* so nothing would flag it. Resolved at render in the component below.
*/
const OPTIONS: Array<{ value: StartupPage; labelKey: string; descriptionKey: string }> = [
{
value: "task_overview",
labelKey: "settings:taskOverview",
descriptionKey: "settings:startOnYourSavedKanbanPipelineOrList",
},
{
value: "last_task",
labelKey: "settings:lastVisitedTask",
descriptionKey: "settings:resumeTheMostRecentlyOpenedTask",
},
{
value: "threads",
labelKey: "kanban:threads",
descriptionKey: "settings:openThreadsOnStartupAndHome",
},
];
export function StartupPageSettingsCard({
value,
isDirty,
onChange,
}: {
value: StartupPage;
isDirty: boolean;
onChange: (value: StartupPage) => void;
}) {
const { t } = useTranslation();
const registerTarget = useSettingsTargetRegistration(GENERAL_SETTINGS_TARGETS.startupPage);
return (
<div
ref={registerTarget}
className="space-y-4 py-3"
data-testid="startup-page-settings-card"
data-settings-dirty={isDirty}
>
<div>
<h4 className="text-sm font-semibold">{t("settings:openKandevTo")}</h4>
<p className="text-xs text-muted-foreground">{t("settings:thisAppliesWhenKandevStarts")}</p>
</div>
<RadioGroup
aria-label={t("settings:startupPage")}
value={value}
onValueChange={(next) => onChange(next as StartupPage)}
data-settings-dirty={isDirty}
className="gap-3"
>
{OPTIONS.map((option) => {
const labelId = `startup-page-${option.value}-label`;
const descriptionId = `startup-page-${option.value}-description`;
const selected = value === option.value;
return (
<Label
key={option.value}
htmlFor={`startup-page-${option.value}`}
className={`flex min-h-11 w-full min-w-0 cursor-pointer items-start gap-3 rounded-md border p-3 transition-colors ${
selected ? "border-primary bg-primary/5" : "border-border hover:bg-muted/30"
}`}
>
<RadioGroupItem
id={`startup-page-${option.value}`}
value={option.value}
aria-labelledby={labelId}
aria-describedby={descriptionId}
className="mt-0.5 border border-muted-foreground/80 data-[state=checked]:border-primary"
/>
<span className="min-w-0 space-y-1">
<span id={labelId} className="block text-sm font-medium">
{t(option.labelKey)}
</span>
<span
id={descriptionId}
className="block whitespace-normal break-words text-xs text-muted-foreground"
>
{t(option.descriptionKey)}
</span>
</span>
</Label>
);
})}
</RadioGroup>
</div>
);
}