From 4cebada1ed4c7d6f22aadced8f3d17c34bcd190b Mon Sep 17 00:00:00 2001 From: root Date: Sun, 9 Nov 2025 19:22:13 +0100 Subject: [PATCH] feat: replace single-date picker with unified date range control --- services/pickupScheduler.js | 6 +- src/App.js | 306 +++++++++++++++++++----------------- src/PickupConfigEditor.js | 213 +++++++++++++------------ 3 files changed, 273 insertions(+), 252 deletions(-) diff --git a/services/pickupScheduler.js b/services/pickupScheduler.js index cca5604..696618b 100644 --- a/services/pickupScheduler.js +++ b/services/pickupScheduler.js @@ -99,10 +99,12 @@ function matchesDesiredDate(pickupDate, desiredDate, desiredDateRange) { if (hasRange) { const startValue = toDateValue(desiredDateRange.start); const endValue = toDateValue(desiredDateRange.end); - if (startValue !== null && pickupValue < startValue) { + const normalizedStart = startValue !== null ? startValue : endValue; + const normalizedEnd = endValue !== null ? endValue : startValue; + if (normalizedStart !== null && pickupValue < normalizedStart) { return false; } - if (endValue !== null && pickupValue > endValue) { + if (normalizedEnd !== null && pickupValue > normalizedEnd) { return false; } return true; diff --git a/src/App.js b/src/App.js index 641afe2..a084d0c 100644 --- a/src/App.js +++ b/src/App.js @@ -31,6 +31,42 @@ function App() { const [confirmDialog, setConfirmDialog] = useState({ open: false, resolve: null }); const weekdays = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag', 'Sonntag']; + + const normalizeConfigEntries = useCallback((entries) => { + if (!Array.isArray(entries)) { + return []; + } + return entries.map((entry) => { + if (!entry || typeof entry !== 'object') { + return entry; + } + const normalized = { ...entry }; + if (normalized.desiredDate) { + if (normalized.desiredDate) { + normalized.desiredDateRange = { + start: normalized.desiredDate, + end: normalized.desiredDate + }; + } + delete normalized.desiredDate; + } + if (normalized.desiredDateRange) { + const startValue = normalized.desiredDateRange.start || null; + const endValue = normalized.desiredDateRange.end || null; + const normalizedStart = startValue || endValue || null; + const normalizedEnd = endValue || startValue || null; + if (!normalizedStart && !normalizedEnd) { + delete normalized.desiredDateRange; + } else { + normalized.desiredDateRange = { + start: normalizedStart, + end: normalizedEnd + }; + } + } + return normalized; + }); + }, []); const delay = useCallback((ms) => new Promise((resolve) => setTimeout(resolve, ms)), []); const startSyncProgress = useCallback((message, percent, block = false) => { @@ -163,7 +199,7 @@ function App() { } const configData = await configResponse.json(); progress?.update?.('Konfiguration wird geladen...', 75); - setConfig(Array.isArray(configData) ? configData : []); + setConfig(normalizeConfigEntries(Array.isArray(configData) ? configData : [])); progress?.update?.('Synchronisierung abgeschlossen', 95); return { storeRefreshJob: data.storeRefreshJob, @@ -176,7 +212,7 @@ function App() { } return {}; }, - [handleUnauthorized, normalizeAdminSettings] + [handleUnauthorized, normalizeAdminSettings, normalizeConfigEntries] ); const authorizedFetch = useCallback( @@ -321,7 +357,7 @@ function App() { throw new Error(`HTTP ${response.status}`); } const data = await response.json(); - setConfig(Array.isArray(data) ? data : []); + setConfig(normalizeConfigEntries(Array.isArray(data) ? data : [])); if (!silent) { setStatus('Konfiguration aktualisiert.'); setTimeout(() => setStatus(''), 3000); @@ -331,7 +367,7 @@ function App() { } finally { setLoading(false); } - }, [session?.token, authorizedFetch]); + }, [session?.token, authorizedFetch, normalizeConfigEntries]); const fetchStoresList = useCallback(async (tokenOverride) => { const tokenToUse = tokenOverride || session?.token; @@ -745,28 +781,6 @@ function App() { return item; } const updated = { ...item, desiredWeekday: value || null }; - if (value && updated.desiredDate) { - delete updated.desiredDate; - } - if (value && updated.desiredDateRange) { - delete updated.desiredDateRange; - } - return updated; - }) - ); - }; - - const handleDateChange = (entryId, value) => { - setIsDirty(true); - setConfig((prev) => - prev.map((item) => { - if (item.id !== entryId) { - return item; - } - const updated = { ...item, desiredDate: value || null }; - if (value && updated.desiredWeekday) { - delete updated.desiredWeekday; - } if (value && updated.desiredDateRange) { delete updated.desiredDateRange; } @@ -782,19 +796,31 @@ function App() { if (item.id !== entryId) { return item; } + const sanitizedValue = value || null; + const currentRange = item.desiredDateRange + ? { ...item.desiredDateRange } + : item.desiredDate + ? { start: item.desiredDate, end: item.desiredDate } + : { start: null, end: null }; const nextRange = { - start: item.desiredDateRange?.start || null, - end: item.desiredDateRange?.end || null + start: currentRange.start || null, + end: currentRange.end || null }; if (field === 'start') { - nextRange.start = value || null; - if (nextRange.end && value && value > nextRange.end) { - nextRange.end = value; + nextRange.start = sanitizedValue; + if (!nextRange.start) { + nextRange.end = null; + } else if (!nextRange.end || nextRange.end < nextRange.start) { + nextRange.end = nextRange.start; } } else if (field === 'end') { - nextRange.end = value || null; - if (nextRange.start && value && value < nextRange.start) { - nextRange.start = value; + nextRange.end = sanitizedValue; + if (!nextRange.end && nextRange.start) { + nextRange.end = nextRange.start; + } else if (!nextRange.start && nextRange.end) { + nextRange.start = nextRange.end; + } else if (nextRange.start && nextRange.end < nextRange.start) { + nextRange.start = nextRange.end; } } const hasRange = Boolean(nextRange.start || nextRange.end); @@ -804,12 +830,12 @@ function App() { if (updated.desiredWeekday) { delete updated.desiredWeekday; } - if (updated.desiredDate) { - delete updated.desiredDate; - } } else if (updated.desiredDateRange) { delete updated.desiredDateRange; } + if (updated.desiredDate) { + delete updated.desiredDate; + } return updated; }) ); @@ -1210,127 +1236,123 @@ function App() { Profil prüfen Nur benachrichtigen Wochentag - Spezifisches Datum - Datumsbereich + Zeitraum (Datum oder Range) Aktionen {visibleConfig.length === 0 && ( - + Keine sichtbaren Einträge. Nutze „Verfügbare Betriebe“, um Betriebe hinzuzufügen oder ausgeblendete Einträge zurückzuholen. )} {visibleConfig.map((item, index) => { - const hasDateRange = Boolean(item.desiredDateRange?.start || item.desiredDateRange?.end); + const normalizedRange = item.desiredDateRange + ? { ...item.desiredDateRange } + : item.desiredDate + ? { start: item.desiredDate, end: item.desiredDate } + : null; + const rangeStart = normalizedRange?.start || ''; + const rangeEnd = normalizedRange?.end || ''; + const hasDateRange = Boolean(rangeStart || rangeEnd); return ( - - handleToggleActive(item.id)} - className="h-5 w-5 text-blue-600 rounded focus:ring-2 focus:ring-blue-500" - /> - - -
- {item.label} -
- ID: {item.id} -
- - - handleToggleProfileCheck(item.id)} - className="h-5 w-5 text-blue-600 rounded focus:ring-2 focus:ring-blue-500" - /> - - - handleToggleOnlyNotify(item.id)} - className="h-5 w-5 text-blue-600 rounded focus:ring-2 focus:ring-blue-500" - /> - - - - - - handleDateChange(item.id, e.target.value)} - className="border rounded p-2 w-full focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500" - disabled={item.desiredWeekday || hasDateRange} - /> - - -
+ + handleToggleActive(item.id)} + className="h-5 w-5 text-blue-600 rounded focus:ring-2 focus:ring-blue-500" + /> + +
- - handleDateRangeChange(item.id, 'start', e.target.value)} - className="border rounded p-2 w-full focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500" - disabled={item.desiredWeekday || item.desiredDate} - /> + {item.label} +
+ ID: {item.id}
-
- - handleDateRangeChange(item.id, 'end', e.target.value)} - className="border rounded p-2 w-full focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500" - disabled={item.desiredWeekday || item.desiredDate} - min={item.desiredDateRange?.start || undefined} - /> + + + handleToggleProfileCheck(item.id)} + className="h-5 w-5 text-blue-600 rounded focus:ring-2 focus:ring-blue-500" + /> + + + handleToggleOnlyNotify(item.id)} + className="h-5 w-5 text-blue-600 rounded focus:ring-2 focus:ring-blue-500" + /> + + + + + +
+ +
+ handleDateRangeChange(item.id, 'start', e.target.value)} + className="border rounded p-2 w-full focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500" + disabled={Boolean(item.desiredWeekday)} + /> + bis + handleDateRangeChange(item.id, 'end', e.target.value)} + className="border rounded p-2 w-full focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500" + disabled={Boolean(item.desiredWeekday)} + min={rangeStart || undefined} + /> +
+

