1–10 of 27
Name | Type | People | Last updated |
|---|---|---|---|
| Data-driven | 12,403 | ||
| Data-driven | 1,120 | ||
| Manual | 318 | ||
| Data-driven | 48,213 | ||
| Data-driven | 64,110 | ||
| Data-driven | 742 | ||
| Manual | 1,245 | ||
| Data-driven | 2,318 | ||
| Data-driven | 586 | ||
| Manual | 47 |
1–10 of 27
Use this page for a collection, such as a workspace's segments, where people often need a quick look at one item without losing their place in the list. Selecting an item's name opens a drawer beside the list with that item's details and an action to edit it. Use it when the details fit in a short summary; when people need the item's full history or tabs, link to a detail page instead.
Name | Type | People | Last updated |
|---|---|---|---|
| Data-driven | 12,403 | Oct 8, 2026 | |
| Data-driven | 1,120 | Oct 7, 2026 | |
| Manual | 318 | Oct 2, 2026 | |
| Data-driven | 48,213 | Oct 8, 2026 | |
| Data-driven | 64,110 | Sep 30, 2026 | |
| Data-driven | 742 | Sep 22, 2026 | |
| Manual | 1,245 | Oct 1, 2026 | |
| Data-driven | 2,318 | Oct 6, 2026 | |
| Data-driven | 586 | Oct 5, 2026 | |
| Manual | 47 | Oct 8, 2026 |
import {
Button,
DataTable,
DescriptionList,
DescriptionListDetails,
DescriptionListTerm,
Drawer,
DrawerBody,
DrawerFooter,
FormattedDateTime,
Link,
Page,
PageBody,
PageHeader,
PageHeaderActions,
PageHeaderTitle,
PageSection,
} from '@customerio/pluma-components/react';
import type { DataTableFeatures } from '@customerio/pluma-components/react';
import { createColumnHelper } from '@tanstack/table-core';
import { useMemo } from 'react';
type SegmentType = 'data-driven' | 'manual';
export interface Segment {
id: string;
name: string;
description: string;
type: SegmentType;
peopleCount: number;
createdBy: string;
/** ISO 8601 timestamp. */
createdAt: string;
/** ISO 8601 timestamp. */
updatedAt: string;
}
const segmentTypes: Record<SegmentType, string> = {
'data-driven': 'Data-driven',
manual: 'Manual',
};
const numberFormatter = new Intl.NumberFormat();
const columnHelper = createColumnHelper<DataTableFeatures, Segment>();
export interface ListWithDrawerDetailProps {
/** The current page of segments that match the search. */
segments: Segment[];
/** How many segments match the search, across all pages. */
totalSegmentCount: number;
/** The current page, starting at 1. */
page: number;
/** How many segments a page holds. */
pageSize: number;
/** The text in the search field. */
searchValue: string;
/** Whether the segments for the current search and page are loading. */
isLoading: boolean;
/**
* The segment the drawer shows, or `null` before one has been opened. Keep it
* set after the drawer closes, so the drawer keeps its content while it slides away.
*/
drawerSegment: Segment | null;
/** Whether the drawer is open. */
isDrawerOpen: boolean;
/** Called when the create action is activated. */
onCreate: () => void;
/** Called on every edit of the search field. */
onSearchInput: (value: string) => void;
/** Called with the search to run. Show the first page of its results. */
onSearch: (value: string) => void;
/** Called with the page to show. */
onPageChange: (page: number) => void;
/** Called with the segment whose name was activated. Show it in the drawer and open it. */
onOpenSegment: (segment: Segment) => void;
/** Called when the drawer asks to close. */
onCloseDrawer: () => void;
/** Called with the segment to edit. */
onEditSegment: (segment: Segment) => void;
}
export function ListWithDrawerDetail({
segments,
totalSegmentCount,
page,
pageSize,
searchValue,
isLoading,
drawerSegment,
isDrawerOpen,
onCreate,
onSearchInput,
onSearch,
onPageChange,
onOpenSegment,
onCloseDrawer,
onEditSegment,
}: ListWithDrawerDetailProps) {
const columns = useMemo(
() =>
columnHelper.columns([
columnHelper.accessor('name', {
header: 'Name',
cell: (info) => (
<Link onClick={() => onOpenSegment(info.row.original)} truncate={true}>
{info.getValue()}
</Link>
),
meta: { width: '2fr', minWidth: 200 },
}),
columnHelper.accessor('type', {
header: 'Type',
cell: (info) => segmentTypes[info.getValue()],
}),
columnHelper.accessor('peopleCount', {
header: 'People',
cell: (info) => numberFormatter.format(info.getValue()),
meta: { align: 'end' },
}),
columnHelper.accessor('updatedAt', {
header: 'Last updated',
cell: (info) => <FormattedDateTime date={info.getValue()} format="medium" />,
}),
]),
[onOpenSegment],
);
return (
<Page isFullScreen>
<PageHeader>
<PageHeaderTitle>Segments</PageHeaderTitle>
<PageHeaderActions>
<Button variant="primary" icon="add" onClick={onCreate}>
Create segment
</Button>
</PageHeaderActions>
</PageHeader>
<PageBody>
<PageSection>
<DataTable
caption="Segments"
data={segments}
columns={columns}
isLoading={isLoading}
withSearch={true}
search={{
value: searchValue,
onSearchInput,
onSearch,
ariaLabel: 'Search segments',
placeholder: 'Search by name',
shouldSearchOnDebounce: 300,
shouldSearchOnClear: true,
}}
withPagination={true}
pagination={{
page,
pageSize,
totalItemCount: totalSegmentCount,
onPageChange,
}}
emptyState={
searchValue === ''
? {
icon: 'segment',
title: 'No segments yet',
description: 'Create a segment to group people by what they do and who they are.',
actions: [{ label: 'Create segment', variant: 'primary', onClick: onCreate }],
}
: {
icon: 'search',
title: 'No segments match your search',
description: 'Try a different name, or clear the search to see every segment.',
}
}
/>
</PageSection>
</PageBody>
{drawerSegment !== null ? (
<Drawer isOpen={isDrawerOpen} onClose={onCloseDrawer} title={drawerSegment.name}>
<DrawerBody>
<DescriptionList layout="horizontal">
<DescriptionListTerm>Description</DescriptionListTerm>
<DescriptionListDetails>{drawerSegment.description}</DescriptionListDetails>
<DescriptionListTerm>Type</DescriptionListTerm>
<DescriptionListDetails>{segmentTypes[drawerSegment.type]}</DescriptionListDetails>
<DescriptionListTerm>People</DescriptionListTerm>
<DescriptionListDetails>{numberFormatter.format(drawerSegment.peopleCount)}</DescriptionListDetails>
<DescriptionListTerm>Created by</DescriptionListTerm>
<DescriptionListDetails>{drawerSegment.createdBy}</DescriptionListDetails>
<DescriptionListTerm>Created</DescriptionListTerm>
<DescriptionListDetails>
<FormattedDateTime date={drawerSegment.createdAt} format="medium" />
</DescriptionListDetails>
<DescriptionListTerm>Last updated</DescriptionListTerm>
<DescriptionListDetails>
<FormattedDateTime date={drawerSegment.updatedAt} format="medium" />
</DescriptionListDetails>
</DescriptionList>
</DrawerBody>
<DrawerFooter>
<Button onClick={onCloseDrawer}>Close</Button>
<Button variant="primary" onClick={() => onEditSegment(drawerSegment)}>
Edit segment
</Button>
</DrawerFooter>
</Drawer>
) : null}
</Page>
);
}
Copy this recipe into your app with the Pluma CLI. Replace <dest-dir> with the folder the file goes in, and add --name <Name> to rename the component and its file.
pluma-cli scaffold list-with-drawer-detail <dest-dir> --framework reactThis recipe uses these components. Each one's docs page lists its props.