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.
This commit is contained in:
@@ -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.
|
||||||
|
|||||||
@@ -248,6 +248,7 @@ function mergeStoresIntoConfig(config = [], stores = []) {
|
|||||||
id,
|
id,
|
||||||
label: store.name || `Store ${id}`,
|
label: store.name || `Store ${id}`,
|
||||||
active: false,
|
active: false,
|
||||||
|
autoDeactivate: true,
|
||||||
checkProfileId: true,
|
checkProfileId: true,
|
||||||
onlyNotify: false,
|
onlyNotify: false,
|
||||||
hidden: hideByDefault
|
hidden: hideByDefault
|
||||||
|
|||||||
@@ -685,6 +685,17 @@ function deactivateEntry(session, entry, options = {}) {
|
|||||||
return changed;
|
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) {
|
function isEntryActiveInConfig(profileId, entryId) {
|
||||||
if (!profileId || !entryId) {
|
if (!profileId || !entryId) {
|
||||||
return false;
|
return false;
|
||||||
@@ -853,7 +864,7 @@ async function processBooking(session, entry, pickup) {
|
|||||||
booked: false,
|
booked: false,
|
||||||
storeId: entry.id
|
storeId: entry.id
|
||||||
});
|
});
|
||||||
deactivateEntry(session, entry);
|
deactivateEntryAfterSuccess(session, entry);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -883,7 +894,7 @@ async function processBooking(session, entry, pickup) {
|
|||||||
booked: true,
|
booked: true,
|
||||||
storeId: entry.id
|
storeId: entry.id
|
||||||
});
|
});
|
||||||
deactivateEntry(session, entry);
|
deactivateEntryAfterSuccess(session, entry);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`[ERROR] Buchung für ${storeName} am ${readableDate} fehlgeschlagen:`, error.message);
|
console.error(`[ERROR] Buchung für ${storeName} am ${readableDate} fehlgeschlagen:`, error.message);
|
||||||
try {
|
try {
|
||||||
|
|||||||
11
src/App.js
11
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) => {
|
const handleToggleProfileCheck = (entryId) => {
|
||||||
setIsDirty(true);
|
setIsDirty(true);
|
||||||
setConfig((prev) =>
|
setConfig((prev) =>
|
||||||
@@ -693,6 +702,7 @@ function App() {
|
|||||||
id: storeId,
|
id: storeId,
|
||||||
label: store.name || `Store ${storeId}`,
|
label: store.name || `Store ${storeId}`,
|
||||||
active: false,
|
active: false,
|
||||||
|
autoDeactivate: true,
|
||||||
checkProfileId: true,
|
checkProfileId: true,
|
||||||
onlyNotify: false,
|
onlyNotify: false,
|
||||||
skipDormantCheck: false,
|
skipDormantCheck: false,
|
||||||
@@ -813,6 +823,7 @@ function App() {
|
|||||||
config={config}
|
config={config}
|
||||||
registeredPickups={registeredPickups}
|
registeredPickups={registeredPickups}
|
||||||
onToggleActive={handleToggleActive}
|
onToggleActive={handleToggleActive}
|
||||||
|
onToggleAutoDeactivate={handleToggleAutoDeactivate}
|
||||||
onToggleProfileCheck={handleToggleProfileCheck}
|
onToggleProfileCheck={handleToggleProfileCheck}
|
||||||
onToggleOnlyNotify={handleToggleOnlyNotify}
|
onToggleOnlyNotify={handleToggleOnlyNotify}
|
||||||
onToggleDormantSkip={handleToggleDormantSkip}
|
onToggleDormantSkip={handleToggleDormantSkip}
|
||||||
|
|||||||
@@ -202,6 +202,7 @@ const DashboardView = ({
|
|||||||
config,
|
config,
|
||||||
registeredPickups,
|
registeredPickups,
|
||||||
onToggleActive,
|
onToggleActive,
|
||||||
|
onToggleAutoDeactivate,
|
||||||
onToggleProfileCheck,
|
onToggleProfileCheck,
|
||||||
onToggleOnlyNotify,
|
onToggleOnlyNotify,
|
||||||
onToggleDormantSkip,
|
onToggleDormantSkip,
|
||||||
@@ -388,6 +389,44 @@ const DashboardView = ({
|
|||||||
enableSorting: false,
|
enableSorting: false,
|
||||||
enableColumnFilter: false
|
enableColumnFilter: false
|
||||||
}),
|
}),
|
||||||
|
columnHelper.accessor((row) => row.autoDeactivate !== false, {
|
||||||
|
id: 'autoDeactivate',
|
||||||
|
header: ({ column }) => (
|
||||||
|
<div>
|
||||||
|
<span className="font-semibold text-sm">Automatisch deaktivieren</span>
|
||||||
|
<ColumnSelectFilter
|
||||||
|
column={column}
|
||||||
|
options={[
|
||||||
|
{ value: 'true', label: 'Ja' },
|
||||||
|
{ value: 'false', label: 'Nein' }
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<div className="text-center">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="h-5 w-5"
|
||||||
|
checked={row.original.autoDeactivate !== false}
|
||||||
|
onChange={() => onToggleAutoDeactivate(row.original.id)}
|
||||||
|
title="Nach erfolgreicher Slot-Aktion automatisch deaktivieren"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
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', {
|
columnHelper.accessor('label', {
|
||||||
header: ({ column }) => (
|
header: ({ column }) => (
|
||||||
<div>
|
<div>
|
||||||
@@ -649,7 +688,9 @@ const DashboardView = ({
|
|||||||
onDeleteEntry,
|
onDeleteEntry,
|
||||||
onHideEntry,
|
onHideEntry,
|
||||||
onRangePickerRequest,
|
onRangePickerRequest,
|
||||||
|
onToggleAutoDeactivate,
|
||||||
onToggleActive,
|
onToggleActive,
|
||||||
|
onToggleDormantSkip,
|
||||||
onToggleOnlyNotify,
|
onToggleOnlyNotify,
|
||||||
onToggleProfileCheck,
|
onToggleProfileCheck,
|
||||||
onWeekdayChange,
|
onWeekdayChange,
|
||||||
|
|||||||
Reference in New Issue
Block a user