"use client"
import {
type HTMLAttributes,
type ImgHTMLAttributes,
useMemo,
useState,
} from "react"
import {
Autocomplete,
AutocompleteCollection,
AutocompleteContent,
AutocompleteEmpty,
AutocompleteGroup,
AutocompleteGroupLabel,
AutocompleteInput,
AutocompleteItem,
AutocompleteList,
} from "@/components/ui/reui-autocomplete"
import { Autocomplete as AutocompletePrimitive } from "@base-ui/react/autocomplete"
function Avatar({ className = "", ...props }: HTMLAttributes<HTMLDivElement>) {
return (
<div
className={`relative flex shrink-0 overflow-hidden rounded-full bg-muted ${className}`}
{...props}
/>
)
}
function AvatarImage(props: ImgHTMLAttributes<HTMLImageElement>) {
return <img className="absolute inset-0 size-full object-cover" {...props} />
}
function AvatarFallback({
className = "",
...props
}: HTMLAttributes<HTMLDivElement>) {
return (
<div
className={`flex size-full items-center justify-center text-xs font-medium ${className}`}
{...props}
/>
)
}
export default function Pattern() {
const [value, setValue] = useState("")
const [open, setOpen] = useState(false)
const { contains } = AutocompletePrimitive.useFilter({ sensitivity: "base" })
const filteredItems = useMemo(() => {
if (!value) return groupedUsers
return groupedUsers
.map((group) => ({
...group,
items: (group.items || []).filter(
(item) =>
contains(item.name || "", value) ||
contains(item.group || "", value) ||
contains(item.position || "", value)
),
}))
.filter((group) => group.items && group.items.length > 0)
}, [value, contains])
return (
<div className="w-full max-w-xs">
<Autocomplete
items={filteredItems}
value={value}
onValueChange={setValue}
open={open}
onOpenChange={setOpen}
itemToStringValue={(item: unknown) => (item as User).name}
filter={null}
>
<AutocompleteInput placeholder="e.g. John, Developer, Marketing" />
{open && (
<AutocompleteContent className="pt-0">
{filteredItems.length === 0 ? (
<AutocompleteEmpty>No matching users found.</AutocompleteEmpty>
) : (
<AutocompleteList className="not-empty:py-0">
{(group: UserGroup) => (
<AutocompleteGroup
key={group.group}
items={group.items}
className="py-0"
>
<AutocompleteGroupLabel className="bg-background text-muted-foreground sticky top-0 z-10 me-1.5 py-2.5 text-xs font-medium">
{group.group}
</AutocompleteGroupLabel>
<AutocompleteCollection>
{(item: User) => (
<AutocompleteItem
key={item.id}
value={item}
className="flex items-center gap-2.5 rounded-lg"
>
<Avatar className="size-9">
<AvatarImage
src={item.avatar}
alt={item.name || "User"}
/>
<AvatarFallback>
{(item.name || "U")
.split(" ")
.map((n) => n[0])
.join("")}
</AvatarFallback>
</Avatar>
<div className="min-w-0 flex-1">
<div className="truncate font-medium">
{item.name || "Unknown"}
</div>
<div className="text-muted-foreground truncate text-sm">
{item.position || "No position available"}
</div>
</div>
</AutocompleteItem>
)}
</AutocompleteCollection>
</AutocompleteGroup>
)}
</AutocompleteList>
)}
</AutocompleteContent>
)}
</Autocomplete>
</div>
)
}
interface User {
id: string
name: string
group: string
position: string
avatar: string
status: "Active" | "Inactive" | "Away"
}
interface UserGroup {
group: string
items: User[]
}
const usersData: User[] = [
// Development Team
{
id: "john-doe",
name: "John Doe",
group: "Development Team",
position: "Senior Frontend Developer",
avatar: "https://cdn.21st.dev/assets/mirror/b1/b1f6209ae26207ebe11c243a659f0e5e15a0a48232261ecf3c05211a40af2225.jpg",
status: "Active",
},
{
id: "jane-smith",
name: "Jane Smith",
group: "Development Team",
position: "Full Stack Developer",
avatar: "https://cdn.21st.dev/assets/mirror/e7/e7a0b30cb92ca533b2f8dbf57649e4b60129a9e84f3fc36d45b09e2dfcaec61d.jpg",
status: "Active",
},
{
id: "mike-wilson",
name: "Mike Wilson",
group: "Development Team",
position: "Backend Developer",
avatar: "https://cdn.21st.dev/assets/mirror/4c/4cff4f892ece6dca0865313df96f11ac30e11b6dcbf3b9a86bad86a3049aa6e1.jpg",
status: "Active",
},
{
id: "sarah-johnson",
name: "Sarah Johnson",
group: "Development Team",
position: "DevOps Engineer",
avatar: "https://cdn.21st.dev/assets/mirror/55/55d0cf713811843ffbd3412ee403668a82597bb83aabbc684a87f66c1fc962e4.jpg",
status: "Away",
},
{
id: "david-brown",
name: "David Brown",
group: "Development Team",
position: "Mobile Developer",
avatar: "https://cdn.21st.dev/assets/mirror/32/32afb68c9233445d08f7c4af3e781f648c6eeeb7dadeb5bdd341a003684d1c93.jpg",
status: "Active",
},
{
id: "lisa-garcia",
name: "Lisa Garcia",
group: "Development Team",
position: "UI/UX Developer",
avatar: "https://cdn.21st.dev/assets/mirror/7f/7f2f1b6a4c09f5092437fe960232360d1e2dcf7a198c8580f3c5478c7b2d9386.jpg",
status: "Active",
},
// Design Team
{
id: "alex-martinez",
name: "Alex Martinez",
group: "Design Team",
position: "Lead UX Designer",
avatar: "https://cdn.21st.dev/assets/mirror/f2/f25b1b7a6a351c0f748d81bf4fcaf8c5a2f8ed036563c2693d4c1ca3718d9d5d.jpg",
status: "Active",
},
{
id: "emma-davis",
name: "Emma Davis",
group: "Design Team",
position: "UI Designer",
avatar: "https://cdn.21st.dev/assets/mirror/41/417105f5784df0a25c3486becfe5c967d448e3c98b3c0231ef4ea0c59d27cb4b.jpg",
status: "Active",
},
{
id: "chris-taylor",
name: "Chris Taylor",
group: "Design Team",
position: "Product Designer",
avatar: "https://cdn.21st.dev/assets/mirror/62/6252a3b6790cbb48919cb8ea756a4e1ce829f3271a141731226871b3c3df9d6d.jpg",
status: "Active",
},
{
id: "olivia-anderson",
name: "Olivia Anderson",
group: "Design Team",
position: "Visual Designer",
avatar: "https://cdn.21st.dev/assets/mirror/54/54ebea0e1cad66565de28318ff2f512398bf5732f6f3f3fecea8ad4338b78778.jpg",
status: "Inactive",
},
// Marketing Team
{
id: "james-moore",
name: "James Moore",
group: "Marketing Team",
position: "Marketing Manager",
avatar: "https://cdn.21st.dev/assets/mirror/21/21d6722e3baa38cb075afdc599a60c8861440a6dea431e7a8ff43f86d05190b9.jpg",
status: "Active",
},
{
id: "sophia-white",
name: "Sophia White",
group: "Marketing Team",
position: "Content Marketing Specialist",
avatar: "https://cdn.21st.dev/assets/mirror/20/20bb8458e0bb0345aae5ab6a975650d1210fdfc5721729b456f7342fc59b3113.jpg",
status: "Active",
},
{
id: "william-harris",
name: "William Harris",
group: "Marketing Team",
position: "Digital Marketing Specialist",
avatar: "https://cdn.21st.dev/assets/mirror/56/56eb13218bfcbdfa4f6f950990d4666d616ad1f9e17777c976ccc79c781b68c6.jpg",
status: "Active",
},
{
id: "ava-martin",
name: "Ava Martin",
group: "Marketing Team",
position: "Social Media Manager",
avatar: "https://cdn.21st.dev/assets/mirror/4c/4c5eaf184e978fcf67bed792f0fa88543b664347c98727aa25da4c16e32eb367.jpg",
status: "Away",
},
// Sales Team
{
id: "ethan-thompson",
name: "Ethan Thompson",
group: "Sales Team",
position: "Sales Director",
avatar: "https://cdn.21st.dev/assets/mirror/e8/e86e44eaa9cb076c9d359973ce68af0e0cd85bb5dac2e72b259582941a57621b.jpg",
status: "Active",
},
{
id: "mia-garcia",
name: "Mia Garcia",
group: "Sales Team",
position: "Account Executive",
avatar: "https://cdn.21st.dev/assets/mirror/cc/cc6b757fbf1174ae601b39aa711d6dfcda1b236001a2f3a67c4293d73c9fd714.jpg",
status: "Active",
},
{
id: "noah-martinez",
name: "Noah Martinez",
group: "Sales Team",
position: "Sales Representative",
avatar: "https://cdn.21st.dev/assets/mirror/e8/e86e44eaa9cb076c9d359973ce68af0e0cd85bb5dac2e72b259582941a57621b.jpg",
status: "Active",
},
{
id: "isabella-rodriguez",
name: "Isabella Rodriguez",
group: "Sales Team",
position: "Business Development Manager",
avatar: "https://cdn.21st.dev/assets/mirror/1c/1c09f1ab9440e4f0922ad6c7494fb9efa1ae446ac5d39c4fb5843ed3fbc5c952.jpg",
status: "Active",
},
// Management Team
{
id: "lucas-lee",
name: "Lucas Lee",
group: "Management Team",
position: "CEO",
avatar: "https://cdn.21st.dev/assets/mirror/c4/c493b0a6d9a42ed0a102bcd31360d00491e23ac5cb4f7cbf8ae9c61f577ccccc.jpg",
status: "Active",
},
{
id: "charlotte-walker",
name: "Charlotte Walker",
group: "Management Team",
position: "CTO",
avatar: "https://cdn.21st.dev/assets/mirror/56/56cfb2a08032e82843ccac91504bbf42ababde4aea91bbacd9b683912cd8b21a.jpg",
status: "Active",
},
{
id: "benjamin-hall",
name: "Benjamin Hall",
group: "Management Team",
position: "VP of Engineering",
avatar: "https://cdn.21st.dev/assets/mirror/35/3560ff7cbc9e86c333fccefe248e3ea5cdade4e46f6b2fc85d84755896cb2e5a.jpg",
status: "Active",
},
{
id: "amelia-allen",
name: "Amelia Allen",
group: "Management Team",
position: "VP of Marketing",
avatar: "https://cdn.21st.dev/assets/mirror/aa/aa4787be04406deac036c92ff766754aa511214f00a4ee181ada4fc2c6622b6f.jpg",
status: "Active",
},
// Support Team
{
id: "henry-young",
name: "Henry Young",
group: "Support Team",
position: "Customer Success Manager",
avatar: "https://cdn.21st.dev/assets/mirror/ca/ca627d33f20754d25814a1d622a9f4837d56d5809c6fa7c14f2f2be7e3f36a05.jpg",
status: "Active",
},
{
id: "grace-king",
name: "Grace King",
group: "Support Team",
position: "Technical Support Specialist",
avatar: "https://cdn.21st.dev/assets/mirror/aa/aaab4a0fbd8e2ad7a7ec4ccaa827918df0d6af1732227caa84d309cb49b45c21.jpg",
status: "Active",
},
{
id: "sebastian-wright",
name: "Sebastian Wright",
group: "Support Team",
position: "Customer Support Representative",
avatar: "https://cdn.21st.dev/assets/mirror/f6/f61f4b3793465be0aa4c3b577a6ccb95ae48ccfb751e7aaa6b5477aadc5255a8.jpg",
status: "Away",
},
{
id: "lily-lopez",
name: "Lily Lopez",
group: "Support Team",
position: "Help Desk Technician",
avatar: "https://cdn.21st.dev/assets/mirror/f9/f90a38d15ecefa1704387dfba523d1c3e78b6344bcee586e0378e6af5e5f7cd0.jpg",
status: "Active",
},
]
function groupUsers(users: User[]): UserGroup[] {
const groups: { [key: string]: User[] } = {}
users.forEach((item) => {
;(groups[item.group] ??= []).push(item)
})
// Sort by status within each group (Active first, then Away, then Inactive)
Object.keys(groups).forEach((group) => {
groups[group].sort((a, b) => {
const statusOrder = { Active: 0, Away: 1, Inactive: 2 }
return statusOrder[a.status] - statusOrder[b.status]
})
})
const order = [
"Management Team",
"Development Team",
"Design Team",
"Marketing Team",
"Sales Team",
"Support Team",
]
return order.map((group) => ({ group, items: groups[group] ?? [] }))
}
const groupedUsers: UserGroup[] = groupUsers(usersData)