fix(reports): throttle export actions after queueing
This commit is contained in:
@@ -98,6 +98,7 @@ export function ReportsTablePanel({
|
|||||||
openDay,
|
openDay,
|
||||||
onToggleDay,
|
onToggleDay,
|
||||||
onExport,
|
onExport,
|
||||||
|
exportState,
|
||||||
labels,
|
labels,
|
||||||
}: {
|
}: {
|
||||||
data: TableReportResponse | null;
|
data: TableReportResponse | null;
|
||||||
@@ -105,6 +106,10 @@ export function ReportsTablePanel({
|
|||||||
openDay: string | null;
|
openDay: string | null;
|
||||||
onToggleDay: (day: string) => void;
|
onToggleDay: (day: string) => void;
|
||||||
onExport: (type: "excel" | "pdf") => void;
|
onExport: (type: "excel" | "pdf") => void;
|
||||||
|
exportState: {
|
||||||
|
excel: { pending: boolean; cooldownSeconds: number };
|
||||||
|
pdf: { pending: boolean; cooldownSeconds: number };
|
||||||
|
};
|
||||||
labels: Record<string, string>;
|
labels: Record<string, string>;
|
||||||
}) {
|
}) {
|
||||||
const { lang } = useTranslation();
|
const { lang } = useTranslation();
|
||||||
@@ -117,18 +122,28 @@ export function ReportsTablePanel({
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => onExport("excel")}
|
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" />
|
<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>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => onExport("pdf")}
|
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" />
|
<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>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -96,6 +96,10 @@ export default function Reports() {
|
|||||||
const [dayDetails, setDayDetails] = useState<DayDetailsResponse | null>(null);
|
const [dayDetails, setDayDetails] = useState<DayDetailsResponse | null>(null);
|
||||||
const [openDay, setOpenDay] = useState<string | null>(null);
|
const [openDay, setOpenDay] = useState<string | null>(null);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
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);
|
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") => {
|
const handleExport = async (type: "excel" | "pdf") => {
|
||||||
if (!apiFilters) return;
|
if (!apiFilters) return;
|
||||||
|
if (exportState[type].pending || exportState[type].cooldownSeconds > 0) return;
|
||||||
|
|
||||||
|
setExportState((current) => ({
|
||||||
|
...current,
|
||||||
|
[type]: { pending: true, cooldownSeconds: 0 },
|
||||||
|
}));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await createReportExport(apiFilters, type, lang);
|
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.");
|
toast.success(t.reports?.exportQueued || "Export queued. You will receive a notification with the download link.");
|
||||||
} catch {
|
} catch {
|
||||||
|
setExportState((current) => ({
|
||||||
|
...current,
|
||||||
|
[type]: { pending: false, cooldownSeconds: 0 },
|
||||||
|
}));
|
||||||
toast.error(t.reports?.exportError || "Failed to queue report export.");
|
toast.error(t.reports?.exportError || "Failed to queue report export.");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -310,6 +346,7 @@ export default function Reports() {
|
|||||||
openDay={openDay}
|
openDay={openDay}
|
||||||
onToggleDay={(day) => void handleToggleDay(day)}
|
onToggleDay={(day) => void handleToggleDay(day)}
|
||||||
onExport={(type) => void handleExport(type)}
|
onExport={(type) => void handleExport(type)}
|
||||||
|
exportState={exportState}
|
||||||
labels={{
|
labels={{
|
||||||
exportExcel: t.reports?.exportExcel || "Export Excel",
|
exportExcel: t.reports?.exportExcel || "Export Excel",
|
||||||
exportPdf: t.reports?.exportPdf || "Export PDF",
|
exportPdf: t.reports?.exportPdf || "Export PDF",
|
||||||
|
|||||||
Reference in New Issue
Block a user