From 77284cbf1078a91f9e8323ecc5d552b4fc2913c3 Mon Sep 17 00:00:00 2001 From: Agent Zero Date: Tue, 28 Jul 2026 12:14:45 +0200 Subject: [PATCH] feat: custom views accordion - save/apply/delete named views with filter+sort+group config --- frontend/src/pages/ContactsList.tsx | 120 +++++++++++++++++++++++----- 1 file changed, 100 insertions(+), 20 deletions(-) diff --git a/frontend/src/pages/ContactsList.tsx b/frontend/src/pages/ContactsList.tsx index dd82fc6..2ca1a4a 100644 --- a/frontend/src/pages/ContactsList.tsx +++ b/frontend/src/pages/ContactsList.tsx @@ -19,7 +19,7 @@ import { SavedFilters } from '@/components/SavedFilters'; import { SavedFilterBar } from '@/components/common/SavedFilterBar'; import { TagSelector } from '@/components/tags/TagSelector'; import type { Tag } from '@/api/tags'; -import { ArrowDownAZ, ArrowUpZA, Bookmark, ChevronLeft, ExternalLink, LayoutGrid, List, Plus, Printer, Table2 } from 'lucide-react'; +import { ArrowDownAZ, ArrowUpZA, Bookmark, ChevronLeft, ExternalLink, LayoutGrid, List, Plus, Printer, Table2, X } from 'lucide-react'; import { FilterPanel, applyFilters, emptyFilterState, type FilterState } from '@/components/contacts/FilterPanel'; import { SortPanel, applySorting, emptySortState, type SortState } from '@/components/contacts/SortPanel'; import { GroupPanel, applyGrouping, emptyGroupState, type GroupState, type GroupedContacts } from '@/components/contacts/GroupPanel'; @@ -50,6 +50,8 @@ export function ContactsListPage() { const [groupState, setGroupState] = useState(emptyGroupState); const [viewsOpen, setViewsOpen] = useState(true); const [foldersOpen, setFoldersOpen] = useState(true); + const [savedViews, setSavedViews] = useState<{ id: string; name: string; filterState: FilterState; sortState: SortState; groupState: GroupState; selectedFilter: ContactFilter; viewMode: ContactViewMode }[]>([]); + const [activeViewId, setActiveViewId] = useState(null); const openWindow = useWindowStore((s) => s.openWindow); // Debounce search @@ -195,6 +197,40 @@ export function ContactsListPage() { setActiveView('list'); }, []); + // Save current view configuration as a custom view + const handleSaveView = useCallback(() => { + const name = window.prompt('Name für diese Ansicht:', 'Meine Ansicht'); + if (!name) return; + const id = `view-${Date.now()}`; + setSavedViews((prev) => [...prev, { + id, + name, + filterState: { ...filterState }, + sortState: { ...sortState }, + groupState: { ...groupState }, + selectedFilter, + viewMode, + }]); + setActiveViewId(id); + }, [filterState, sortState, groupState, selectedFilter, viewMode]); + + // Apply a saved custom view + const applySavedView = useCallback((view: { id: string; filterState: FilterState; sortState: SortState; groupState: GroupState; selectedFilter: ContactFilter; viewMode: ContactViewMode }) => { + setFilterState({ ...view.filterState }); + setSortState({ ...view.sortState }); + setGroupState({ ...view.groupState }); + setSelectedFilter(view.selectedFilter); + setViewMode(view.viewMode); + setActiveViewId(view.id); + setPage(1); + }, []); + + // Delete a saved custom view + const deleteSavedView = useCallback((id: string) => { + setSavedViews((prev) => prev.filter((v) => v.id !== id)); + if (activeViewId === id) setActiveViewId(null); + }, [activeViewId]); + // Sort options const sortOptions = useMemo(() => [ { value: 'displayname', label: t('contacts.fullName') }, @@ -334,7 +370,7 @@ export function ContactsListPage() { ]; registerItems('contacts', items); return () => unregisterPlugin('contacts'); - }, [handleSearch, handleCreate, handleSelectFilter, t, registerItems, unregisterPlugin, selectedFilter, viewMode, sortOptions, viewModeOptions, filterState, contactType, sortState, groupState]); + }, [handleSearch, handleCreate, handleSelectFilter, handleSaveView, applySavedView, deleteSavedView, t, registerItems, unregisterPlugin, selectedFilter, viewMode, sortOptions, viewModeOptions, filterState, contactType, sortState, groupState, savedViews, activeViewId]); return (
@@ -349,7 +385,7 @@ export function ContactsListPage() { data-testid="contact-folder-pane" >
- {/* Accordion 1: Views */} + {/* Accordion 1: Custom Views */}
{viewsOpen && (
- {[ - { value: 'list', label: t('contacts.viewList'), icon: }, - { value: 'table', label: t('contacts.viewTable'), icon: }, - { value: 'cards', label: t('contacts.viewCards'), icon: }, - ].map((opt) => ( - - ))} + {/* Default view */} + + {/* Placeholder for saved custom views */} + {savedViews.length === 0 ? ( +
+

Keine benutzerdefinierten Ansichten

+ +
+ ) : ( + <> + {savedViews.map((view) => ( + + + ))} + + + )}
)}