feat: replace single-date picker with unified date range control
This commit is contained in:
@@ -99,10 +99,12 @@ function matchesDesiredDate(pickupDate, desiredDate, desiredDateRange) {
|
|||||||
if (hasRange) {
|
if (hasRange) {
|
||||||
const startValue = toDateValue(desiredDateRange.start);
|
const startValue = toDateValue(desiredDateRange.start);
|
||||||
const endValue = toDateValue(desiredDateRange.end);
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
if (endValue !== null && pickupValue > endValue) {
|
if (normalizedEnd !== null && pickupValue > normalizedEnd) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
306
src/App.js
306
src/App.js
@@ -31,6 +31,42 @@ function App() {
|
|||||||
const [confirmDialog, setConfirmDialog] = useState({ open: false, resolve: null });
|
const [confirmDialog, setConfirmDialog] = useState({ open: false, resolve: null });
|
||||||
|
|
||||||
const weekdays = ['Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag', 'Sonntag'];
|
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 delay = useCallback((ms) => new Promise((resolve) => setTimeout(resolve, ms)), []);
|
||||||
|
|
||||||
const startSyncProgress = useCallback((message, percent, block = false) => {
|
const startSyncProgress = useCallback((message, percent, block = false) => {
|
||||||
@@ -163,7 +199,7 @@ function App() {
|
|||||||
}
|
}
|
||||||
const configData = await configResponse.json();
|
const configData = await configResponse.json();
|
||||||
progress?.update?.('Konfiguration wird geladen...', 75);
|
progress?.update?.('Konfiguration wird geladen...', 75);
|
||||||
setConfig(Array.isArray(configData) ? configData : []);
|
setConfig(normalizeConfigEntries(Array.isArray(configData) ? configData : []));
|
||||||
progress?.update?.('Synchronisierung abgeschlossen', 95);
|
progress?.update?.('Synchronisierung abgeschlossen', 95);
|
||||||
return {
|
return {
|
||||||
storeRefreshJob: data.storeRefreshJob,
|
storeRefreshJob: data.storeRefreshJob,
|
||||||
@@ -176,7 +212,7 @@ function App() {
|
|||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
},
|
},
|
||||||
[handleUnauthorized, normalizeAdminSettings]
|
[handleUnauthorized, normalizeAdminSettings, normalizeConfigEntries]
|
||||||
);
|
);
|
||||||
|
|
||||||
const authorizedFetch = useCallback(
|
const authorizedFetch = useCallback(
|
||||||
@@ -321,7 +357,7 @@ function App() {
|
|||||||
throw new Error(`HTTP ${response.status}`);
|
throw new Error(`HTTP ${response.status}`);
|
||||||
}
|
}
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
setConfig(Array.isArray(data) ? data : []);
|
setConfig(normalizeConfigEntries(Array.isArray(data) ? data : []));
|
||||||
if (!silent) {
|
if (!silent) {
|
||||||
setStatus('Konfiguration aktualisiert.');
|
setStatus('Konfiguration aktualisiert.');
|
||||||
setTimeout(() => setStatus(''), 3000);
|
setTimeout(() => setStatus(''), 3000);
|
||||||
@@ -331,7 +367,7 @@ function App() {
|
|||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, [session?.token, authorizedFetch]);
|
}, [session?.token, authorizedFetch, normalizeConfigEntries]);
|
||||||
|
|
||||||
const fetchStoresList = useCallback(async (tokenOverride) => {
|
const fetchStoresList = useCallback(async (tokenOverride) => {
|
||||||
const tokenToUse = tokenOverride || session?.token;
|
const tokenToUse = tokenOverride || session?.token;
|
||||||
@@ -745,28 +781,6 @@ function App() {
|
|||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
const updated = { ...item, desiredWeekday: value || null };
|
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) {
|
if (value && updated.desiredDateRange) {
|
||||||
delete updated.desiredDateRange;
|
delete updated.desiredDateRange;
|
||||||
}
|
}
|
||||||
@@ -782,19 +796,31 @@ function App() {
|
|||||||
if (item.id !== entryId) {
|
if (item.id !== entryId) {
|
||||||
return item;
|
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 = {
|
const nextRange = {
|
||||||
start: item.desiredDateRange?.start || null,
|
start: currentRange.start || null,
|
||||||
end: item.desiredDateRange?.end || null
|
end: currentRange.end || null
|
||||||
};
|
};
|
||||||
if (field === 'start') {
|
if (field === 'start') {
|
||||||
nextRange.start = value || null;
|
nextRange.start = sanitizedValue;
|
||||||
if (nextRange.end && value && value > nextRange.end) {
|
if (!nextRange.start) {
|
||||||
nextRange.end = value;
|
nextRange.end = null;
|
||||||
|
} else if (!nextRange.end || nextRange.end < nextRange.start) {
|
||||||
|
nextRange.end = nextRange.start;
|
||||||
}
|
}
|
||||||
} else if (field === 'end') {
|
} else if (field === 'end') {
|
||||||
nextRange.end = value || null;
|
nextRange.end = sanitizedValue;
|
||||||
if (nextRange.start && value && value < nextRange.start) {
|
if (!nextRange.end && nextRange.start) {
|
||||||
nextRange.start = value;
|
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);
|
const hasRange = Boolean(nextRange.start || nextRange.end);
|
||||||
@@ -804,12 +830,12 @@ function App() {
|
|||||||
if (updated.desiredWeekday) {
|
if (updated.desiredWeekday) {
|
||||||
delete updated.desiredWeekday;
|
delete updated.desiredWeekday;
|
||||||
}
|
}
|
||||||
if (updated.desiredDate) {
|
|
||||||
delete updated.desiredDate;
|
|
||||||
}
|
|
||||||
} else if (updated.desiredDateRange) {
|
} else if (updated.desiredDateRange) {
|
||||||
delete updated.desiredDateRange;
|
delete updated.desiredDateRange;
|
||||||
}
|
}
|
||||||
|
if (updated.desiredDate) {
|
||||||
|
delete updated.desiredDate;
|
||||||
|
}
|
||||||
return updated;
|
return updated;
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -1210,127 +1236,123 @@ function App() {
|
|||||||
<th className="px-4 py-2 border-b">Profil prüfen</th>
|
<th className="px-4 py-2 border-b">Profil prüfen</th>
|
||||||
<th className="px-4 py-2 border-b">Nur benachrichtigen</th>
|
<th className="px-4 py-2 border-b">Nur benachrichtigen</th>
|
||||||
<th className="px-4 py-2 border-b">Wochentag</th>
|
<th className="px-4 py-2 border-b">Wochentag</th>
|
||||||
<th className="px-4 py-2 border-b">Spezifisches Datum</th>
|
<th className="px-4 py-2 border-b">Zeitraum (Datum oder Range)</th>
|
||||||
<th className="px-4 py-2 border-b">Datumsbereich</th>
|
|
||||||
<th className="px-4 py-2 border-b">Aktionen</th>
|
<th className="px-4 py-2 border-b">Aktionen</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{visibleConfig.length === 0 && (
|
{visibleConfig.length === 0 && (
|
||||||
<tr>
|
<tr>
|
||||||
<td colSpan="8" className="px-4 py-6 text-center text-sm text-gray-500">
|
<td colSpan="7" className="px-4 py-6 text-center text-sm text-gray-500">
|
||||||
Keine sichtbaren Einträge. Nutze „Verfügbare Betriebe“, um Betriebe hinzuzufügen oder ausgeblendete Einträge zurückzuholen.
|
Keine sichtbaren Einträge. Nutze „Verfügbare Betriebe“, um Betriebe hinzuzufügen oder ausgeblendete Einträge zurückzuholen.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
)}
|
||||||
{visibleConfig.map((item, index) => {
|
{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 (
|
return (
|
||||||
<tr key={`${item.id}-${index}`} className={index % 2 === 0 ? 'bg-gray-50' : 'bg-white'}>
|
<tr key={`${item.id}-${index}`} className={index % 2 === 0 ? 'bg-gray-50' : 'bg-white'}>
|
||||||
<td className="px-4 py-2 border-b text-center">
|
<td className="px-4 py-2 border-b text-center">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={item.active || false}
|
checked={item.active || false}
|
||||||
onChange={() => handleToggleActive(item.id)}
|
onChange={() => handleToggleActive(item.id)}
|
||||||
className="h-5 w-5 text-blue-600 rounded focus:ring-2 focus:ring-blue-500"
|
className="h-5 w-5 text-blue-600 rounded focus:ring-2 focus:ring-blue-500"
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-2 border-b">
|
<td className="px-4 py-2 border-b">
|
||||||
<div>
|
|
||||||
<span className="font-medium">{item.label}</span>
|
|
||||||
<br />
|
|
||||||
<span className="text-sm text-gray-500">ID: {item.id}</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td className="px-4 py-2 border-b text-center">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={item.checkProfileId || false}
|
|
||||||
onChange={() => handleToggleProfileCheck(item.id)}
|
|
||||||
className="h-5 w-5 text-blue-600 rounded focus:ring-2 focus:ring-blue-500"
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td className="px-4 py-2 border-b text-center">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={item.onlyNotify || false}
|
|
||||||
onChange={() => handleToggleOnlyNotify(item.id)}
|
|
||||||
className="h-5 w-5 text-blue-600 rounded focus:ring-2 focus:ring-blue-500"
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td className="px-4 py-2 border-b">
|
|
||||||
<select
|
|
||||||
value={item.desiredWeekday || ''}
|
|
||||||
onChange={(e) => handleWeekdayChange(item.id, e.target.value)}
|
|
||||||
className="border rounded p-2 w-full bg-white focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
|
||||||
disabled={item.desiredDate || hasDateRange}
|
|
||||||
>
|
|
||||||
<option value="">Kein Wochentag</option>
|
|
||||||
{weekdays.map((day) => (
|
|
||||||
<option key={day} value={day}>
|
|
||||||
{day}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
<td className="px-4 py-2 border-b">
|
|
||||||
<input
|
|
||||||
type="date"
|
|
||||||
value={item.desiredDate || ''}
|
|
||||||
onChange={(e) => 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}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td className="px-4 py-2 border-b">
|
|
||||||
<div className="grid grid-cols-1 gap-2">
|
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-xs text-gray-500 mb-1">Von</label>
|
<span className="font-medium">{item.label}</span>
|
||||||
<input
|
<br />
|
||||||
type="date"
|
<span className="text-sm text-gray-500">ID: {item.id}</span>
|
||||||
value={item.desiredDateRange?.start || ''}
|
|
||||||
onChange={(e) => 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}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
</td>
|
||||||
<label className="block text-xs text-gray-500 mb-1">Bis</label>
|
<td className="px-4 py-2 border-b text-center">
|
||||||
<input
|
<input
|
||||||
type="date"
|
type="checkbox"
|
||||||
value={item.desiredDateRange?.end || ''}
|
checked={item.checkProfileId || false}
|
||||||
onChange={(e) => handleDateRangeChange(item.id, 'end', e.target.value)}
|
onChange={() => handleToggleProfileCheck(item.id)}
|
||||||
className="border rounded p-2 w-full focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
className="h-5 w-5 text-blue-600 rounded focus:ring-2 focus:ring-blue-500"
|
||||||
disabled={item.desiredWeekday || item.desiredDate}
|
/>
|
||||||
min={item.desiredDateRange?.start || undefined}
|
</td>
|
||||||
/>
|
<td className="px-4 py-2 border-b text-center">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={item.onlyNotify || false}
|
||||||
|
onChange={() => handleToggleOnlyNotify(item.id)}
|
||||||
|
className="h-5 w-5 text-blue-600 rounded focus:ring-2 focus:ring-blue-500"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td className="px-4 py-2 border-b">
|
||||||
|
<select
|
||||||
|
value={item.desiredWeekday || ''}
|
||||||
|
onChange={(e) => handleWeekdayChange(item.id, e.target.value)}
|
||||||
|
className="border rounded p-2 w-full bg-white focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||||
|
disabled={hasDateRange}
|
||||||
|
>
|
||||||
|
<option value="">Kein Wochentag</option>
|
||||||
|
{weekdays.map((day) => (
|
||||||
|
<option key={day} value={day}>
|
||||||
|
{day}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td className="px-4 py-2 border-b">
|
||||||
|
<div className="border rounded p-2 bg-gray-50">
|
||||||
|
<label className="block text-xs font-medium text-gray-600 mb-1">Datum / Zeitraum</label>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
value={rangeStart}
|
||||||
|
onChange={(e) => 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)}
|
||||||
|
/>
|
||||||
|
<span className="text-xs text-gray-500">bis</span>
|
||||||
|
<input
|
||||||
|
type="date"
|
||||||
|
value={rangeEnd}
|
||||||
|
onChange={(e) => 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}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-gray-500 mt-1">Ein einzelner Tag: Start und Ende identisch wählen.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</td>
|
||||||
</td>
|
<td className="px-4 py-2 border-b">
|
||||||
<td className="px-4 py-2 border-b">
|
<div className="flex items-center justify-center gap-2">
|
||||||
<div className="flex items-center justify-center gap-2">
|
<button
|
||||||
<button
|
onClick={() => hideEntry(item.id)}
|
||||||
onClick={() => hideEntry(item.id)}
|
className="bg-yellow-100 hover:bg-yellow-200 text-yellow-800 rounded-full p-1 focus:outline-none focus:ring-2 focus:ring-yellow-400"
|
||||||
className="bg-yellow-100 hover:bg-yellow-200 text-yellow-800 rounded-full p-1 focus:outline-none focus:ring-2 focus:ring-yellow-400"
|
title="Ausblenden"
|
||||||
title="Ausblenden"
|
>
|
||||||
>
|
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13.875 18.825A10.05 10.05 0 0112 19c-5.523 0-10-4.03-11-7 1.148-3.008 4.514-6 9-6 .824 0 1.627.087 2.4.252M15 12a3 3 0 11-6 0 3 3 0 016 0zm6.121 5.121L4.879 4.879" />
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13.875 18.825A10.05 10.05 0 0112 19c-5.523 0-10-4.03-11-7 1.148-3.008 4.514-6 9-6 .824 0 1.627.087 2.4.252M15 12a3 3 0 11-6 0 3 3 0 016 0zm6.121 5.121L4.879 4.879" />
|
</svg>
|
||||||
</svg>
|
</button>
|
||||||
</button>
|
<button
|
||||||
<button
|
onClick={() => deleteEntry(item.id)}
|
||||||
onClick={() => deleteEntry(item.id)}
|
className="bg-red-500 hover:bg-red-600 text-white rounded-full p-1 focus:outline-none focus:ring-2 focus:ring-red-500"
|
||||||
className="bg-red-500 hover:bg-red-600 text-white rounded-full p-1 focus:outline-none focus:ring-2 focus:ring-red-500"
|
title="Löschen"
|
||||||
title="Löschen"
|
>
|
||||||
>
|
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1 1v3M4 7h16" />
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
</svg>
|
||||||
</svg>
|
</button>
|
||||||
</button>
|
</div>
|
||||||
</div>
|
</td>
|
||||||
</td>
|
</tr>
|
||||||
</tr>
|
);
|
||||||
);
|
|
||||||
})}
|
})}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -30,7 +30,14 @@ const PickupConfigEditor = () => {
|
|||||||
{ id: "63448", active: false, checkProfileId: true, onlyNotify: true, label: "Penny Baden-Oos" },
|
{ 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: "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: "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: "33875", active: false, checkProfileId: true, onlyNotify: false, label: "Cap Markt", desiredWeekday: "Donnerstag" },
|
||||||
{ id: "42322", active: false, checkProfileId: false, onlyNotify: false, label: "Edeka Haueneberstein" },
|
{ id: "42322", active: false, checkProfileId: false, onlyNotify: false, label: "Edeka Haueneberstein" },
|
||||||
{ id: "51450", active: false, checkProfileId: true, onlyNotify: false, label: "Hornbach Grünwinkel" }
|
{ id: "51450", active: false, checkProfileId: true, onlyNotify: false, label: "Hornbach Grünwinkel" }
|
||||||
@@ -90,23 +97,6 @@ const PickupConfigEditor = () => {
|
|||||||
const handleWeekdayChange = (index, value) => {
|
const handleWeekdayChange = (index, value) => {
|
||||||
const newConfig = [...config];
|
const newConfig = [...config];
|
||||||
newConfig[index].desiredWeekday = value;
|
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) {
|
if (newConfig[index].desiredDateRange) {
|
||||||
delete newConfig[index].desiredDateRange;
|
delete newConfig[index].desiredDateRange;
|
||||||
}
|
}
|
||||||
@@ -115,19 +105,30 @@ const PickupConfigEditor = () => {
|
|||||||
|
|
||||||
const handleDateRangeChange = (index, field, value) => {
|
const handleDateRangeChange = (index, field, value) => {
|
||||||
const newConfig = [...config];
|
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 = {
|
const nextRange = {
|
||||||
start: newConfig[index].desiredDateRange?.start || null,
|
start: currentRange.start || null,
|
||||||
end: newConfig[index].desiredDateRange?.end || null
|
end: currentRange.end || null
|
||||||
};
|
};
|
||||||
if (field === 'start') {
|
if (field === 'start') {
|
||||||
nextRange.start = value || null;
|
nextRange.start = value || null;
|
||||||
if (nextRange.end && value && value > nextRange.end) {
|
if (!nextRange.start) {
|
||||||
nextRange.end = value;
|
nextRange.end = null;
|
||||||
|
} else if (!nextRange.end || nextRange.end < nextRange.start) {
|
||||||
|
nextRange.end = nextRange.start;
|
||||||
}
|
}
|
||||||
} else if (field === 'end') {
|
} else if (field === 'end') {
|
||||||
nextRange.end = value || null;
|
nextRange.end = value || null;
|
||||||
if (nextRange.start && value && value < nextRange.start) {
|
if (!nextRange.end && nextRange.start) {
|
||||||
nextRange.start = value;
|
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);
|
const hasRange = Boolean(nextRange.start || nextRange.end);
|
||||||
@@ -136,12 +137,12 @@ const PickupConfigEditor = () => {
|
|||||||
if (newConfig[index].desiredWeekday) {
|
if (newConfig[index].desiredWeekday) {
|
||||||
delete newConfig[index].desiredWeekday;
|
delete newConfig[index].desiredWeekday;
|
||||||
}
|
}
|
||||||
if (newConfig[index].desiredDate) {
|
|
||||||
delete newConfig[index].desiredDate;
|
|
||||||
}
|
|
||||||
} else if (newConfig[index].desiredDateRange) {
|
} else if (newConfig[index].desiredDateRange) {
|
||||||
delete newConfig[index].desiredDateRange;
|
delete newConfig[index].desiredDateRange;
|
||||||
}
|
}
|
||||||
|
if (newConfig[index].desiredDate) {
|
||||||
|
delete newConfig[index].desiredDate;
|
||||||
|
}
|
||||||
setConfig(newConfig);
|
setConfig(newConfig);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -176,93 +177,89 @@ const PickupConfigEditor = () => {
|
|||||||
<th className="px-4 py-2">Profil prüfen</th>
|
<th className="px-4 py-2">Profil prüfen</th>
|
||||||
<th className="px-4 py-2">Nur benachrichtigen</th>
|
<th className="px-4 py-2">Nur benachrichtigen</th>
|
||||||
<th className="px-4 py-2">Wochentag</th>
|
<th className="px-4 py-2">Wochentag</th>
|
||||||
<th className="px-4 py-2">Spezifisches Datum</th>
|
<th className="px-4 py-2">Datum / Zeitraum</th>
|
||||||
<th className="px-4 py-2">Datumsbereich</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{config.map((item, index) => {
|
{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 (
|
return (
|
||||||
<tr key={index} className={index % 2 === 0 ? 'bg-gray-50' : 'bg-white'}>
|
<tr key={index} className={index % 2 === 0 ? 'bg-gray-50' : 'bg-white'}>
|
||||||
<td className="px-4 py-2 text-center">
|
<td className="px-4 py-2 text-center">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={item.active}
|
checked={item.active}
|
||||||
onChange={() => handleToggleActive(index)}
|
onChange={() => handleToggleActive(index)}
|
||||||
className="h-5 w-5"
|
className="h-5 w-5"
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-2">
|
<td className="px-4 py-2">
|
||||||
<span className="font-medium">{item.label}</span>
|
<span className="font-medium">{item.label}</span>
|
||||||
<br />
|
<br />
|
||||||
<span className="text-sm text-gray-500">ID: {item.id}</span>
|
<span className="text-sm text-gray-500">ID: {item.id}</span>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-2 text-center">
|
<td className="px-4 py-2 text-center">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={item.checkProfileId}
|
checked={item.checkProfileId}
|
||||||
onChange={() => handleToggleProfileCheck(index)}
|
onChange={() => handleToggleProfileCheck(index)}
|
||||||
className="h-5 w-5"
|
className="h-5 w-5"
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-2 text-center">
|
<td className="px-4 py-2 text-center">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={item.onlyNotify}
|
checked={item.onlyNotify}
|
||||||
onChange={() => handleToggleOnlyNotify(index)}
|
onChange={() => handleToggleOnlyNotify(index)}
|
||||||
className="h-5 w-5"
|
className="h-5 w-5"
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-2">
|
<td className="px-4 py-2">
|
||||||
<select
|
<select
|
||||||
value={item.desiredWeekday || ''}
|
value={item.desiredWeekday || ''}
|
||||||
onChange={(e) => handleWeekdayChange(index, e.target.value)}
|
onChange={(e) => handleWeekdayChange(index, e.target.value)}
|
||||||
className="border rounded p-1 w-full"
|
className="border rounded p-1 w-full"
|
||||||
disabled={item.desiredDate || hasDateRange}
|
disabled={hasDateRange}
|
||||||
>
|
>
|
||||||
<option value="">Kein Wochentag</option>
|
<option value="">Kein Wochentag</option>
|
||||||
{weekdays.map((day) => (
|
{weekdays.map((day) => (
|
||||||
<option key={day} value={day}>{day}</option>
|
<option key={day} value={day}>{day}</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
<td className="px-4 py-2">
|
<td className="px-4 py-2">
|
||||||
<input
|
<div className="border rounded p-2 bg-gray-50">
|
||||||
type="date"
|
<label className="block text-xs text-gray-500 mb-1">Datum / Zeitraum</label>
|
||||||
value={item.desiredDate || ''}
|
<div className="flex items-center gap-2">
|
||||||
onChange={(e) => handleDateChange(index, e.target.value)}
|
<input
|
||||||
className="border rounded p-1 w-full"
|
type="date"
|
||||||
disabled={item.desiredWeekday || hasDateRange}
|
value={rangeStart}
|
||||||
/>
|
onChange={(e) => handleDateRangeChange(index, 'start', e.target.value)}
|
||||||
</td>
|
className="border rounded p-1 w-full"
|
||||||
<td className="px-4 py-2">
|
disabled={Boolean(item.desiredWeekday)}
|
||||||
<div className="grid grid-cols-1 gap-2">
|
/>
|
||||||
<div>
|
<span className="text-xs text-gray-500">bis</span>
|
||||||
<label className="block text-xs text-gray-500 mb-1">Von</label>
|
<input
|
||||||
<input
|
type="date"
|
||||||
type="date"
|
value={rangeEnd}
|
||||||
value={item.desiredDateRange?.start || ''}
|
onChange={(e) => handleDateRangeChange(index, 'end', e.target.value)}
|
||||||
onChange={(e) => handleDateRangeChange(index, 'start', e.target.value)}
|
className="border rounded p-1 w-full"
|
||||||
className="border rounded p-1 w-full"
|
disabled={Boolean(item.desiredWeekday)}
|
||||||
disabled={item.desiredWeekday || item.desiredDate}
|
min={rangeStart || undefined}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
|
<p className="text-xs text-gray-500 mt-1">Ein einzelner Tag: Start = Ende.</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
</td>
|
||||||
<label className="block text-xs text-gray-500 mb-1">Bis</label>
|
</tr>
|
||||||
<input
|
);
|
||||||
type="date"
|
|
||||||
value={item.desiredDateRange?.end || ''}
|
|
||||||
onChange={(e) => handleDateRangeChange(index, 'end', e.target.value)}
|
|
||||||
className="border rounded p-1 w-full"
|
|
||||||
disabled={item.desiredWeekday || item.desiredDate}
|
|
||||||
min={item.desiredDateRange?.start || undefined}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
);
|
|
||||||
})}
|
})}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
Reference in New Issue
Block a user