From 41ed69a058d30c9af60f2acfbb3d4a2a7e6c9765 Mon Sep 17 00:00:00 2001 From: Meik Date: Mon, 10 Nov 2025 17:54:06 +0100 Subject: [PATCH] Feat: Geolocation --- src/components/DashboardView.js | 448 ++++++++++++++++++++++++-------- 1 file changed, 341 insertions(+), 107 deletions(-) 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 = ({ )} -
+
- - - - - - - - - - + + {configTable.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + ))} + + ))} - {visibleConfig.map((item, index) => { - const normalizedRange = item.desiredDateRange - ? { ...item.desiredDateRange } - : item.desiredDate - ? { start: item.desiredDate, end: item.desiredDate } - : null; - const rangeStart = normalizedRange?.start || ''; - const rangeEnd = normalizedRange?.end || ''; - return ( - - + {row.getVisibleCells().map((cell) => ( + - - - - - - - - ); - })} + ))} + + ))}
AktivBetriebProfil prüfenNur benachrichtigenWochentagDatum / ZeitraumAktionen
+ {flexRender(header.column.columnDef.header, header.getContext())} +
- onToggleActive(item.id)} - className="h-5 w-5" - /> + {configTable.getRowModel().rows.map((row, idx) => ( +
+ {flexRender(cell.column.columnDef.cell, cell.getContext())} - {item.label} - {item.hidden && (ausgeblendet)} - - onToggleProfileCheck(item.id)} - className="h-5 w-5" - /> - - onToggleOnlyNotify(item.id)} - className="h-5 w-5" - /> - - - - - -
- - {canDelete && ( - - )} -
-