FactuGES_ERP/packages/rdx-ui/src/components/layout/data-table.tsx

752 lines
25 KiB
TypeScript
Raw Normal View History

2025-05-17 19:12:01 +00:00
"use client";
import {
DndContext,
type DragEndEvent,
KeyboardSensor,
MouseSensor,
TouchSensor,
type UniqueIdentifier,
closestCenter,
useSensor,
useSensors,
} from "@dnd-kit/core";
import { restrictToVerticalAxis } from "@dnd-kit/modifiers";
import {
SortableContext,
arrayMove,
useSortable,
verticalListSortingStrategy,
} from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities";
import { Badge } from "@repo/shadcn-ui/components/badge";
import { Button } from "@repo/shadcn-ui/components/button";
import {
type ChartConfig,
2025-05-17 19:12:01 +00:00
ChartContainer,
ChartTooltip,
ChartTooltipContent,
} from "@repo/shadcn-ui/components/chart";
import { Checkbox } from "@repo/shadcn-ui/components/checkbox";
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@repo/shadcn-ui/components/dropdown-menu";
import { Input } from "@repo/shadcn-ui/components/input";
import { Label } from "@repo/shadcn-ui/components/label";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@repo/shadcn-ui/components/select";
import { Separator } from "@repo/shadcn-ui/components/separator";
import {
Sheet,
SheetClose,
SheetContent,
SheetDescription,
SheetFooter,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "@repo/shadcn-ui/components/sheet";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@repo/shadcn-ui/components/table";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@repo/shadcn-ui/components/tabs";
import { useIsMobile } from "@repo/shadcn-ui/hooks";
import {
type ColumnDef,
type ColumnFiltersState,
type Row,
type SortingState,
type VisibilityState,
flexRender,
getCoreRowModel,
getFacetedRowModel,
getFacetedUniqueValues,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from "@tanstack/react-table";
import {
CheckCircle2Icon,
ChevronDownIcon,
ChevronLeftIcon,
ChevronRightIcon,
ChevronsLeftIcon,
ChevronsRightIcon,
ColumnsIcon,
GripVerticalIcon,
LoaderIcon,
MoreVerticalIcon,
PlusIcon,
TrendingUpIcon,
} from "lucide-react";
import * as React from "react";
import { Area, AreaChart, CartesianGrid, XAxis } from "recharts";
import { toast } from "sonner";
import { z } from "zod/v4";
2025-05-17 19:12:01 +00:00
export const schema = z.object({
id: z.number(),
header: z.string(),
type: z.string(),
status: z.string(),
target: z.string(),
limit: z.string(),
reviewer: z.string(),
});
// Create a separate component for the drag handle
function DragHandle({ id }: { id: number }) {
const { attributes, listeners } = useSortable({
id,
});
return (
<Button
{...attributes}
{...listeners}
className="size-7 text-muted-foreground hover:bg-transparent"
size="icon"
variant="ghost"
2025-05-17 19:12:01 +00:00
>
<GripVerticalIcon className="size-3 text-muted-foreground" />
<span className="sr-only">Drag to reorder</span>
2025-05-17 19:12:01 +00:00
</Button>
);
}
const columns: ColumnDef<z.infer<typeof schema>>[] = [
{
id: "drag",
header: () => null,
cell: ({ row }) => <DragHandle id={row.original.id} />,
},
{
id: "select",
header: ({ table }) => (
<div className="flex items-center justify-center">
2025-05-17 19:12:01 +00:00
<Checkbox
aria-label="Select all"
2025-05-17 19:12:01 +00:00
checked={
table.getIsAllPageRowsSelected() ||
(table.getIsSomePageRowsSelected() && "indeterminate")
}
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
/>
</div>
),
cell: ({ row }) => (
<div className="flex items-center justify-center">
2025-05-17 19:12:01 +00:00
<Checkbox
aria-label="Select row"
2025-05-17 19:12:01 +00:00
checked={row.getIsSelected()}
onCheckedChange={(value) => row.toggleSelected(!!value)}
/>
</div>
),
enableSorting: false,
enableHiding: false,
},
{
accessorKey: "header",
header: "Header",
cell: ({ row }) => {
return <TableCellViewer item={row.original} />;
},
enableHiding: false,
},
{
accessorKey: "type",
header: "Section Type",
cell: ({ row }) => (
<div className="w-32">
<Badge className="px-1.5 text-muted-foreground" variant="outline">
2025-05-17 19:12:01 +00:00
{row.original.type}
</Badge>
</div>
),
},
{
accessorKey: "status",
header: "Status",
cell: ({ row }) => (
<Badge className="flex gap-1 px-1.5 text-muted-foreground [&_svg]:size-3" variant="outline">
2025-05-17 19:12:01 +00:00
{row.original.status === "Done" ? (
<CheckCircle2Icon className="text-green-500 dark:text-green-400" />
2025-05-17 19:12:01 +00:00
) : (
<LoaderIcon />
)}
{row.original.status}
</Badge>
),
},
{
accessorKey: "target",
header: () => <div className="w-full text-right">Target</div>,
2025-05-17 19:12:01 +00:00
cell: ({ row }) => (
<form
onSubmit={(e) => {
e.preventDefault();
toast.promise(new Promise((resolve) => setTimeout(resolve, 1000)), {
loading: `Saving ${row.original.header}`,
success: "Done",
error: "Error",
});
}}
>
<Label className="sr-only" htmlFor={`${row.original.id}-target`}>
2025-05-17 19:12:01 +00:00
Target
</Label>
<Input
className="h-8 w-16 border-transparent bg-transparent text-right shadow-none hover:bg-input/30 focus-visible:border focus-visible:bg-background"
2025-05-17 19:12:01 +00:00
defaultValue={row.original.target}
id={`${row.original.id}-target`}
/>
</form>
),
},
{
accessorKey: "limit",
header: () => <div className="w-full text-right">Limit</div>,
2025-05-17 19:12:01 +00:00
cell: ({ row }) => (
<form
onSubmit={(e) => {
e.preventDefault();
toast.promise(new Promise((resolve) => setTimeout(resolve, 1000)), {
loading: `Saving ${row.original.header}`,
success: "Done",
error: "Error",
});
}}
>
<Label className="sr-only" htmlFor={`${row.original.id}-limit`}>
2025-05-17 19:12:01 +00:00
Limit
</Label>
<Input
className="h-8 w-16 border-transparent bg-transparent text-right shadow-none hover:bg-input/30 focus-visible:border focus-visible:bg-background"
2025-05-17 19:12:01 +00:00
defaultValue={row.original.limit}
id={`${row.original.id}-limit`}
/>
</form>
),
},
{
accessorKey: "reviewer",
header: "Reviewer",
cell: ({ row }) => {
const isAssigned = row.original.reviewer !== "Assign reviewer";
if (isAssigned) {
return row.original.reviewer;
}
return (
<>
<Label className="sr-only" htmlFor={`${row.original.id}-reviewer`}>
2025-05-17 19:12:01 +00:00
Reviewer
</Label>
<Select>
<SelectTrigger className="h-8 w-40" id={`${row.original.id}-reviewer`}>
<SelectValue placeholder="Assign reviewer" />
2025-05-17 19:12:01 +00:00
</SelectTrigger>
<SelectContent align="end">
<SelectItem value="Eddie Lake">Eddie Lake</SelectItem>
<SelectItem value="Jamik Tashpulatov">Jamik Tashpulatov</SelectItem>
2025-05-17 19:12:01 +00:00
</SelectContent>
</Select>
</>
);
},
},
{
id: "actions",
cell: () => (
<DropdownMenu>
<DropdownMenuTrigger>
2025-05-17 19:12:01 +00:00
<Button
className="flex size-8 text-muted-foreground data-[state=open]:bg-muted"
size="icon"
variant="ghost"
2025-05-17 19:12:01 +00:00
>
<MoreVerticalIcon />
<span className="sr-only">Open menu</span>
2025-05-17 19:12:01 +00:00
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-32">
2025-05-17 19:12:01 +00:00
<DropdownMenuItem>Edit</DropdownMenuItem>
<DropdownMenuItem>Make a copy</DropdownMenuItem>
<DropdownMenuItem>Favorite</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem>Delete</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
),
},
];
function DraggableRow({ row }: { row: Row<z.infer<typeof schema>> }) {
const { transform, transition, setNodeRef, isDragging } = useSortable({
id: row.original.id,
});
return (
<TableRow
className="relative z-0 data-[dragging=true]:z-10 data-[dragging=true]:opacity-80"
2025-05-17 19:12:01 +00:00
data-dragging={isDragging}
data-state={row.getIsSelected() && "selected"}
2025-05-17 19:12:01 +00:00
ref={setNodeRef}
style={{
transform: CSS.Transform.toString(transform),
transition: transition,
}}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</TableCell>
))}
</TableRow>
);
}
export function DataTable({ data: initialData }: { data: z.infer<typeof schema>[] }) {
2025-05-17 19:12:01 +00:00
const [data, setData] = React.useState(() => initialData);
const [rowSelection, setRowSelection] = React.useState({});
const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({});
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([]);
const [sorting, setSorting] = React.useState<SortingState>([]);
const [pagination, setPagination] = React.useState({
pageIndex: 0,
pageSize: 10,
});
const sortableId = React.useId();
const sensors = useSensors(
useSensor(MouseSensor, {}),
useSensor(TouchSensor, {}),
useSensor(KeyboardSensor, {})
);
const dataIds = React.useMemo<UniqueIdentifier[]>(() => data?.map(({ id }) => id) || [], [data]);
const table = useReactTable({
data,
columns,
state: {
sorting,
columnVisibility,
rowSelection,
columnFilters,
pagination,
},
getRowId: (row) => row.id.toString(),
enableRowSelection: true,
onRowSelectionChange: setRowSelection,
onSortingChange: setSorting,
onColumnFiltersChange: setColumnFilters,
onColumnVisibilityChange: setColumnVisibility,
onPaginationChange: setPagination,
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFacetedRowModel: getFacetedRowModel(),
getFacetedUniqueValues: getFacetedUniqueValues(),
});
function handleDragEnd(event: DragEndEvent) {
const { active, over } = event;
if (active && over && active.id !== over.id) {
setData((data) => {
const oldIndex = dataIds.indexOf(active.id);
const newIndex = dataIds.indexOf(over.id);
return arrayMove(data, oldIndex, newIndex);
});
}
}
return (
<Tabs className="flex w-full flex-col justify-start gap-6" defaultValue="outline">
<div className="flex items-center justify-between px-4 lg:px-6">
<Label className="sr-only" htmlFor="view-selector">
2025-05-17 19:12:01 +00:00
View
</Label>
<Select defaultValue="outline">
<SelectTrigger className="@4xl/main:hidden flex w-fit" id="view-selector">
<SelectValue placeholder="Select a view" />
2025-05-17 19:12:01 +00:00
</SelectTrigger>
<SelectContent>
<SelectItem value="outline">Outline</SelectItem>
<SelectItem value="past-performance">Past Performance</SelectItem>
<SelectItem value="key-personnel">Key Personnel</SelectItem>
<SelectItem value="focus-documents">Focus Documents</SelectItem>
2025-05-17 19:12:01 +00:00
</SelectContent>
</Select>
<TabsList className="@4xl/main:flex hidden">
<TabsTrigger value="outline">Outline</TabsTrigger>
<TabsTrigger className="gap-1" value="past-performance">
2025-05-17 19:12:01 +00:00
Past Performance{" "}
<Badge
className="flex size-5 items-center justify-center rounded-full bg-muted-foreground/30"
variant="secondary"
2025-05-17 19:12:01 +00:00
>
3
</Badge>
</TabsTrigger>
<TabsTrigger className="gap-1" value="key-personnel">
2025-05-17 19:12:01 +00:00
Key Personnel{" "}
<Badge
className="flex size-5 items-center justify-center rounded-full bg-muted-foreground/30"
variant="secondary"
2025-05-17 19:12:01 +00:00
>
2
</Badge>
</TabsTrigger>
<TabsTrigger value="focus-documents">Focus Documents</TabsTrigger>
2025-05-17 19:12:01 +00:00
</TabsList>
<div className="flex items-center gap-2">
2025-05-17 19:12:01 +00:00
<DropdownMenu>
<DropdownMenuTrigger>
<Button size="sm" variant="outline">
2025-05-17 19:12:01 +00:00
<ColumnsIcon />
<span className="hidden lg:inline">Customize Columns</span>
<span className="lg:hidden">Columns</span>
2025-05-17 19:12:01 +00:00
<ChevronDownIcon />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-56">
2025-05-17 19:12:01 +00:00
{table
.getAllColumns()
.filter((column) => typeof column.accessorFn !== "undefined" && column.getCanHide())
.map((column) => {
return (
<DropdownMenuCheckboxItem
checked={column.getIsVisible()}
className="capitalize"
key={column.id}
2025-05-17 19:12:01 +00:00
onCheckedChange={(value) => column.toggleVisibility(!!value)}
>
{column.id}
</DropdownMenuCheckboxItem>
);
})}
</DropdownMenuContent>
</DropdownMenu>
<Button size="sm" variant="outline">
2025-05-17 19:12:01 +00:00
<PlusIcon />
<span className="hidden lg:inline">Add Section</span>
2025-05-17 19:12:01 +00:00
</Button>
</div>
</div>
<TabsContent
className="relative flex flex-col gap-4 overflow-auto px-4 lg:px-6"
value="outline"
2025-05-17 19:12:01 +00:00
>
<div className="overflow-hidden rounded-lg border">
2025-05-17 19:12:01 +00:00
<DndContext
collisionDetection={closestCenter}
id={sortableId}
2025-05-17 19:12:01 +00:00
modifiers={[restrictToVerticalAxis]}
onDragEnd={handleDragEnd}
sensors={sensors}
>
<Table>
<TableHeader className="sticky top-0 z-10 bg-muted">
2025-05-17 19:12:01 +00:00
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead colSpan={header.colSpan} key={header.id}>
2025-05-17 19:12:01 +00:00
{header.isPlaceholder
? null
: flexRender(header.column.columnDef.header, header.getContext())}
</TableHead>
);
})}
</TableRow>
))}
</TableHeader>
<TableBody className="**:data-[slot=table-cell]:first:w-8">
2025-05-17 19:12:01 +00:00
{table.getRowModel().rows?.length ? (
<SortableContext items={dataIds} strategy={verticalListSortingStrategy}>
{table.getRowModel().rows.map((row) => (
<DraggableRow key={row.id} row={row} />
))}
</SortableContext>
) : (
<TableRow>
<TableCell className="h-24 text-center" colSpan={columns.length}>
2025-05-17 19:12:01 +00:00
No results.
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</DndContext>
</div>
<div className="flex items-center justify-between px-4">
<div className="hidden flex-1 text-sm text-muted-foreground lg:flex">
2025-05-17 19:12:01 +00:00
{table.getFilteredSelectedRowModel().rows.length} of{" "}
{table.getFilteredRowModel().rows.length} row(s) selected.
</div>
<div className="flex w-full items-center gap-8 lg:w-fit">
<div className="hidden items-center gap-2 lg:flex">
<Label className="text-sm font-medium" htmlFor="rows-per-page">
2025-05-17 19:12:01 +00:00
Rows per page
</Label>
<Select
onValueChange={(value) => {
table.setPageSize(Number(value));
}}
value={`${table.getState().pagination.pageSize}`}
2025-05-17 19:12:01 +00:00
>
<SelectTrigger className="w-20" id="rows-per-page">
2025-05-17 19:12:01 +00:00
<SelectValue placeholder={table.getState().pagination.pageSize} />
</SelectTrigger>
<SelectContent side="top">
2025-05-17 19:12:01 +00:00
{[10, 20, 30, 40, 50].map((pageSize) => (
<SelectItem key={pageSize} value={`${pageSize}`}>
{pageSize}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="flex w-fit items-center justify-center text-sm font-medium">
2025-05-17 19:12:01 +00:00
Page {table.getState().pagination.pageIndex + 1} of {table.getPageCount()}
</div>
<div className="ml-auto flex items-center gap-2 lg:ml-0">
2025-05-17 19:12:01 +00:00
<Button
className="hidden size-8 p-0 lg:flex"
2025-05-17 19:12:01 +00:00
disabled={!table.getCanPreviousPage()}
onClick={() => table.setPageIndex(0)}
variant="outline"
2025-05-17 19:12:01 +00:00
>
<span className="sr-only">Go to first page</span>
2025-05-17 19:12:01 +00:00
<ChevronsLeftIcon />
</Button>
<Button
className="size-8"
2025-05-17 19:12:01 +00:00
disabled={!table.getCanPreviousPage()}
onClick={() => table.previousPage()}
size="icon"
variant="outline"
2025-05-17 19:12:01 +00:00
>
<span className="sr-only">Go to previous page</span>
2025-05-17 19:12:01 +00:00
<ChevronLeftIcon />
</Button>
<Button
className="size-8"
2025-05-17 19:12:01 +00:00
disabled={!table.getCanNextPage()}
onClick={() => table.nextPage()}
size="icon"
variant="outline"
2025-05-17 19:12:01 +00:00
>
<span className="sr-only">Go to next page</span>
2025-05-17 19:12:01 +00:00
<ChevronRightIcon />
</Button>
<Button
className="hidden size-8 lg:flex"
2025-05-17 19:12:01 +00:00
disabled={!table.getCanNextPage()}
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
size="icon"
variant="outline"
2025-05-17 19:12:01 +00:00
>
<span className="sr-only">Go to last page</span>
2025-05-17 19:12:01 +00:00
<ChevronsRightIcon />
</Button>
</div>
</div>
</div>
</TabsContent>
<TabsContent className="flex flex-col px-4 lg:px-6" value="past-performance">
<div className="aspect-video w-full flex-1 rounded-lg border border-dashed" />
2025-05-17 19:12:01 +00:00
</TabsContent>
<TabsContent className="flex flex-col px-4 lg:px-6" value="key-personnel">
<div className="aspect-video w-full flex-1 rounded-lg border border-dashed" />
2025-05-17 19:12:01 +00:00
</TabsContent>
<TabsContent className="flex flex-col px-4 lg:px-6" value="focus-documents">
<div className="aspect-video w-full flex-1 rounded-lg border border-dashed" />
2025-05-17 19:12:01 +00:00
</TabsContent>
</Tabs>
);
}
const chartData = [
{ month: "January", desktop: 186, mobile: 80 },
{ month: "February", desktop: 305, mobile: 200 },
{ month: "March", desktop: 237, mobile: 120 },
{ month: "April", desktop: 73, mobile: 190 },
{ month: "May", desktop: 209, mobile: 130 },
{ month: "June", desktop: 214, mobile: 140 },
];
const chartConfig = {
desktop: {
label: "Desktop",
color: "var(--primary)",
},
mobile: {
label: "Mobile",
color: "var(--primary)",
},
} satisfies ChartConfig;
function TableCellViewer({ item }: { item: z.infer<typeof schema> }) {
const isMobile = useIsMobile();
return (
<Sheet>
<SheetTrigger>
<Button className="w-fit px-0 text-left text-foreground" variant="link">
2025-05-17 19:12:01 +00:00
{item.header}
</Button>
</SheetTrigger>
<SheetContent className="flex flex-col" side="right">
<SheetHeader className="gap-1">
2025-05-17 19:12:01 +00:00
<SheetTitle>{item.header}</SheetTitle>
<SheetDescription>Showing total visitors for the last 6 months</SheetDescription>
</SheetHeader>
<div className="flex flex-1 flex-col gap-4 overflow-y-auto py-4 text-sm">
2025-05-17 19:12:01 +00:00
{!isMobile && (
<>
<ChartContainer config={chartConfig}>
<AreaChart
accessibilityLayer
data={chartData}
margin={{
left: 0,
right: 10,
}}
>
<CartesianGrid vertical={false} />
<XAxis
axisLine={false}
dataKey="month"
2025-05-17 19:12:01 +00:00
hide
tickFormatter={(value) => value.slice(0, 3)}
tickLine={false}
tickMargin={8}
2025-05-17 19:12:01 +00:00
/>
<ChartTooltip content={<ChartTooltipContent indicator="dot" />} cursor={false} />
2025-05-17 19:12:01 +00:00
<Area
dataKey="mobile"
fill="var(--color-mobile)"
2025-05-17 19:12:01 +00:00
fillOpacity={0.6}
stackId="a"
stroke="var(--color-mobile)"
type="natural"
2025-05-17 19:12:01 +00:00
/>
<Area
dataKey="desktop"
fill="var(--color-desktop)"
2025-05-17 19:12:01 +00:00
fillOpacity={0.4}
stackId="a"
stroke="var(--color-desktop)"
type="natural"
2025-05-17 19:12:01 +00:00
/>
</AreaChart>
</ChartContainer>
<Separator />
<div className="grid gap-2">
<div className="flex gap-2 font-medium leading-none">
Trending up by 5.2% this month <TrendingUpIcon className="size-4" />
2025-05-17 19:12:01 +00:00
</div>
<div className="text-muted-foreground">
2025-05-17 19:12:01 +00:00
Showing total visitors for the last 6 months. This is just some random text to
test the layout. It spans multiple lines and should wrap around.
</div>
</div>
<Separator />
</>
)}
<form className="flex flex-col gap-4">
<div className="flex flex-col gap-3">
<Label htmlFor="header">Header</Label>
<Input defaultValue={item.header} id="header" />
2025-05-17 19:12:01 +00:00
</div>
<div className="grid grid-cols-2 gap-4">
<div className="flex flex-col gap-3">
<Label htmlFor="type">Type</Label>
2025-05-17 19:12:01 +00:00
<Select defaultValue={item.type}>
<SelectTrigger className="w-full" id="type">
<SelectValue placeholder="Select a type" />
2025-05-17 19:12:01 +00:00
</SelectTrigger>
<SelectContent>
<SelectItem value="Table of Contents">Table of Contents</SelectItem>
<SelectItem value="Executive Summary">Executive Summary</SelectItem>
<SelectItem value="Technical Approach">Technical Approach</SelectItem>
<SelectItem value="Design">Design</SelectItem>
<SelectItem value="Capabilities">Capabilities</SelectItem>
<SelectItem value="Focus Documents">Focus Documents</SelectItem>
<SelectItem value="Narrative">Narrative</SelectItem>
<SelectItem value="Cover Page">Cover Page</SelectItem>
2025-05-17 19:12:01 +00:00
</SelectContent>
</Select>
</div>
<div className="flex flex-col gap-3">
<Label htmlFor="status">Status</Label>
2025-05-17 19:12:01 +00:00
<Select defaultValue={item.status}>
<SelectTrigger className="w-full" id="status">
<SelectValue placeholder="Select a status" />
2025-05-17 19:12:01 +00:00
</SelectTrigger>
<SelectContent>
<SelectItem value="Done">Done</SelectItem>
<SelectItem value="In Progress">In Progress</SelectItem>
<SelectItem value="Not Started">Not Started</SelectItem>
2025-05-17 19:12:01 +00:00
</SelectContent>
</Select>
</div>
</div>
<div className="grid grid-cols-2 gap-4">
<div className="flex flex-col gap-3">
<Label htmlFor="target">Target</Label>
<Input defaultValue={item.target} id="target" />
2025-05-17 19:12:01 +00:00
</div>
<div className="flex flex-col gap-3">
<Label htmlFor="limit">Limit</Label>
<Input defaultValue={item.limit} id="limit" />
2025-05-17 19:12:01 +00:00
</div>
</div>
<div className="flex flex-col gap-3">
<Label htmlFor="reviewer">Reviewer</Label>
2025-05-17 19:12:01 +00:00
<Select defaultValue={item.reviewer}>
<SelectTrigger className="w-full" id="reviewer">
<SelectValue placeholder="Select a reviewer" />
2025-05-17 19:12:01 +00:00
</SelectTrigger>
<SelectContent>
<SelectItem value="Eddie Lake">Eddie Lake</SelectItem>
<SelectItem value="Jamik Tashpulatov">Jamik Tashpulatov</SelectItem>
<SelectItem value="Emily Whalen">Emily Whalen</SelectItem>
2025-05-17 19:12:01 +00:00
</SelectContent>
</Select>
</div>
</form>
</div>
<SheetFooter className="mt-auto flex gap-2 sm:flex-col sm:space-x-0">
<Button className="w-full">Submit</Button>
<SheetClose>
<Button className="w-full" variant="outline">
2025-05-17 19:12:01 +00:00
Done
</Button>
</SheetClose>
</SheetFooter>
</SheetContent>
</Sheet>
);
}