User select dropdowns: group all staff by department (Claims first, then alphabetical)
This commit is contained in:
parent
ff6135da5c
commit
ceb82c9ef7
1 changed files with 46 additions and 35 deletions
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue