aktueller stand

This commit is contained in:
2026-01-29 20:44:39 +01:00
parent ad32f299cf
commit e64edabf85
10 changed files with 1115 additions and 86 deletions

View File

@@ -140,6 +140,7 @@ function App() {
setError,
setLoading,
setStores,
setRegularPickupMap,
setConfig,
normalizeConfigEntries,
setIsDirty,
@@ -552,45 +553,6 @@ function App() {
const visibleConfig = useMemo(() => config.filter((item) => !item.hidden), [config]);
useEffect(() => {
let cancelled = false;
if (!session?.token || !authorizedFetch || visibleConfig.length === 0) {
return () => {
cancelled = true;
};
}
const uniqueIds = Array.from(new Set(visibleConfig.map((item) => String(item.id))));
const missing = uniqueIds.filter((id) => regularPickupMap[id] === undefined);
if (missing.length === 0) {
return () => {
cancelled = true;
};
}
const fetchSchedules = async () => {
for (const id of missing) {
try {
const response = await authorizedFetch(`/api/stores/${id}/regular-pickup`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
const rules = Array.isArray(data) ? data : Array.isArray(data?.rules) ? data.rules : [];
if (!cancelled) {
setRegularPickupMap((prev) => ({ ...prev, [id]: rules }));
}
} catch (err) {
if (!cancelled) {
setRegularPickupMap((prev) => ({ ...prev, [id]: [] }));
}
}
}
};
fetchSchedules();
return () => {
cancelled = true;
};
}, [authorizedFetch, regularPickupMap, session?.token, visibleConfig]);
const activeRangeEntry = useMemo(() => {
if (!activeRangePicker) {
return null;

View File

@@ -103,6 +103,32 @@ const AdminSettingsPanel = ({
/>
</SettingField>
<SettingField
label="Regular-Pickup Cache Refresh (Cron)"
description="Regelmäßiger Refresh der Regular-Pickup-Zeiten (serverseitiger Cache)."
>
<input
type="text"
value={adminSettings.regularPickupRefreshCron}
onChange={(event) => onSettingChange('regularPickupRefreshCron', event.target.value)}
className="border rounded p-2 w-full focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-purple-500"
placeholder="z. B. 0 3 * * *"
/>
</SettingField>
<SettingField
label="Letzte Abholungsprüfung (Cron)"
description="Wann die Info zur letzten Abholung (last_fetch) aktualisiert wird."
>
<input
type="text"
value={adminSettings.dormantMembershipCron}
onChange={(event) => onSettingChange('dormantMembershipCron', event.target.value)}
className="border rounded p-2 w-full focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-purple-500"
placeholder="z. B. 0 4 */14 * *"
/>
</SettingField>
<SettingField
label="Check-Fenster um Slot-Zeiten (Minuten)"
description="Minuten relativ zur Slot-Zeit, kommagetrennt. Beispiel: -1,-0.5,0,0.5,1,1.5"

View File

@@ -7,6 +7,7 @@ const useStoreSync = ({
setError,
setLoading,
setStores,
setRegularPickupMap,
setConfig,
normalizeConfigEntries,
setIsDirty,
@@ -63,7 +64,14 @@ const useStoreSync = ({
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
setStores(Array.isArray(data) ? data : []);
if (Array.isArray(data)) {
setStores(data);
} else {
setStores(Array.isArray(data?.stores) ? data.stores : []);
if (data?.regularPickupMap && typeof data.regularPickupMap === 'object') {
setRegularPickupMap(data.regularPickupMap);
}
}
if (!silent) {
setStatus('Betriebe aktualisiert.');
setTimeout(() => setStatus(''), 3000);
@@ -72,7 +80,7 @@ const useStoreSync = ({
setError(`Fehler beim Laden der Betriebe: ${err.message}`);
}
},
[sessionToken, authorizedFetch, setStatus, setError, setStores]
[sessionToken, authorizedFetch, setStatus, setError, setStores, setRegularPickupMap]
);
const syncStoresWithProgress = useCallback(

View File

@@ -8,6 +8,8 @@ export const normalizeAdminSettings = (raw) => {
pickupWindowOffsetsMinutes: Array.isArray(raw.pickupWindowOffsetsMinutes)
? raw.pickupWindowOffsetsMinutes.join(', ')
: '',
regularPickupRefreshCron: raw.regularPickupRefreshCron || '',
dormantMembershipCron: raw.dormantMembershipCron || '',
randomDelayMinSeconds: raw.randomDelayMinSeconds ?? '',
randomDelayMaxSeconds: raw.randomDelayMaxSeconds ?? '',
initialDelayMinSeconds: raw.initialDelayMinSeconds ?? '',
@@ -74,6 +76,8 @@ export const serializeAdminSettings = (adminSettings) => {
scheduleCron: adminSettings.scheduleCron,
pickupFallbackCron: adminSettings.pickupFallbackCron,
pickupWindowOffsetsMinutes: toNumberArray(adminSettings.pickupWindowOffsetsMinutes),
regularPickupRefreshCron: adminSettings.regularPickupRefreshCron,
dormantMembershipCron: adminSettings.dormantMembershipCron,
randomDelayMinSeconds: toNumberOrUndefined(adminSettings.randomDelayMinSeconds),
randomDelayMaxSeconds: toNumberOrUndefined(adminSettings.randomDelayMaxSeconds),
initialDelayMinSeconds: toNumberOrUndefined(adminSettings.initialDelayMinSeconds),