From 33aae769e4e140e428e7e02a4f313dd49f1fe3a4 Mon Sep 17 00:00:00 2001 From: Agent Zero Date: Tue, 28 Jul 2026 15:26:46 +0200 Subject: [PATCH] feat: tree grouping for list and cards views (matching table view) --- .../src/components/contacts/ContactList.tsx | 80 ++++++++++++++++++- 1 file changed, 78 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/contacts/ContactList.tsx b/frontend/src/components/contacts/ContactList.tsx index 1fdafde..29c092e 100644 --- a/frontend/src/components/contacts/ContactList.tsx +++ b/frontend/src/components/contacts/ContactList.tsx @@ -475,10 +475,48 @@ export function ContactList({ ); + // Group header renderer for list view + const renderListGroupHeader = (group: GroupedContacts) => { + const isExpanded = expandedGroups.has(group.key); + return ( +
+ + {group.label} + ({group.contacts.length}) +
+ ); + }; + return (
- {shouldVirtualize ? ( + {isGrouped && groupedContacts ? ( +
+ {groupedContacts.map((group) => ( +
+ {renderListGroupHeader(group)} + {expandedGroups.has(group.key) && ( +
    + {group.contacts.map((contact) => renderContactItem(contact))} +
+ )} +
+ ))} +
+ ) : shouldVirtualize ? (
    {rowVirtualizer.getVirtualItems().map((virtualRow) => { const contact = contacts[virtualRow.index]; @@ -826,10 +864,48 @@ export function ContactList({
); + // Group header renderer for cards view + const renderCardsGroupHeader = (group: GroupedContacts) => { + const isExpanded = expandedGroups.has(group.key); + return ( +
+ + {group.label} + ({group.contacts.length}) +
+ ); + }; + return (
- {shouldVirtualize ? ( + {isGrouped && groupedContacts ? ( +
+ {groupedContacts.map((group) => ( +
+ {renderCardsGroupHeader(group)} + {expandedGroups.has(group.key) && ( +
+ {group.contacts.map((contact) => renderContactCard(contact))} +
+ )} +
+ ))} +
+ ) : shouldVirtualize ? (
{rowVirtualizer.getVirtualItems().map((virtualRow) => {