diff --git a/src/components/common/DataGrid.tsx b/src/components/common/DataGrid.tsx index 19f167da9..95de94981 100644 --- a/src/components/common/DataGrid.tsx +++ b/src/components/common/DataGrid.tsx @@ -1,5 +1,15 @@ import type { UseQueryResult } from '@tanstack/react-query'; -import { Column, Row, SearchField } from '@umami/react-zen'; +import { + Button, + Column, + Icon, + Row, + SearchField, + Text, + Tooltip, + TooltipTrigger, +} from '@umami/react-zen'; +import { LayoutGrid, Table2 } from 'lucide-react'; import { cloneElement, isValidElement, @@ -12,9 +22,13 @@ import { Empty } from '@/components/common/Empty'; import { LoadingPanel } from '@/components/common/LoadingPanel'; import { Pager } from '@/components/common/Pager'; import { useMessages, useMobile, useNavigation } from '@/components/hooks'; +import { getItem, setItem } from '@/lib/storage'; import type { PageResult } from '@/lib/types'; const DEFAULT_SEARCH_DELAY = 600; +const DISPLAY_MODE_STORAGE_KEY = 'umami.datagrid.displayMode'; + +type DisplayMode = 'table' | 'cards'; export interface DataGridProps { query: UseQueryResult, any>; @@ -43,7 +57,23 @@ export function DataGrid({ const [search, setSearch] = useState(queryParams?.search || data?.search || ''); const showPager = allowPaging && data && data.count > 0; const { isMobile } = useMobile(); - const displayMode = isMobile ? 'cards' : undefined; + const [userDisplayMode, setUserDisplayMode] = useState(() => { + // localStorage can hold anything (extensions, manual edits, schema drift), + // so accept only the two values we know how to render and otherwise fall + // back to the useMobile-driven default. + const stored = getItem(DISPLAY_MODE_STORAGE_KEY); + return stored === 'table' || stored === 'cards' ? stored : null; + }); + + // Effective mode: explicit user choice wins, otherwise fall back to the + // mobile-driven default (cards on small viewports, table elsewhere). + const displayMode: DisplayMode | undefined = userDisplayMode ?? (isMobile ? 'cards' : undefined); + + const handleToggleDisplayMode = () => { + const next: DisplayMode = displayMode === 'cards' ? 'table' : 'cards'; + setItem(DISPLAY_MODE_STORAGE_KEY, next); + setUserDisplayMode(next); + }; const handleSearch = (value: string) => { if (value !== search) { @@ -61,29 +91,42 @@ export function DataGrid({ const child = data ? (typeof children === 'function' ? children(data) : children) : null; + const viewToggleButton = ( + + + + {displayMode === 'cards' ? 'Switch to table view' : 'Switch to card view'} + + + ); + return ( - - + + {allowSearch && ( - - - {renderActions?.()} - + )} - - {data && ( + + {renderActions?.()} + {viewToggleButton} + + + + {data && (
+ {isValidElement(child) ? cloneElement(child as ReactElement, { displayMode }) : child} +
)}