fix(reports): throttle export actions after queueing

This commit is contained in:
2026-04-27 20:52:17 +03:30
parent 803c3ce629
commit 858aa977f7
2 changed files with 56 additions and 4 deletions

View File

@@ -98,6 +98,7 @@ export function ReportsTablePanel({
openDay,
onToggleDay,
onExport,
exportState,
labels,
}: {
data: TableReportResponse | null;
@@ -105,6 +106,10 @@ export function ReportsTablePanel({
openDay: string | null;
onToggleDay: (day: string) => void;
onExport: (type: "excel" | "pdf") => void;
exportState: {
excel: { pending: boolean; cooldownSeconds: number };
pdf: { pending: boolean; cooldownSeconds: number };
};
labels: Record<string, string>;
}) {
const { lang } = useTranslation();
@@ -117,18 +122,28 @@ export function ReportsTablePanel({
<button
type="button"
onClick={() => onExport("excel")}
className="inline-flex h-11 items-center justify-center gap-2 rounded-2xl border border-emerald-200 bg-emerald-50 px-4 text-sm font-medium text-emerald-700 transition hover:bg-emerald-100 dark:border-emerald-500/30 dark:bg-emerald-500/10 dark:text-emerald-300"
disabled={exportState.excel.pending || exportState.excel.cooldownSeconds > 0}
className="inline-flex h-11 items-center justify-center gap-2 rounded-2xl border border-emerald-200 bg-emerald-50 px-4 text-sm font-medium text-emerald-700 transition hover:bg-emerald-100 disabled:cursor-not-allowed disabled:opacity-60 dark:border-emerald-500/30 dark:bg-emerald-500/10 dark:text-emerald-300"
>
<FileSpreadsheet className="h-4 w-4" />
{labels.exportExcel}
{exportState.excel.pending
? labels.exportExcel
: exportState.excel.cooldownSeconds > 0
? `${labels.exportExcel} (${localizeDigits(String(exportState.excel.cooldownSeconds), lang)})`
: labels.exportExcel}
</button>
<button
type="button"
onClick={() => onExport("pdf")}
className="inline-flex h-11 items-center justify-center gap-2 rounded-2xl border border-rose-200 bg-rose-50 px-4 text-sm font-medium text-rose-700 transition hover:bg-rose-100 dark:border-rose-500/30 dark:bg-rose-500/10 dark:text-rose-300"
disabled={exportState.pdf.pending || exportState.pdf.cooldownSeconds > 0}
className="inline-flex h-11 items-center justify-center gap-2 rounded-2xl border border-rose-200 bg-rose-50 px-4 text-sm font-medium text-rose-700 transition hover:bg-rose-100 disabled:cursor-not-allowed disabled:opacity-60 dark:border-rose-500/30 dark:bg-rose-500/10 dark:text-rose-300"
>
<FileText className="h-4 w-4" />
{labels.exportPdf}
{exportState.pdf.pending
? labels.exportPdf
: exportState.pdf.cooldownSeconds > 0
? `${labels.exportPdf} (${localizeDigits(String(exportState.pdf.cooldownSeconds), lang)})`
: labels.exportPdf}
</button>
</div>

View File

@@ -96,6 +96,10 @@ export default function Reports() {
const [dayDetails, setDayDetails] = useState<DayDetailsResponse | null>(null);
const [openDay, setOpenDay] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [exportState, setExportState] = useState({
excel: { pending: false, cooldownSeconds: 0 },
pdf: { pending: false, cooldownSeconds: 0 },
});
const canSelectUsers = canWorkspace(activeWorkspace?.my_role, WORKSPACE_MEMBERS_VIEW);
@@ -188,12 +192,44 @@ export default function Reports() {
}
};
useEffect(() => {
const interval = window.setInterval(() => {
setExportState((current) => ({
excel: {
pending: current.excel.pending,
cooldownSeconds: Math.max(current.excel.cooldownSeconds - 1, 0),
},
pdf: {
pending: current.pdf.pending,
cooldownSeconds: Math.max(current.pdf.cooldownSeconds - 1, 0),
},
}));
}, 1000);
return () => window.clearInterval(interval);
}, []);
const handleExport = async (type: "excel" | "pdf") => {
if (!apiFilters) return;
if (exportState[type].pending || exportState[type].cooldownSeconds > 0) return;
setExportState((current) => ({
...current,
[type]: { pending: true, cooldownSeconds: 0 },
}));
try {
await createReportExport(apiFilters, type, lang);
setExportState((current) => ({
...current,
[type]: { pending: false, cooldownSeconds: 60 },
}));
toast.success(t.reports?.exportQueued || "Export queued. You will receive a notification with the download link.");
} catch {
setExportState((current) => ({
...current,
[type]: { pending: false, cooldownSeconds: 0 },
}));
toast.error(t.reports?.exportError || "Failed to queue report export.");
}
};
@@ -310,6 +346,7 @@ export default function Reports() {
openDay={openDay}
onToggleDay={(day) => void handleToggleDay(day)}
onExport={(type) => void handleExport(type)}
exportState={exportState}
labels={{
exportExcel: t.reports?.exportExcel || "Export Excel",
exportPdf: t.reports?.exportPdf || "Export PDF",