diff --git a/src/PickupConfigEditor.js b/src/PickupConfigEditor.js
index 2affb55..47e75eb 100644
--- a/src/PickupConfigEditor.js
+++ b/src/PickupConfigEditor.js
@@ -1,81 +1,23 @@
-import { useState, useEffect } from 'react';
-import { DateRange } from 'react-date-range';
-import { format, parseISO, isValid, startOfDay } from 'date-fns';
-import { de } from 'date-fns/locale';
-import 'react-date-range/dist/styles.css';
-import 'react-date-range/dist/theme/default.css';
-
-const parseDateValue = (value) => {
- if (!value) {
- return null;
- }
- const parsed = parseISO(value);
- return isValid(parsed) ? parsed : null;
-};
-
-const formatDateValue = (date) => {
- if (!(date instanceof Date) || !isValid(date)) {
- return null;
- }
- return format(date, 'yyyy-MM-dd');
-};
-
-const formatRangeLabel = (start, end) => {
- const startDate = parseDateValue(start);
- const endDate = parseDateValue(end);
- if (startDate && endDate) {
- const startLabel = format(startDate, 'dd.MM.yyyy', { locale: de });
- const endLabel = format(endDate, 'dd.MM.yyyy', { locale: de });
- if (startLabel === endLabel) {
- return startLabel;
- }
- return `${startLabel} – ${endLabel}`;
- }
- if (startDate) {
- return format(startDate, 'dd.MM.yyyy', { locale: de });
- }
- return 'Zeitraum auswählen';
-};
-
-const buildSelectionRange = (start, end, minDate) => {
- const minimum = minDate || startOfDay(new Date());
- let startDate = parseDateValue(start) || parseDateValue(end) || minimum;
- let endDate = parseDateValue(end) || parseDateValue(start) || startDate;
- if (startDate < minimum) {
- startDate = minimum;
- }
- if (endDate < minimum) {
- endDate = startDate;
- }
- return {
- startDate,
- endDate,
- key: 'selection'
- };
-};
-
-const sortEntriesByLabel = (entries = []) => {
- return [...entries].sort((a, b) =>
- (a.label || '').localeCompare(b.label || '', 'de', { sensitivity: 'base' })
- );
-};
+import { useEffect, useState } from 'react';
+import { startOfDay } from 'date-fns';
+import PickupConfigTable from './components/PickupConfigTable';
+import RangePickerModal from './components/RangePickerModal';
+import usePickupConfig from './hooks/usePickupConfig';
+import { formatDateValue } from './utils/dateUtils';
const PickupConfigEditor = () => {
- const [config, setConfig] = useState([]);
- const [loading, setLoading] = useState(true);
- const [status, setStatus] = useState('');
- const [error, setError] = useState('');
+ const {
+ config,
+ setConfig,
+ loading,
+ status,
+ error,
+ fetchConfig,
+ saveConfig
+ } = usePickupConfig();
const [activeRangePicker, setActiveRangePicker] = useState(null);
const minSelectableDate = startOfDay(new Date());
- // Simulierte API-Endpunkte - diese müssen in Ihrer tatsächlichen Implementierung angepasst werden
- const API_URL = '/api/iobroker/pickup-config';
-
- useEffect(() => {
- // Beim Laden der Komponente die aktuelle Konfiguration abrufen
- fetchConfig();
- }, []);
-
useEffect(() => {
if (!activeRangePicker) {
return;
@@ -86,68 +28,6 @@ const PickupConfigEditor = () => {
}
}, [activeRangePicker, config]);
- const fetchConfig = async () => {
- setLoading(true);
- setError('');
-
- try {
- // In einer echten Implementierung würden Sie Ihre API aufrufen
- // Hier wird die statische Konfiguration verwendet
- // const response = await fetch(API_URL);
- // const data = await response.json();
-
- // Simulierte Verzögerung und Antwort mit der statischen Konfiguration
- setTimeout(() => {
- const staticConfig = [
- { 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",
- 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" }
- ];
- setConfig(sortEntriesByLabel(staticConfig));
- setLoading(false);
- }, 500);
- } catch (err) {
- setError('Fehler beim Laden der Konfiguration: ' + err.message);
- setLoading(false);
- }
- };
-
- const saveConfig = async () => {
- setStatus('Speichere...');
- setError('');
-
- try {
- // API-Aufruf zum Speichern der Konfiguration in ioBroker
- // In einer echten Implementierung würden Sie Ihre API aufrufen
- // const response = await fetch(API_URL, {
- // method: 'POST',
- // headers: {
- // 'Content-Type': 'application/json',
- // },
- // body: JSON.stringify(config),
- // });
-
- // Simulierte Verzögerung zum Darstellen des Speichervorgangs
- setTimeout(() => {
- setStatus('Konfiguration erfolgreich gespeichert!');
- setTimeout(() => setStatus(''), 3000);
- }, 1000);
- } catch (err) {
- setError('Fehler beim Speichern: ' + err.message);
- }
- };
-
const handleToggleActive = (index) => {
const newConfig = [...config];
newConfig[index].active = !newConfig[index].active;
@@ -233,96 +113,15 @@ const PickupConfigEditor = () => {
)}
-
+
- {activeRangeEntry && !activeRangeEntry.desiredWeekday && (
- setActiveRangePicker(null)}
- >
-
event.stopPropagation()}
- >
-
-
Zeitraum auswählen für
-
- {activeRangeEntry.label || `Store ${activeRangeEntry.id}`}
-
-
-
- {
- const { startDate, endDate } = ranges.selection;
- handleDateRangeSelection(activeRangeEntry.id, startDate, endDate);
- }}
- moveRangeOnFirstSelection={false}
- ranges={[
- buildSelectionRange(
- activeRangeEntry.desiredDateRange?.start,
- activeRangeEntry.desiredDateRange?.end,
- minSelectableDate
- )
- ]}
- rangeColors={['#2563EB']}
- months={1}
- direction="horizontal"
- showDateDisplay={false}
- locale={de}
- minDate={minSelectableDate}
- />
-
-
-
-
-
-
-
-
-
-
+ {activeRangeEntry && (
+
+ handleDateRangeSelection(activeRangeEntry.id, startDate, endDate)
+ }
+ onResetRange={() => {
+ handleDateRangeSelection(activeRangeEntry.id, null, null);
+ setActiveRangePicker(null);
+ }}
+ onClose={() => setActiveRangePicker(null)}
+ />
)}
);
diff --git a/src/components/PickupConfigTable.js b/src/components/PickupConfigTable.js
new file mode 100644
index 0000000..0400784
--- /dev/null
+++ b/src/components/PickupConfigTable.js
@@ -0,0 +1,106 @@
+import { formatRangeLabel } from '../utils/dateUtils';
+
+const PickupConfigTable = ({
+ config,
+ weekdays,
+ onToggleActive,
+ onToggleProfileCheck,
+ onToggleOnlyNotify,
+ onWeekdayChange,
+ onRangePickerRequest
+}) => {
+ return (
+
+ );
+};
+
+export default PickupConfigTable;
diff --git a/src/components/RangePickerModal.js b/src/components/RangePickerModal.js
new file mode 100644
index 0000000..b45cc04
--- /dev/null
+++ b/src/components/RangePickerModal.js
@@ -0,0 +1,66 @@
+import { DateRange } from 'react-date-range';
+import { de } from 'date-fns/locale';
+import { buildSelectionRange } from '../utils/dateUtils';
+import 'react-date-range/dist/styles.css';
+import 'react-date-range/dist/theme/default.css';
+
+const RangePickerModal = ({ entry, minDate, onSelectRange, onResetRange, onClose }) => {
+ if (!entry || entry.desiredWeekday) {
+ return null;
+ }
+
+ return (
+
+
event.stopPropagation()}
+ >
+
+
Zeitraum auswählen für
+
{entry.label || `Store ${entry.id}`}
+
+
+ {
+ const { startDate, endDate } = ranges.selection;
+ onSelectRange(startDate, endDate);
+ }}
+ moveRangeOnFirstSelection={false}
+ ranges={[
+ buildSelectionRange(
+ entry.desiredDateRange?.start,
+ entry.desiredDateRange?.end,
+ minDate
+ )
+ ]}
+ rangeColors={['#2563EB']}
+ months={1}
+ direction="horizontal"
+ showDateDisplay={false}
+ locale={de}
+ minDate={minDate}
+ />
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default RangePickerModal;
diff --git a/src/hooks/usePickupConfig.js b/src/hooks/usePickupConfig.js
new file mode 100644
index 0000000..2d9b687
--- /dev/null
+++ b/src/hooks/usePickupConfig.js
@@ -0,0 +1,78 @@
+import { useCallback, useEffect, useState } from 'react';
+import { sortEntriesByLabel } from '../utils/configUtils';
+
+const API_URL = '/api/iobroker/pickup-config';
+const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
+
+const STATIC_CONFIG = [
+ { 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',
+ 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' }
+];
+
+const usePickupConfig = () => {
+ const [config, setConfig] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [status, setStatus] = useState('');
+ const [error, setError] = useState('');
+
+ const fetchConfig = useCallback(async () => {
+ setLoading(true);
+ setError('');
+
+ try {
+ // In einer echten Implementierung würde hier die API aufgerufen:
+ // const response = await fetch(API_URL);
+ // const data = await response.json();
+ await delay(500);
+ setConfig(sortEntriesByLabel(STATIC_CONFIG));
+ } catch (err) {
+ setError('Fehler beim Laden der Konfiguration: ' + err.message);
+ } finally {
+ setLoading(false);
+ }
+ }, []);
+
+ const saveConfig = useCallback(async () => {
+ setStatus('Speichere...');
+ setError('');
+
+ try {
+ // API-Aufruf zum Speichern der Konfiguration:
+ // await fetch(API_URL, { method: 'POST', body: JSON.stringify(config) });
+ await delay(1000);
+ setStatus('Konfiguration erfolgreich gespeichert!');
+ setTimeout(() => setStatus(''), 3000);
+ } catch (err) {
+ setError('Fehler beim Speichern: ' + err.message);
+ }
+ }, []);
+
+ useEffect(() => {
+ fetchConfig();
+ }, [fetchConfig]);
+
+ return {
+ API_URL,
+ config,
+ setConfig,
+ loading,
+ status,
+ error,
+ fetchConfig,
+ saveConfig
+ };
+};
+
+export default usePickupConfig;
diff --git a/src/utils/configUtils.js b/src/utils/configUtils.js
new file mode 100644
index 0000000..1f3d3ce
--- /dev/null
+++ b/src/utils/configUtils.js
@@ -0,0 +1,5 @@
+export const sortEntriesByLabel = (entries = []) => {
+ return [...entries].sort((a, b) =>
+ (a?.label || '').localeCompare(b?.label || '', 'de', { sensitivity: 'base' })
+ );
+};
diff --git a/src/utils/dateUtils.js b/src/utils/dateUtils.js
new file mode 100644
index 0000000..9da40f3
--- /dev/null
+++ b/src/utils/dateUtils.js
@@ -0,0 +1,51 @@
+import { format, parseISO, isValid, startOfDay } from 'date-fns';
+import { de } from 'date-fns/locale';
+
+export const parseDateValue = (value) => {
+ if (!value) {
+ return null;
+ }
+ const parsed = parseISO(value);
+ return isValid(parsed) ? parsed : null;
+};
+
+export const formatDateValue = (date) => {
+ if (!(date instanceof Date) || !isValid(date)) {
+ return null;
+ }
+ return format(date, 'yyyy-MM-dd');
+};
+
+export const formatRangeLabel = (start, end) => {
+ const startDate = parseDateValue(start);
+ const endDate = parseDateValue(end);
+ if (startDate && endDate) {
+ const startLabel = format(startDate, 'dd.MM.yyyy', { locale: de });
+ const endLabel = format(endDate, 'dd.MM.yyyy', { locale: de });
+ if (startLabel === endLabel) {
+ return startLabel;
+ }
+ return `${startLabel} – ${endLabel}`;
+ }
+ if (startDate) {
+ return format(startDate, 'dd.MM.yyyy', { locale: de });
+ }
+ return 'Zeitraum auswählen';
+};
+
+export const buildSelectionRange = (start, end, minDate) => {
+ const minimum = minDate || startOfDay(new Date());
+ let startDate = parseDateValue(start) || parseDateValue(end) || minimum;
+ let endDate = parseDateValue(end) || parseDateValue(start) || startDate;
+ if (startDate < minimum) {
+ startDate = minimum;
+ }
+ if (endDate < minimum) {
+ endDate = startDate;
+ }
+ return {
+ startDate,
+ endDate,
+ key: 'selection'
+ };
+};