From 3f1892b951494a91bd2d21de9b9f86ba31ebae01 Mon Sep 17 00:00:00 2001 From: Meik Date: Sun, 14 Jun 2026 21:23:47 +0200 Subject: [PATCH] feat: add auto deactivate option for slot plans Add an autoDeactivate flag to pickup configuration entries, expose it as a dashboard checkbox beside the active state, and keep existing entries defaulting to automatic deactivation unless explicitly disabled. --- .commitmessage | 4 ++-- server.js | 1 + services/pickupScheduler.js | 15 ++++++++++-- src/App.js | 11 +++++++++ src/components/DashboardView.js | 41 +++++++++++++++++++++++++++++++++ 5 files changed, 68 insertions(+), 4 deletions(-) diff --git a/.commitmessage b/.commitmessage index 2239055..f102431 100644 --- a/.commitmessage +++ b/.commitmessage @@ -1,3 +1,3 @@ -chore: sync running docker state +feat: add auto deactivate option for slot plans -Commit the app code currently running in Docker, including scheduler, store status, journal reminder, maintenance mode, and Foodsharing API check updates. Remove generated runtime logs from version control and ignore local runtime captures. +Add an autoDeactivate flag to pickup configuration entries, expose it as a dashboard checkbox beside the active state, and keep existing entries defaulting to automatic deactivation unless explicitly disabled. diff --git a/server.js b/server.js index 7309e65..9226152 100644 --- a/server.js +++ b/server.js @@ -248,6 +248,7 @@ function mergeStoresIntoConfig(config = [], stores = []) { id, label: store.name || `Store ${id}`, active: false, + autoDeactivate: true, checkProfileId: true, onlyNotify: false, hidden: hideByDefault diff --git a/services/pickupScheduler.js b/services/pickupScheduler.js index 6b7d91b..8b3eb5d 100644 --- a/services/pickupScheduler.js +++ b/services/pickupScheduler.js @@ -685,6 +685,17 @@ function deactivateEntry(session, entry, options = {}) { return changed; } +function shouldAutoDeactivateEntry(entry) { + return entry?.autoDeactivate !== false; +} + +function deactivateEntryAfterSuccess(session, entry, options = {}) { + if (!shouldAutoDeactivateEntry(entry)) { + return false; + } + return deactivateEntry(session, entry, options); +} + function isEntryActiveInConfig(profileId, entryId) { if (!profileId || !entryId) { return false; @@ -853,7 +864,7 @@ async function processBooking(session, entry, pickup) { booked: false, storeId: entry.id }); - deactivateEntry(session, entry); + deactivateEntryAfterSuccess(session, entry); return; } @@ -883,7 +894,7 @@ async function processBooking(session, entry, pickup) { booked: true, storeId: entry.id }); - deactivateEntry(session, entry); + deactivateEntryAfterSuccess(session, entry); } catch (error) { console.error(`[ERROR] Buchung für ${storeName} am ${readableDate} fehlgeschlagen:`, error.message); try { diff --git a/src/App.js b/src/App.js index ae4557f..5a44d5a 100644 --- a/src/App.js +++ b/src/App.js @@ -529,6 +529,15 @@ function App() { ); }; + const handleToggleAutoDeactivate = (entryId) => { + setIsDirty(true); + setConfig((prev) => + prev.map((item) => + item.id === entryId ? { ...item, autoDeactivate: item.autoDeactivate === false } : item + ) + ); + }; + const handleToggleProfileCheck = (entryId) => { setIsDirty(true); setConfig((prev) => @@ -693,6 +702,7 @@ function App() { id: storeId, label: store.name || `Store ${storeId}`, active: false, + autoDeactivate: true, checkProfileId: true, onlyNotify: false, skipDormantCheck: false, @@ -813,6 +823,7 @@ function App() { config={config} registeredPickups={registeredPickups} onToggleActive={handleToggleActive} + onToggleAutoDeactivate={handleToggleAutoDeactivate} onToggleProfileCheck={handleToggleProfileCheck} onToggleOnlyNotify={handleToggleOnlyNotify} onToggleDormantSkip={handleToggleDormantSkip} diff --git a/src/components/DashboardView.js b/src/components/DashboardView.js index 79bd085..fae206d 100644 --- a/src/components/DashboardView.js +++ b/src/components/DashboardView.js @@ -202,6 +202,7 @@ const DashboardView = ({ config, registeredPickups, onToggleActive, + onToggleAutoDeactivate, onToggleProfileCheck, onToggleOnlyNotify, onToggleDormantSkip, @@ -388,6 +389,44 @@ const DashboardView = ({ enableSorting: false, enableColumnFilter: false }), + columnHelper.accessor((row) => row.autoDeactivate !== false, { + id: 'autoDeactivate', + header: ({ column }) => ( +
+ Automatisch deaktivieren + +
+ ), + cell: ({ row }) => ( +
+ onToggleAutoDeactivate(row.original.id)} + title="Nach erfolgreicher Slot-Aktion automatisch deaktivieren" + /> +
+ ), + filterFn: (row, columnId, value) => { + if (value === undefined) { + return true; + } + const boolValue = value === 'true'; + return row.getValue(columnId) === boolValue; + }, + sortingFn: (rowA, rowB, columnId) => { + const a = rowA.getValue(columnId); + const b = rowB.getValue(columnId); + return Number(b) - Number(a); + } + }), columnHelper.accessor('label', { header: ({ column }) => (
@@ -649,7 +688,9 @@ const DashboardView = ({ onDeleteEntry, onHideEntry, onRangePickerRequest, + onToggleAutoDeactivate, onToggleActive, + onToggleDormantSkip, onToggleOnlyNotify, onToggleProfileCheck, onWeekdayChange,