fix: tighten cron session deletion UX
This commit is contained in:
@@ -8,8 +8,11 @@ import {
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
} from "@/components/ui/alert-dialog";
|
||||
import type { TFunction } from "i18next";
|
||||
import { Trash2 } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { currentLocale } from "@/i18n";
|
||||
import { fmtDateTime } from "@/lib/format";
|
||||
import type { SessionAutomationJob } from "@/lib/types";
|
||||
|
||||
interface DeleteConfirmProps {
|
||||
@@ -28,6 +31,7 @@ export function DeleteConfirm({
|
||||
onConfirm,
|
||||
}: DeleteConfirmProps) {
|
||||
const { t } = useTranslation();
|
||||
const locale = currentLocale();
|
||||
const hasAutomations = automations.length > 0;
|
||||
const visibleAutomations = automations.slice(0, 4);
|
||||
const hiddenCount = Math.max(0, automations.length - visibleAutomations.length);
|
||||
@@ -49,23 +53,29 @@ export function DeleteConfirm({
|
||||
{hasAutomations
|
||||
? t("deleteConfirm.automationsDescription", {
|
||||
count: automations.length,
|
||||
defaultValue:
|
||||
"This chat has scheduled automations. Deleting it will also delete them.",
|
||||
})
|
||||
: t("deleteConfirm.description")}
|
||||
</AlertDialogDescription>
|
||||
{hasAutomations ? (
|
||||
<div className="mt-4 max-h-32 w-full overflow-y-auto rounded-2xl bg-muted/55 px-3 py-2 text-left">
|
||||
<div className="mt-4 max-h-40 w-full overflow-y-auto rounded-2xl bg-muted/55 px-3 py-2 text-left">
|
||||
{visibleAutomations.map((job) => (
|
||||
<div key={job.id} className="truncate text-[13px] leading-6 text-foreground">
|
||||
{job.name || job.id}
|
||||
<div key={job.id} className="min-w-0 py-1.5">
|
||||
<div className="truncate text-[13px] font-medium leading-5 text-foreground">
|
||||
{job.name || job.id}
|
||||
</div>
|
||||
<div className="mt-0.5 flex min-w-0 flex-wrap items-center gap-x-2 gap-y-0.5 text-[11.5px] leading-5 text-muted-foreground">
|
||||
<span className="truncate">
|
||||
{formatAutomationSchedule(job, t, locale)}
|
||||
</span>
|
||||
<span aria-hidden>·</span>
|
||||
<span className="truncate">{formatAutomationNextRun(job, t, locale)}</span>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
{hiddenCount > 0 ? (
|
||||
<div className="text-[13px] leading-6 text-muted-foreground">
|
||||
{t("deleteConfirm.moreAutomations", {
|
||||
count: hiddenCount,
|
||||
defaultValue: "+ {{count}} more",
|
||||
})}
|
||||
</div>
|
||||
) : null}
|
||||
@@ -85,7 +95,7 @@ export function DeleteConfirm({
|
||||
>
|
||||
{hasAutomations
|
||||
? t("deleteConfirm.confirmWithAutomations", {
|
||||
defaultValue: "Delete all",
|
||||
count: automations.length,
|
||||
})
|
||||
: t("deleteConfirm.confirm")}
|
||||
</AlertDialogAction>
|
||||
@@ -94,3 +104,63 @@ export function DeleteConfirm({
|
||||
</AlertDialog>
|
||||
);
|
||||
}
|
||||
|
||||
function formatAutomationSchedule(
|
||||
job: SessionAutomationJob,
|
||||
t: TFunction,
|
||||
locale: string,
|
||||
): string {
|
||||
if (job.schedule.kind === "at" && job.schedule.at_ms) {
|
||||
return t("deleteConfirm.schedule.at", { time: fmtDateTime(job.schedule.at_ms, locale) });
|
||||
}
|
||||
if (job.schedule.kind === "every" && job.schedule.every_ms) {
|
||||
return t("deleteConfirm.schedule.every", {
|
||||
duration: formatDuration(job.schedule.every_ms, locale),
|
||||
});
|
||||
}
|
||||
if (job.schedule.kind === "cron" && job.schedule.expr) {
|
||||
return job.schedule.tz
|
||||
? t("deleteConfirm.schedule.cronWithTz", {
|
||||
expr: job.schedule.expr,
|
||||
tz: job.schedule.tz,
|
||||
})
|
||||
: t("deleteConfirm.schedule.cron", { expr: job.schedule.expr });
|
||||
}
|
||||
return t("deleteConfirm.schedule.unknown");
|
||||
}
|
||||
|
||||
function formatAutomationNextRun(
|
||||
job: SessionAutomationJob,
|
||||
t: TFunction,
|
||||
locale: string,
|
||||
): string {
|
||||
if (!job.enabled) return t("deleteConfirm.next.disabled");
|
||||
const next = job.state.next_run_at_ms;
|
||||
if (!next) return t("deleteConfirm.next.none");
|
||||
return t("deleteConfirm.next.label", { time: fmtDateTime(next, locale) });
|
||||
}
|
||||
|
||||
function formatDuration(ms: number, locale: string): string {
|
||||
const units: Array<[Intl.NumberFormatOptions["unit"], number]> = [
|
||||
["day", 86_400_000],
|
||||
["hour", 3_600_000],
|
||||
["minute", 60_000],
|
||||
["second", 1000],
|
||||
];
|
||||
for (const [unit, size] of units) {
|
||||
if (ms >= size && ms % size === 0) {
|
||||
return new Intl.NumberFormat(locale, {
|
||||
style: "unit",
|
||||
unit,
|
||||
unitDisplay: "long",
|
||||
maximumFractionDigits: 0,
|
||||
}).format(ms / size);
|
||||
}
|
||||
}
|
||||
return new Intl.NumberFormat(locale, {
|
||||
style: "unit",
|
||||
unit: "minute",
|
||||
unitDisplay: "long",
|
||||
maximumFractionDigits: 1,
|
||||
}).format(ms / 60_000);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user