Ein einzelner Tag: Start und Ende identisch wählen.

-
- - -
- - -
- - - ); + + +
+ + +
+ + + ); })} diff --git a/src/PickupConfigEditor.js b/src/PickupConfigEditor.js index 97051d2..90f7e6f 100644 --- a/src/PickupConfigEditor.js +++ b/src/PickupConfigEditor.js @@ -30,7 +30,14 @@ const PickupConfigEditor = () => { { id: "63448", active: false, checkProfileId: true, onlyNotify: true, label: "Penny Baden-Oos" }, { id: "44975", active: false, checkProfileId: true, onlyNotify: false, label: "Aldi Kuppenheim", desiredWeekday: "Samstag" }, { id: "44972", active: false, checkProfileId: true, onlyNotify: false, label: "Aldi Biblisweg", desiredWeekday: "Dienstag" }, - { id: "44975", active: false, checkProfileId: true, onlyNotify: false, label: "Aldi Kuppenheim", desiredDate: "2025-05-18" }, + { + id: "44975", + active: false, + checkProfileId: true, + onlyNotify: false, + label: "Aldi Kuppenheim", + desiredDateRange: { start: "2025-05-18", end: "2025-05-18" } + }, { id: "33875", active: false, checkProfileId: true, onlyNotify: false, label: "Cap Markt", desiredWeekday: "Donnerstag" }, { id: "42322", active: false, checkProfileId: false, onlyNotify: false, label: "Edeka Haueneberstein" }, { id: "51450", active: false, checkProfileId: true, onlyNotify: false, label: "Hornbach Grünwinkel" } @@ -90,23 +97,6 @@ const PickupConfigEditor = () => { const handleWeekdayChange = (index, value) => { const newConfig = [...config]; newConfig[index].desiredWeekday = value; - // Wenn ein Wochentag gesetzt wird, entfernen wir das spezifische Datum - if (newConfig[index].desiredDate) { - delete newConfig[index].desiredDate; - } - if (newConfig[index].desiredDateRange) { - delete newConfig[index].desiredDateRange; - } - setConfig(newConfig); - }; - - const handleDateChange = (index, value) => { - const newConfig = [...config]; - newConfig[index].desiredDate = value; - // Wenn ein spezifisches Datum gesetzt wird, entfernen wir den Wochentag - if (newConfig[index].desiredWeekday) { - delete newConfig[index].desiredWeekday; - } if (newConfig[index].desiredDateRange) { delete newConfig[index].desiredDateRange; } @@ -115,19 +105,30 @@ const PickupConfigEditor = () => { const handleDateRangeChange = (index, field, value) => { const newConfig = [...config]; + const currentRange = newConfig[index].desiredDateRange + ? { ...newConfig[index].desiredDateRange } + : newConfig[index].desiredDate + ? { start: newConfig[index].desiredDate, end: newConfig[index].desiredDate } + : { start: null, end: null }; const nextRange = { - start: newConfig[index].desiredDateRange?.start || null, - end: newConfig[index].desiredDateRange?.end || null + start: currentRange.start || null, + end: currentRange.end || null }; if (field === 'start') { nextRange.start = value || null; - if (nextRange.end && value && value > nextRange.end) { - nextRange.end = value; + if (!nextRange.start) { + nextRange.end = null; + } else if (!nextRange.end || nextRange.end < nextRange.start) { + nextRange.end = nextRange.start; } } else if (field === 'end') { nextRange.end = value || null; - if (nextRange.start && value && value < nextRange.start) { - nextRange.start = value; + if (!nextRange.end && nextRange.start) { + nextRange.end = nextRange.start; + } else if (!nextRange.start && nextRange.end) { + nextRange.start = nextRange.end; + } else if (nextRange.start && nextRange.end < nextRange.start) { + nextRange.start = nextRange.end; } } const hasRange = Boolean(nextRange.start || nextRange.end); @@ -136,12 +137,12 @@ const PickupConfigEditor = () => { if (newConfig[index].desiredWeekday) { delete newConfig[index].desiredWeekday; } - if (newConfig[index].desiredDate) { - delete newConfig[index].desiredDate; - } } else if (newConfig[index].desiredDateRange) { delete newConfig[index].desiredDateRange; } + if (newConfig[index].desiredDate) { + delete newConfig[index].desiredDate; + } setConfig(newConfig); }; @@ -176,93 +177,89 @@ const PickupConfigEditor = () => { Profil prüfen Nur benachrichtigen Wochentag - Spezifisches Datum - Datumsbereich + Datum / Zeitraum {config.map((item, index) => { - const hasDateRange = Boolean(item.desiredDateRange?.start || item.desiredDateRange?.end); + const normalizedRange = item.desiredDateRange + ? { ...item.desiredDateRange } + : item.desiredDate + ? { start: item.desiredDate, end: item.desiredDate } + : null; + const rangeStart = normalizedRange?.start || ''; + const rangeEnd = normalizedRange?.end || ''; + const hasDateRange = Boolean(rangeStart || rangeEnd); return ( - - - handleToggleActive(index)} - className="h-5 w-5" - /> - - - {item.label} -
- ID: {item.id} - - - handleToggleProfileCheck(index)} - className="h-5 w-5" - /> - - - handleToggleOnlyNotify(index)} - className="h-5 w-5" - /> - - - - - - handleDateChange(index, e.target.value)} - className="border rounded p-1 w-full" - disabled={item.desiredWeekday || hasDateRange} - /> - - -
-
- - handleDateRangeChange(index, 'start', e.target.value)} - className="border rounded p-1 w-full" - disabled={item.desiredWeekday || item.desiredDate} - /> + + + handleToggleActive(index)} + className="h-5 w-5" + /> + + + {item.label} +
+ ID: {item.id} + + + handleToggleProfileCheck(index)} + className="h-5 w-5" + /> + + + handleToggleOnlyNotify(index)} + className="h-5 w-5" + /> + + + + + +
+ +
+ handleDateRangeChange(index, 'start', e.target.value)} + className="border rounded p-1 w-full" + disabled={Boolean(item.desiredWeekday)} + /> + bis + handleDateRangeChange(index, 'end', e.target.value)} + className="border rounded p-1 w-full" + disabled={Boolean(item.desiredWeekday)} + min={rangeStart || undefined} + /> +
+

Ein einzelner Tag: Start = Ende.

-
- - handleDateRangeChange(index, 'end', e.target.value)} - className="border rounded p-1 w-full" - disabled={item.desiredWeekday || item.desiredDate} - min={item.desiredDateRange?.start || undefined} - /> -
-
- - - ); + + + ); })}