-
Notifications
You must be signed in to change notification settings - Fork 1
Description
What needs to be built?
A dialog modal for viewing and managing group members. Displays the group name, member count, a search input, and a scrollable list of members with avatars, names, and checkboxes. The group owner is labeled "(owner)" and always appears first. Opens when the user clicks the "+" button in the edit modal or "View All" in the detail view modal.
Design reference
Documentation
Existing patterns:
src/components/ui/dialog.tsx— shadcn Dialogsrc/components/ui/checkbox.tsx— shadcn Checkboxsrc/components/ui/input.tsx— shadcn Inputsrc/components/ui/avatar.tsx— shadcn Avatarsrc/utils/avatar.ts—getInitials()andgetAvatarColor()src/schema/contact-group.ts—AddMembersSchema(groupId + members array)src/services/contact-group.ts—EnrichedGroupMembertype (hasownername,owneremail)
Official docs:
- shadcn Dialog
- shadcn Checkbox
- Lucide Icons — Search
Technical notes
Create the component at src/components/groups/add-members-modal.tsx.
Modal layout:
- Header:
- Group name as title (bold)
- "members (N)" subtitle showing current count
- Search input: Text input with Search icon, placeholder "Search or add members". Filters the member list client-side by name (case-insensitive substring match).
- Member list (scrollable, max height ~400px):
- Each row: Avatar (initials + color) + member name + checkbox (right side)
- First row: owner with "(owner)" label after name
- Owner checkbox is always checked and disabled (cannot remove the owner)
- Checked = member is in the group, unchecked = not in the group
- Toggling a checkbox calls
onSelectionChange
- Footer: "Save" button (Paso Brown fill) — calls
onConfirm - Close: X button
interface MemberRow {
memberId: number;
ownername: string;
isOwner: boolean;
isSelected: boolean;
}
interface AddMembersModalProps {
open: boolean;
onOpenChange: (open: boolean) => void;
groupName: string;
members: MemberRow[];
onSelectionChange: (memberId: number, selected: boolean) => void;
onConfirm: () => void;
isSubmitting?: boolean;
}The parent (tech lead) provides the full member list with selection state. The modal handles local search filtering but delegates selection changes to the parent via onSelectionChange. This keeps the modal stateless regarding member selection — the parent manages the source of truth.
Search behavior: Filter the members array by ownername matching the search input. Owner always stays visible regardless of search term. Clear search when modal closes.
Acceptance criteria
- Component created at
src/components/groups/add-members-modal.tsx - Uses shadcn Dialog for modal overlay
- Group name displays as title, "members (N)" as subtitle
- Search input with Search icon and "Search or add members" placeholder
- Search filters member list by name (case-insensitive)
- Member list is scrollable with a max height (~400px)
- Each member row: avatar (initials + color) + name + checkbox
- Owner row shows "(owner)" label and checkbox is checked + disabled
- Owner always appears first regardless of search filter
-
onSelectionChange(memberId, selected)fires when a checkbox is toggled - "Save" button with Paso Brown fill calls
onConfirm - Save button disabled and shows loading when
isSubmittingis true - Search resets when modal closes
- X close button dismisses the modal
- Exported as named export
-
npm run buildpasses