diff --git a/src/components/MobileConfigList.css b/src/components/MobileConfigList.css
new file mode 100644
index 0000000..86b901d
--- /dev/null
+++ b/src/components/MobileConfigList.css
@@ -0,0 +1,262 @@
+.mobile-config-list {
+ display: none;
+}
+
+@media (max-width: 767px) {
+ .mobile-config-list {
+ display: block;
+ padding-bottom: 5.5rem;
+ }
+
+ .mobile-config-list__heading {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin: 1.25rem 0 0.75rem;
+ }
+
+ .mobile-config-list__heading h2,
+ .mobile-config-card h3 {
+ margin: 0;
+ color: #1f2937;
+ }
+
+ .mobile-config-list__heading h2 {
+ font-size: 1.125rem;
+ }
+
+ .mobile-config-list__heading p,
+ .mobile-config-card__header p,
+ .mobile-config-card small {
+ margin: 0.15rem 0 0;
+ color: #6b7280;
+ font-size: 0.75rem;
+ }
+
+ .mobile-config-list__filters {
+ display: grid;
+ gap: 0.6rem;
+ margin-bottom: 0.75rem;
+ }
+
+ .mobile-config-list__search input {
+ box-sizing: border-box;
+ width: 100%;
+ min-height: 2.75rem;
+ border: 1px solid #cbd5e1;
+ border-radius: 6px;
+ padding: 0.5rem 0.75rem;
+ background: #fff;
+ }
+
+ .mobile-config-list__filter-tabs {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 0.35rem;
+ }
+
+ .mobile-config-list__filter-tabs button {
+ width: auto;
+ min-height: 2.35rem;
+ border: 1px solid #cbd5e1;
+ border-radius: 5px;
+ background: #fff;
+ color: #374151;
+ font-size: 0.8125rem;
+ }
+
+ .mobile-config-list__filter-tabs button.is-selected {
+ border-color: #2563eb;
+ background: #2563eb;
+ color: #fff;
+ }
+
+ .mobile-config-list__cards {
+ display: grid;
+ gap: 0.75rem;
+ }
+
+ .mobile-config-card {
+ border: 1px solid #d1d5db;
+ border-radius: 6px;
+ background: #fff;
+ padding: 0.9rem;
+ }
+
+ .mobile-config-card__header,
+ .mobile-config-card__switch-row,
+ .mobile-config-card__check-row {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 0.75rem;
+ }
+
+ .mobile-config-card__slots {
+ margin-top: 0.45rem !important;
+ }
+
+ .mobile-config-card__store-link {
+ flex: 0 0 auto;
+ width: 2.5rem;
+ height: 2.5rem;
+ display: grid;
+ place-items: center;
+ border: 1px solid #cbd5e1;
+ border-radius: 5px;
+ color: #475569;
+ }
+
+ .mobile-config-card__store-link svg {
+ width: 1.15rem;
+ height: 1.15rem;
+ }
+
+ .mobile-config-card__switch-row {
+ margin-top: 0.9rem;
+ padding: 0.75rem 0;
+ border-top: 1px solid #e5e7eb;
+ border-bottom: 1px solid #e5e7eb;
+ }
+
+ .mobile-config-card input[type='checkbox'] {
+ width: 1.2rem;
+ height: 1.2rem;
+ flex: 0 0 auto;
+ }
+
+ .mobile-config-card__core-fields {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) minmax(0, 1.35fr);
+ gap: 0.6rem;
+ margin: 0.85rem 0;
+ }
+
+ .mobile-config-card__core-fields label,
+ .mobile-config-card__range {
+ width: 100%;
+ min-width: 0;
+ display: grid;
+ gap: 0.25rem;
+ color: #4b5563;
+ font-size: 0.75rem;
+ text-align: left;
+ }
+
+ .mobile-config-card__core-fields select,
+ .mobile-config-card__range {
+ box-sizing: border-box;
+ min-height: 2.6rem;
+ border: 1px solid #cbd5e1;
+ border-radius: 5px;
+ background: #fff;
+ }
+
+ .mobile-config-card__range {
+ align-content: center;
+ padding: 0.4rem 0.55rem;
+ }
+
+ .mobile-config-card__range strong {
+ overflow: hidden;
+ color: #1f2937;
+ font-size: 0.8rem;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ .mobile-config-card__check-row {
+ justify-content: flex-start;
+ padding: 0.25rem 0;
+ color: #374151;
+ font-size: 0.875rem;
+ }
+
+ .mobile-config-card__check-row span {
+ display: grid;
+ gap: 0.1rem;
+ }
+
+ .mobile-config-card__details {
+ margin-top: 0.8rem;
+ border-top: 1px solid #e5e7eb;
+ }
+
+ .mobile-config-card__details summary {
+ padding: 0.75rem 0;
+ color: #374151;
+ font-size: 0.875rem;
+ font-weight: 600;
+ cursor: pointer;
+ }
+
+ .mobile-config-card__details[open] summary {
+ margin-bottom: 0.2rem;
+ }
+
+ .mobile-config-card__actions {
+ display: flex;
+ gap: 0.6rem;
+ margin-top: 0.65rem;
+ }
+
+ .mobile-config-card__actions button,
+ .mobile-config-list__reset,
+ .mobile-config-list__save {
+ min-height: 2.5rem;
+ border: 1px solid #cbd5e1;
+ border-radius: 5px;
+ padding: 0.5rem 0.75rem;
+ background: #fff;
+ color: #374151;
+ font-size: 0.875rem;
+ }
+
+ .mobile-config-card__actions button {
+ width: auto;
+ flex: 1;
+ }
+
+ .mobile-config-card__actions button.is-danger {
+ border-color: #fecaca;
+ color: #b91c1c;
+ }
+
+ .mobile-config-list__empty {
+ margin: 1.5rem 0;
+ text-align: center;
+ color: #6b7280;
+ font-size: 0.875rem;
+ }
+
+ .mobile-config-list__save-bar {
+ position: fixed;
+ z-index: 30;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ display: grid;
+ grid-template-columns: 1fr 1.4fr;
+ gap: 0.6rem;
+ padding: 0.7rem 1rem calc(0.7rem + env(safe-area-inset-bottom));
+ border-top: 1px solid #cbd5e1;
+ background: #fff;
+ box-shadow: 0 -4px 12px rgba(15, 23, 42, 0.12);
+ }
+
+ .mobile-config-list__save {
+ border-color: #2563eb;
+ background: #2563eb;
+ color: #fff;
+ font-weight: 600;
+ }
+
+ .desktop-config-table,
+ .desktop-config-actions {
+ display: none;
+ }
+
+ .App {
+ padding: 0;
+ }
+}
diff --git a/src/components/MobileConfigList.js b/src/components/MobileConfigList.js
new file mode 100644
index 0000000..29c54b2
--- /dev/null
+++ b/src/components/MobileConfigList.js
@@ -0,0 +1,181 @@
+import { useMemo, useState } from 'react';
+import './MobileConfigList.css';
+
+const FILTER_OPTIONS = [
+ { value: 'all', label: 'Alle' },
+ { value: 'active', label: 'Aktiv' },
+ { value: 'inactive', label: 'Inaktiv' }
+];
+
+const MobileConfigList = ({
+ entries,
+ regularPickupMap,
+ weekdays,
+ formatRangeLabel,
+ formatRegularPickup,
+ onToggleActive,
+ onToggleAutoDeactivate,
+ onToggleProfileCheck,
+ onToggleOnlyNotify,
+ onToggleDormantSkip,
+ onWeekdayChange,
+ onRangePickerRequest,
+ onHideEntry,
+ onDeleteEntry,
+ canDelete,
+ isDirty,
+ onSaveConfig,
+ onResetConfig
+}) => {
+ const [search, setSearch] = useState('');
+ const [activityFilter, setActivityFilter] = useState('all');
+
+ const filteredEntries = useMemo(() => {
+ const normalizedSearch = search.trim().toLocaleLowerCase('de-DE');
+ return (Array.isArray(entries) ? entries : []).filter((entry) => {
+ if (activityFilter === 'active' && !entry.active) {
+ return false;
+ }
+ if (activityFilter === 'inactive' && entry.active) {
+ return false;
+ }
+ if (!normalizedSearch) {
+ return true;
+ }
+ return `${entry.label || ''} ${entry.id || ''}`.toLocaleLowerCase('de-DE').includes(normalizedSearch);
+ });
+ }, [activityFilter, entries, search]);
+
+ return (
+
+
+
+
Slot-Konfiguration
+
{entries.length} Betriebe
+
+
+
+
+
+
+ {FILTER_OPTIONS.map((option) => (
+
+ ))}
+
+
+
+
+
+ {filteredEntries.length === 0 && Keine passenden Betriebe gefunden.
}
+
+ {isDirty && (
+
+
+
+
+ )}
+
+ );
+};
+
+export default MobileConfigList;
diff --git a/src/components/MobileConfigList.test.js b/src/components/MobileConfigList.test.js
new file mode 100644
index 0000000..ca133f7
--- /dev/null
+++ b/src/components/MobileConfigList.test.js
@@ -0,0 +1,65 @@
+import { fireEvent, render, screen } from '@testing-library/react';
+import MobileConfigList from './MobileConfigList';
+
+const entry = {
+ id: '33875',
+ label: 'Balkanbäckerei',
+ active: false,
+ autoDeactivate: true,
+ checkProfileId: true,
+ onlyNotify: false,
+ skipDormantCheck: false
+};
+
+const props = {
+ entries: [entry, { ...entry, id: '69375', label: 'Donat', active: true }],
+ regularPickupMap: {},
+ weekdays: ['Montag', 'Dienstag'],
+ formatRangeLabel: () => 'Kein Zeitraum',
+ formatRegularPickup: () => null,
+ onToggleActive: jest.fn(),
+ onToggleAutoDeactivate: jest.fn(),
+ onToggleProfileCheck: jest.fn(),
+ onToggleOnlyNotify: jest.fn(),
+ onToggleDormantSkip: jest.fn(),
+ onWeekdayChange: jest.fn(),
+ onRangePickerRequest: jest.fn(),
+ onHideEntry: jest.fn(),
+ onDeleteEntry: jest.fn(),
+ canDelete: true,
+ isDirty: true,
+ onSaveConfig: jest.fn(),
+ onResetConfig: jest.fn()
+};
+
+describe('MobileConfigList', () => {
+ beforeEach(() => jest.clearAllMocks());
+
+ it('filters entries by search and activity', () => {
+ render(
);
+
+ fireEvent.click(screen.getByRole('button', { name: 'Aktiv' }));
+ expect(screen.getByText('Donat')).toBeInTheDocument();
+ expect(screen.queryByText('Balkanbäckerei')).not.toBeInTheDocument();
+
+ fireEvent.click(screen.getByRole('button', { name: 'Alle' }));
+ fireEvent.change(screen.getByPlaceholderText('Betrieb oder ID suchen'), { target: { value: '33875' } });
+ expect(screen.getByText('Balkanbäckerei')).toBeInTheDocument();
+ expect(screen.queryByText('Donat')).not.toBeInTheDocument();
+ });
+
+ it('uses the existing configuration callbacks', () => {
+ render(
);
+
+ fireEvent.click(screen.getByLabelText('Balkanbäckerei automatisch buchen'));
+ fireEvent.click(screen.getAllByText('Kein Zeitraum')[0]);
+ fireEvent.click(screen.getAllByText('Weitere Optionen')[0]);
+ fireEvent.click(screen.getAllByLabelText('Profil prüfen')[0]);
+ fireEvent.click(screen.getByText('Speichern'));
+
+ expect(props.onToggleActive).toHaveBeenCalledWith('33875');
+ expect(props.onRangePickerRequest).toHaveBeenCalledWith('33875');
+ expect(props.onToggleProfileCheck).toHaveBeenCalledWith('33875');
+ expect(props.onSaveConfig).toHaveBeenCalledTimes(1);
+ });
+});
diff --git a/src/components/RangePickerModal.js b/src/components/RangePickerModal.js
index b145459..e9f498b 100644
--- a/src/components/RangePickerModal.js
+++ b/src/components/RangePickerModal.js
@@ -121,9 +121,9 @@ const RangePickerModal = ({
};
return (
-
+
event.stopPropagation()}
>