From a28a766f8b411a7ee92bc6d39743ecfd47263b85 Mon Sep 17 00:00:00 2001 From: Arunavo Ray Date: Sat, 24 May 2025 20:12:27 +0530 Subject: [PATCH] refactor: update cleanup and schedule config to use seconds for retentionDays and improve nextRun calculation --- src/components/config/ConfigTabs.tsx | 34 +++++++- .../config/DatabaseCleanupConfigForm.tsx | 85 +++++++++++++++---- src/components/config/ScheduleConfigForm.tsx | 13 +-- src/lib/cleanup-service.ts | 49 ++++++++--- src/lib/db/schema.ts | 2 +- src/pages/api/config/index.ts | 68 +++++++++++++-- src/types/config.ts | 2 +- 7 files changed, 212 insertions(+), 41 deletions(-) diff --git a/src/components/config/ConfigTabs.tsx b/src/components/config/ConfigTabs.tsx index aa57404..e6a0899 100644 --- a/src/components/config/ConfigTabs.tsx +++ b/src/components/config/ConfigTabs.tsx @@ -53,7 +53,7 @@ export function ConfigTabs() { }, cleanupConfig: { enabled: false, - retentionDays: 7, + retentionDays: 604800, // 7 days in seconds }, }); const { user, refreshUser } = useAuth(); @@ -181,6 +181,22 @@ export function ConfigTabs() { // Removed refreshUser() call to prevent page reload // Invalidate config cache so other components get fresh data invalidateConfigCache(); + + // Fetch updated config to get the recalculated nextRun time + try { + const updatedResponse = await apiRequest( + `/config?userId=${user.id}`, + { method: 'GET' }, + ); + if (updatedResponse && !updatedResponse.error) { + setConfig(prev => ({ + ...prev, + scheduleConfig: updatedResponse.scheduleConfig || prev.scheduleConfig, + })); + } + } catch (fetchError) { + console.warn('Failed to fetch updated config after auto-save:', fetchError); + } } else { toast.error( `Auto-save failed: ${result.message || 'Unknown error'}`, @@ -233,6 +249,22 @@ export function ConfigTabs() { // Silent success - no toast for auto-save // Invalidate config cache so other components get fresh data invalidateConfigCache(); + + // Fetch updated config to get the recalculated nextRun time + try { + const updatedResponse = await apiRequest( + `/config?userId=${user.id}`, + { method: 'GET' }, + ); + if (updatedResponse && !updatedResponse.error) { + setConfig(prev => ({ + ...prev, + cleanupConfig: updatedResponse.cleanupConfig || prev.cleanupConfig, + })); + } + } catch (fetchError) { + console.warn('Failed to fetch updated config after auto-save:', fetchError); + } } else { toast.error( `Auto-save failed: ${result.message || 'Unknown error'}`, diff --git a/src/components/config/DatabaseCleanupConfigForm.tsx b/src/components/config/DatabaseCleanupConfigForm.tsx index a4c7d78..c77382f 100644 --- a/src/components/config/DatabaseCleanupConfigForm.tsx +++ b/src/components/config/DatabaseCleanupConfigForm.tsx @@ -18,38 +18,79 @@ interface DatabaseCleanupConfigFormProps { isAutoSaving?: boolean; } + +// Helper to calculate cleanup interval in hours (should match backend logic) +function calculateCleanupInterval(retentionSeconds: number): number { + const retentionDays = retentionSeconds / (24 * 60 * 60); + if (retentionDays <= 1) { + return 6; + } else if (retentionDays <= 3) { + return 12; + } else if (retentionDays <= 7) { + return 24; + } else if (retentionDays <= 30) { + return 48; + } else { + return 168; + } +} + export function DatabaseCleanupConfigForm({ config, setConfig, onAutoSave, isAutoSaving = false, }: DatabaseCleanupConfigFormProps) { + // Optimistically update nextRun when enabled or retention changes const handleChange = ( e: React.ChangeEvent ) => { const { name, value, type } = e.target; - const newConfig = { + let newConfig = { ...config, - [name]: - type === "checkbox" ? (e.target as HTMLInputElement).checked : value, + [name]: type === "checkbox" ? (e.target as HTMLInputElement).checked : value, }; - setConfig(newConfig); - // Trigger auto-save for cleanup config changes + // If enabling or changing retention, recalculate nextRun + if ( + (name === "enabled" && (e.target as HTMLInputElement).checked) || + (name === "retentionDays" && config.enabled) + ) { + const now = new Date(); + const retentionSeconds = + name === "retentionDays" + ? Number(value) + : Number(newConfig.retentionDays); + const intervalHours = calculateCleanupInterval(retentionSeconds); + const nextRun = new Date(now.getTime() + intervalHours * 60 * 60 * 1000); + newConfig = { + ...newConfig, + nextRun, + }; + } + // If disabling, clear nextRun + if (name === "enabled" && !(e.target as HTMLInputElement).checked) { + newConfig = { + ...newConfig, + nextRun: undefined, + }; + } + + setConfig(newConfig); if (onAutoSave) { onAutoSave(newConfig); } }; - // Predefined retention periods + // Predefined retention periods (in seconds, like schedule intervals) const retentionOptions: { value: number; label: string }[] = [ - { value: 1, label: "1 day" }, - { value: 3, label: "3 days" }, - { value: 7, label: "7 days" }, - { value: 14, label: "14 days" }, - { value: 30, label: "30 days" }, - { value: 60, label: "60 days" }, - { value: 90, label: "90 days" }, + { value: 86400, label: "1 day" }, // 24 * 60 * 60 + { value: 259200, label: "3 days" }, // 3 * 24 * 60 * 60 + { value: 604800, label: "7 days" }, // 7 * 24 * 60 * 60 + { value: 1209600, label: "14 days" }, // 14 * 24 * 60 * 60 + { value: 2592000, label: "30 days" }, // 30 * 24 * 60 * 60 + { value: 5184000, label: "60 days" }, // 60 * 24 * 60 * 60 + { value: 7776000, label: "90 days" }, // 90 * 24 * 60 * 60 ]; return ( @@ -92,7 +133,7 @@ export function DatabaseCleanupConfigForm({ {config.enabled && (