User select dropdowns: group all staff by department (Claims first, then alphabetical)

This commit is contained in:
lorentz 2026-04-09 15:44:52 +00:00
parent ff6135da5c
commit ceb82c9ef7

View file

@ -1,8 +1,7 @@
'use client'
import { useState } from 'react'
import { useMemo } from 'react'
import { SelectContent, SelectGroup, SelectItem, SelectLabel } from '@/components/ui/select'
import { ChevronDown } from 'lucide-react'
export interface SelectableUser {
id: string
@ -17,49 +16,61 @@ interface UserSelectContentProps {
prefixItem?: React.ReactNode
}
export function UserSelectContent({ users, excludeIds = [], prefixItem }: UserSelectContentProps) {
const [showAll, setShowAll] = useState(false)
/** Pretty-print a department slug (e.g. "private-client" → "Private Client") */
function formatDepartment(dept: string): string {
return dept
.split('-')
.map((w) => w.charAt(0).toUpperCase() + w.slice(1))
.join(' ')
}
const eligible = users.filter((u) => !excludeIds.includes(u.id))
const claims = eligible.filter((u) => u.department?.toLowerCase().includes('claims'))
const rest = eligible.filter((u) => !u.department?.toLowerCase().includes('claims'))
/** Departments shown first in this order; everything else follows alphabetically */
const PRIORITY_DEPARTMENTS = ['claims']
export function UserSelectContent({ users, excludeIds = [], prefixItem }: UserSelectContentProps) {
const grouped = useMemo(() => {
const eligible = users.filter((u) => !excludeIds.includes(u.id))
const map = new Map<string, SelectableUser[]>()
for (const u of eligible) {
const key = (u.department || 'other').toLowerCase()
if (!map.has(key)) map.set(key, [])
map.get(key)!.push(u)
}
// Sort users within each group
for (const list of map.values()) {
list.sort((a, b) =>
(a.displayName || a.email).localeCompare(b.displayName || b.email)
)
}
// Sort department keys: priority first, then alphabetical
const keys = [...map.keys()].sort((a, b) => {
const ai = PRIORITY_DEPARTMENTS.indexOf(a)
const bi = PRIORITY_DEPARTMENTS.indexOf(b)
if (ai !== -1 && bi !== -1) return ai - bi
if (ai !== -1) return -1
if (bi !== -1) return 1
return a.localeCompare(b)
})
return keys.map((key) => ({ key, label: formatDepartment(key), users: map.get(key)! }))
}, [users, excludeIds])
return (
<SelectContent>
{prefixItem}
{claims.length > 0 && (
<SelectGroup>
<SelectLabel>Claims</SelectLabel>
{claims.map((u) => (
{grouped.map((group) => (
<SelectGroup key={group.key}>
<SelectLabel>{group.label}</SelectLabel>
{group.users.map((u) => (
<SelectItem key={u.id} value={u.id}>
{u.displayName || u.email}
</SelectItem>
))}
</SelectGroup>
)}
{rest.length > 0 && (
<SelectGroup>
<SelectLabel
className="flex items-center justify-between cursor-pointer select-none hover:text-foreground"
onClick={(e) => {
e.preventDefault()
e.stopPropagation()
setShowAll((v) => !v)
}}
>
<span>All Staff</span>
<ChevronDown
className={`h-3.5 w-3.5 transition-transform ${showAll ? 'rotate-180' : ''}`}
/>
</SelectLabel>
{showAll &&
rest.map((u) => (
<SelectItem key={u.id} value={u.id}>
{u.displayName || u.email}
</SelectItem>
))}
</SelectGroup>
)}
))}
</SelectContent>
)
}