Record details drawer
Open a row to see details without leaving the list.
Built with
- Avatar
- AvatarLabel
- Badge
- Button
- Drawer
- ListItem
- Table
- TableBody
- TableCell
- TableHeader
- TableHeaderCell
- TableRow
Employees
Select a row to open the full record.
| Employee | Team | Location | Status |
|---|---|---|---|
Ayesha KhanSenior product designer | Design | London | Active |
Sara MalikPayroll specialist | People Ops | Lisbon | On leave |
Ahmed RazaEngineering manager | Engineering | Lahore | Remote |
Mia JensenProduct manager | Product | Copenhagen | Active |
Luis OrtegaFrontend engineer | Engineering | Madrid | Active |
Yuki TanakaRecruiter | People Ops | Tokyo | Active |
Chloe MartinData analyst | Finance | Paris | Remote |
Omar HaddadSupport lead | Customer | Dubai | Invited |
record-details-drawer.tsx
"use client";
import { useState } from "react";
import {
LuBuilding2,
LuCalendar,
LuMapPin,
LuMessageSquare,
LuTreePalm,
LuUser,
} from "react-icons/lu";
import {
Avatar,
AvatarLabel,
Badge,
Button,
Drawer,
ListItem,
Table,
TableBody,
TableCell,
TableHeader,
TableHeaderCell,
TableRow,
} from "@nexera-ui/react";
// Sample data
interface Person {
id: string;
name: string;
initials: string;
role: string;
team: string;
email: string;
location: string;
status: "Active" | "On leave" | "Remote" | "Invited";
}
const PEOPLE: Person[] = [
{
id: "p1",
name: "Ayesha Khan",
initials: "AK",
role: "Senior product designer",
team: "Design",
email: "ayesha.khan@northwind.io",
location: "London",
status: "Active",
},
{
id: "p2",
name: "Sara Malik",
initials: "SM",
role: "Payroll specialist",
team: "People Ops",
email: "sara.malik@northwind.io",
location: "Lisbon",
status: "On leave",
},
{
id: "p3",
name: "Ahmed Raza",
initials: "AR",
role: "Engineering manager",
team: "Engineering",
email: "ahmed.raza@northwind.io",
location: "Lahore",
status: "Remote",
},
{
id: "p4",
name: "Mia Jensen",
initials: "MJ",
role: "Product manager",
team: "Product",
email: "mia.jensen@northwind.io",
location: "Copenhagen",
status: "Active",
},
{
id: "p5",
name: "Luis Ortega",
initials: "LO",
role: "Frontend engineer",
team: "Engineering",
email: "luis.ortega@northwind.io",
location: "Madrid",
status: "Active",
},
{
id: "p6",
name: "Yuki Tanaka",
initials: "YT",
role: "Recruiter",
team: "People Ops",
email: "yuki.tanaka@northwind.io",
location: "Tokyo",
status: "Active",
},
{
id: "p7",
name: "Chloe Martin",
initials: "CM",
role: "Data analyst",
team: "Finance",
email: "chloe.martin@northwind.io",
location: "Paris",
status: "Remote",
},
{
id: "p8",
name: "Omar Haddad",
initials: "OH",
role: "Support lead",
team: "Customer",
email: "omar.haddad@northwind.io",
location: "Dubai",
status: "Invited",
},
];
const TONE = { Active: "success", "On leave": "leave", Remote: "remote", Invited: "info" } as const;
// Details the list does not show, keyed by person id.
const DETAILS: Record<string, { manager: string; started: string; leave: string }> = {
p1: {
manager: "Jonas Berg, Head of Design",
started: "15 Mar 2021",
leave: "14 of 25 days left",
},
p2: {
manager: "Elena Rossi, Head of People",
started: "9 Jan 2023",
leave: "On leave until 20 Oct",
},
p3: {
manager: "Hannah Weber, VP Engineering",
started: "26 Aug 2019",
leave: "9 of 25 days left",
},
p4: {
manager: "Jonas Berg, Head of Product",
started: "3 May 2022",
leave: "18 of 25 days left",
},
p5: {
manager: "Ahmed Raza, Engineering manager",
started: "12 Feb 2024",
leave: "6 of 25 days left",
},
p6: {
manager: "Elena Rossi, Head of People",
started: "18 Sep 2023",
leave: "21 of 25 days left",
},
p7: {
manager: "Priya Nair, Finance manager",
started: "2 Nov 2020",
leave: "11 of 25 days left",
},
p8: {
manager: "Grace Lee, Head of Customer",
started: "Starts 5 Oct 2026",
leave: "Accrues from start date",
},
};
/** Record details drawer: open any row of the people table to read and act on that person's record in a side panel. */
export default function RecordDetailsDrawer() {
const [openId, setOpenId] = useState<string | null>(null);
const person = PEOPLE.find((p) => p.id === openId);
const details = person ? DETAILS[person.id] : undefined;
return (
<div className="@container bg-page px-4 py-8 @3xl:px-8">
<div className="mx-auto grid max-w-5xl gap-4">
<header className="grid gap-1">
<h2 id="records-title" className="text-heading-h2 text-primary">
Employees
</h2>
<p className="text-body-default text-secondary">Select a row to open the full record.</p>
</header>
<div className="min-w-0 overflow-hidden rounded-lg border border-default bg-surface">
<Table
aria-labelledby="records-title"
onRowAction={(key) => {
setOpenId(String(key));
}}
>
<TableHeader>
<TableHeaderCell id="name" isRowHeader label="Employee" />
<TableHeaderCell id="team" label="Team" />
<TableHeaderCell id="location" label="Location" />
<TableHeaderCell id="status" label="Status" />
</TableHeader>
<TableBody items={PEOPLE}>
{(p) => (
<TableRow id={p.id}>
<TableCell type="avatar" name={p.name} email={p.role} initials={p.initials} />
<TableCell>{p.team}</TableCell>
<TableCell>{p.location}</TableCell>
<TableCell type="badge">
<Badge label={p.status} tone={TONE[p.status]} />
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
</div>
<Drawer
isOpen={person !== undefined}
onOpenChange={(open) => {
if (!open) setOpenId(null);
}}
size="md"
title={person?.name ?? "Employee"}
{...(person ? { subtitle: `${person.role} · ${person.team}` } : {})}
secondaryAction={
<Button variant="secondary" size="md" leadingIcon={<LuMessageSquare />}>
Message
</Button>
}
primaryAction={
<Button size="md" slot="close">
Done
</Button>
}
>
{person && details ? (
<div className="grid gap-4">
<div className="flex flex-wrap items-center justify-between gap-3">
<AvatarLabel
name={person.name}
subtitle={person.email}
size="lg"
avatar={<Avatar initials={person.initials} isDecorative />}
/>
<Badge label={person.status} tone={TONE[person.status]} />
</div>
<ul className="grid divide-y divide-subtle rounded-lg border border-default">
<li>
<ListItem icon={<LuUser />} title="Manager" subtitle={details.manager} />
</li>
<li>
<ListItem icon={<LuBuilding2 />} title="Team" subtitle={person.team} />
</li>
<li>
<ListItem icon={<LuMapPin />} title="Location" subtitle={person.location} />
</li>
<li>
<ListItem icon={<LuCalendar />} title="Start date" subtitle={details.started} />
</li>
<li>
<ListItem
icon={<LuTreePalm />}
title="Annual leave"
subtitle={details.leave}
href="#leave-balance"
/>
</li>
</ul>
</div>
) : null}
</Drawer>
</div>
);
}
More lists, tables and records blocks
- Data tableSearch, sort, select rows and page through results.
- Bulk actionsA floating bar that acts on the selected rows.
- Filter barActive filters as removable chips above results.
- Filters drawerDetailed filters in a side panel.
- People directorySearchable list of people with presence.
- Activity feedNewest first, with a 'new items' pill and paging.
- File browserFolders and files in a tree.
- Keyboard shortcutsA modal listing every shortcut.