diff --git a/src/components/DashboardView.js b/src/components/DashboardView.js
index ed893a5..0dbfb42 100644
--- a/src/components/DashboardView.js
+++ b/src/components/DashboardView.js
@@ -1,6 +1,287 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
+import {
+ createColumnHelper,
+ flexRender,
+ getCoreRowModel,
+ getFilteredRowModel,
+ getSortedRowModel,
+ useReactTable
+} from '@tanstack/react-table';
import NotificationPanel from './NotificationPanel';
+const CONFIG_TABLE_STATE_KEY = 'dashboardConfigTableState';
+const columnHelper = createColumnHelper();
+
+const ColumnTextFilter = ({ column, placeholder }) => {
+ if (!column.getCanFilter()) {
+ return null;
+ }
+ const configTableData = useMemo(() => {
+ return Array.isArray(visibleConfig)
+ ? visibleConfig.map((item) => ({
+ ...item,
+ normalizedLabel: (item.label || '').toLowerCase()
+ }))
+ : [];
+ }, [visibleConfig]);
+
+ const weekdaysOptions = useMemo(
+ () =>
+ weekdays.map((day) => ({
+ value: day,
+ label: day
+ })),
+ [weekdays]
+ );
+
+ const configColumns = useMemo(
+ () => [
+ columnHelper.display({
+ id: 'active',
+ header: () => Aktiv,
+ cell: ({ row }) => (
+
+ onToggleActive(row.original.id)}
+ />
+
+ ),
+ enableSorting: false,
+ enableColumnFilter: false
+ }),
+ columnHelper.accessor('label', {
+ header: ({ column }) => (
+
+
+
+
+ ),
+ cell: ({ row }) => (
+
+
{row.original.label}
+ {row.original.hidden &&
(ausgeblendet)}
+
#{row.original.id}
+
+ ),
+ sortingFn: 'alphanumeric',
+ filterFn: 'includesString'
+ }),
+ columnHelper.display({
+ id: 'checkProfileId',
+ header: () => Profil prüfen,
+ cell: ({ row }) => (
+
+ onToggleProfileCheck(row.original.id)}
+ />
+
+ ),
+ enableSorting: false,
+ enableColumnFilter: false
+ }),
+ columnHelper.accessor((row) => row.onlyNotify, {
+ id: 'onlyNotify',
+ header: ({ column }) => (
+
+ Nur benachrichtigen
+
+
+ ),
+ cell: ({ row }) => (
+
+ onToggleOnlyNotify(row.original.id)}
+ />
+
+ ),
+ filterFn: (row, columnId, value) => {
+ if (value === undefined) {
+ return true;
+ }
+ const boolValue = value === 'true';
+ return row.getValue(columnId) === boolValue;
+ },
+ sortingFn: (rowA, rowB, columnId) => {
+ const a = rowA.getValue(columnId);
+ const b = rowB.getValue(columnId);
+ return Number(b) - Number(a);
+ }
+ }),
+ columnHelper.accessor('desiredWeekday', {
+ header: ({ column }) => (
+
+ Wochentag
+
+
+ ),
+ cell: ({ row }) => (
+
+ ),
+ filterFn: (row, columnId, value) => {
+ if (!value) {
+ return true;
+ }
+ return (row.getValue(columnId) || '') === value;
+ },
+ sortingFn: 'alphanumeric'
+ }),
+ columnHelper.display({
+ id: 'dateRange',
+ header: () => Datum / Zeitraum,
+ cell: ({ row }) => {
+ const normalizedRange = row.original.desiredDateRange
+ ? { ...row.original.desiredDateRange }
+ : row.original.desiredDate
+ ? { start: row.original.desiredDate, end: row.original.desiredDate }
+ : null;
+ const rangeStart = normalizedRange?.start || '';
+ const rangeEnd = normalizedRange?.end || '';
+ return (
+
+ );
+ },
+ enableSorting: false,
+ enableColumnFilter: false
+ }),
+ columnHelper.display({
+ id: 'actions',
+ header: () => Aktionen,
+ cell: ({ row }) => (
+
+
+ {canDelete && (
+
+ )}
+
+ ),
+ enableSorting: false,
+ enableColumnFilter: false
+ })
+ ],
+ [
+ onDeleteEntry,
+ onHideEntry,
+ onRangePickerRequest,
+ onToggleActive,
+ onToggleOnlyNotify,
+ onToggleProfileCheck,
+ onWeekdayChange,
+ formatRangeLabel,
+ canDelete,
+ weekdays,
+ weekdaysOptions
+ ]
+ );
+
+ const configTable = useReactTable({
+ data: configTableData,
+ columns: configColumns,
+ state: {
+ sorting: tableSorting,
+ columnFilters: tableFilters
+ },
+ onSortingChange: setTableSorting,
+ onColumnFiltersChange: setTableFilters,
+ getCoreRowModel: getCoreRowModel(),
+ getSortedRowModel: getSortedRowModel(),
+ getFilteredRowModel: getFilteredRowModel()
+ });
+
+ return (
+ column.setFilterValue(event.target.value || undefined)}
+ placeholder={placeholder}
+ className="mt-1 w-full rounded border px-2 py-1 text-xs focus:outline-none focus:ring-1 focus:ring-blue-500"
+ />
+ );
+};
+
+const ColumnSelectFilter = ({ column, options, placeholder = 'Alle' }) => {
+ if (!column.getCanFilter()) {
+ return null;
+ }
+ return (
+
+ );
+};
+
const DashboardView = ({
session,
onRefresh,
@@ -38,6 +319,27 @@ const DashboardView = ({
locationError,
onUpdateLocation
}) => {
+ const loadTableState = useCallback(() => {
+ if (typeof window === 'undefined') {
+ return { sorting: [], columnFilters: [] };
+ }
+ try {
+ const raw = window.localStorage.getItem(CONFIG_TABLE_STATE_KEY);
+ if (!raw) {
+ return { sorting: [], columnFilters: [] };
+ }
+ const parsed = JSON.parse(raw);
+ return {
+ sorting: Array.isArray(parsed.sorting) ? parsed.sorting : [],
+ columnFilters: Array.isArray(parsed.columnFilters) ? parsed.columnFilters : []
+ };
+ } catch {
+ return { sorting: [], columnFilters: [] };
+ }
+ }, []);
+
+ const initialTableState = loadTableState();
+
useEffect(() => {
if (!focusedStoreId) {
return;
@@ -60,6 +362,22 @@ const DashboardView = ({
}, [focusedStoreId, onClearFocus]);
const [geoBusy, setGeoBusy] = useState(false);
const [geoError, setGeoError] = useState('');
+ const [tableSorting, setTableSorting] = useState(initialTableState.sorting);
+ const [tableFilters, setTableFilters] = useState(initialTableState.columnFilters);
+
+ useEffect(() => {
+ if (typeof window === 'undefined') {
+ return;
+ }
+ try {
+ window.localStorage.setItem(
+ CONFIG_TABLE_STATE_KEY,
+ JSON.stringify({ sorting: tableSorting, columnFilters: tableFilters })
+ );
+ } catch {
+ /* ignore */
+ }
+ }, [tableSorting, tableFilters]);
const handleDetectLocation = useCallback(() => {
if (!navigator.geolocation) {
@@ -284,117 +602,33 @@ const DashboardView = ({
)}
-