Open Source Team Metrics based on PRs
1"use client"
2
3import * as React from "react"
4import {
5 DndContext,
6 KeyboardSensor,
7 MouseSensor,
8 TouchSensor,
9 closestCenter,
10 useSensor,
11 useSensors,
12 type DragEndEvent,
13 type UniqueIdentifier,
14} from "@dnd-kit/core"
15import { restrictToVerticalAxis } from "@dnd-kit/modifiers"
16import {
17 SortableContext,
18 arrayMove,
19 useSortable,
20 verticalListSortingStrategy,
21} from "@dnd-kit/sortable"
22import { CSS } from "@dnd-kit/utilities"
23import {
24 IconChevronDown,
25 IconChevronLeft,
26 IconChevronRight,
27 IconChevronsLeft,
28 IconChevronsRight,
29 IconCircleCheckFilled,
30 IconDotsVertical,
31 IconGripVertical,
32 IconLayoutColumns,
33 IconLoader,
34 IconPlus,
35 IconTrendingUp,
36} from "@tabler/icons-react"
37import {
38 ColumnDef,
39 ColumnFiltersState,
40 Row,
41 SortingState,
42 VisibilityState,
43 flexRender,
44 getCoreRowModel,
45 getFacetedRowModel,
46 getFacetedUniqueValues,
47 getFilteredRowModel,
48 getPaginationRowModel,
49 getSortedRowModel,
50 useReactTable,
51} from "@tanstack/react-table"
52import { Area, AreaChart, CartesianGrid, XAxis } from "recharts"
53import { toast } from "sonner"
54import { z } from "zod"
55
56import { useIsMobile } from "@/hooks/use-mobile"
57import { Badge } from "@/components/ui/badge"
58import { Button } from "@/components/ui/button"
59import {
60 ChartConfig,
61 ChartContainer,
62 ChartTooltip,
63 ChartTooltipContent,
64} from "@/components/ui/chart"
65import { Checkbox } from "@/components/ui/checkbox"
66import {
67 Drawer,
68 DrawerClose,
69 DrawerContent,
70 DrawerDescription,
71 DrawerFooter,
72 DrawerHeader,
73 DrawerTitle,
74 DrawerTrigger,
75} from "@/components/ui/drawer"
76import {
77 DropdownMenu,
78 DropdownMenuCheckboxItem,
79 DropdownMenuContent,
80 DropdownMenuItem,
81 DropdownMenuSeparator,
82 DropdownMenuTrigger,
83} from "@/components/ui/dropdown-menu"
84import { Input } from "@/components/ui/input"
85import { Label } from "@/components/ui/label"
86import {
87 Select,
88 SelectContent,
89 SelectItem,
90 SelectTrigger,
91 SelectValue,
92} from "@/components/ui/select"
93import { Separator } from "@/components/ui/separator"
94import {
95 Table,
96 TableBody,
97 TableCell,
98 TableHead,
99 TableHeader,
100 TableRow,
101} from "@/components/ui/table"
102import {
103 Tabs,
104 TabsContent,
105 TabsList,
106 TabsTrigger,
107} from "@/components/ui/tabs"
108
109export const schema = z.object({
110 id: z.number(),
111 header: z.string(),
112 type: z.string(),
113 status: z.string(),
114 target: z.string(),
115 limit: z.string(),
116 reviewer: z.string(),
117})
118
119// Create a separate component for the drag handle
120function DragHandle({ id }: { id: number }) {
121 const { attributes, listeners } = useSortable({
122 id,
123 })
124
125 return (
126 <Button
127 {...attributes}
128 {...listeners}
129 variant="ghost"
130 size="icon"
131 className="text-muted-foreground size-7 hover:bg-transparent"
132 >
133 <IconGripVertical className="text-muted-foreground size-3" />
134 <span className="sr-only">Drag to reorder</span>
135 </Button>
136 )
137}
138
139const columns: ColumnDef<z.infer<typeof schema>>[] = [
140 {
141 id: "drag",
142 header: () => null,
143 cell: ({ row }) => <DragHandle id={row.original.id} />,
144 },
145 {
146 id: "select",
147 header: ({ table }) => (
148 <div className="flex items-center justify-center">
149 <Checkbox
150 checked={
151 table.getIsAllPageRowsSelected() ||
152 (table.getIsSomePageRowsSelected() && "indeterminate")
153 }
154 onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
155 aria-label="Select all"
156 />
157 </div>
158 ),
159 cell: ({ row }) => (
160 <div className="flex items-center justify-center">
161 <Checkbox
162 checked={row.getIsSelected()}
163 onCheckedChange={(value) => row.toggleSelected(!!value)}
164 aria-label="Select row"
165 />
166 </div>
167 ),
168 enableSorting: false,
169 enableHiding: false,
170 },
171 {
172 accessorKey: "header",
173 header: "Header",
174 cell: ({ row }) => {
175 return <TableCellViewer item={row.original} />
176 },
177 enableHiding: false,
178 },
179 {
180 accessorKey: "type",
181 header: "Section Type",
182 cell: ({ row }) => (
183 <div className="w-32">
184 <Badge variant="outline" className="text-muted-foreground px-1.5">
185 {row.original.type}
186 </Badge>
187 </div>
188 ),
189 },
190 {
191 accessorKey: "status",
192 header: "Status",
193 cell: ({ row }) => (
194 <Badge variant="outline" className="text-muted-foreground px-1.5">
195 {row.original.status === "Done" ? (
196 <IconCircleCheckFilled className="fill-green-500 dark:fill-green-400" />
197 ) : (
198 <IconLoader />
199 )}
200 {row.original.status}
201 </Badge>
202 ),
203 },
204 {
205 accessorKey: "target",
206 header: () => <div className="w-full text-right">Target</div>,
207 cell: ({ row }) => (
208 <form
209 onSubmit={(e) => {
210 e.preventDefault()
211 toast.promise(new Promise((resolve) => setTimeout(resolve, 1000)), {
212 loading: `Saving ${row.original.header}`,
213 success: "Done",
214 error: "Error",
215 })
216 }}
217 >
218 <Label htmlFor={`${row.original.id}-target`} className="sr-only">
219 Target
220 </Label>
221 <Input
222 className="hover:bg-input/30 focus-visible:bg-background dark:hover:bg-input/30 dark:focus-visible:bg-input/30 h-8 w-16 border-transparent bg-transparent text-right shadow-none focus-visible:border dark:bg-transparent"
223 defaultValue={row.original.target}
224 id={`${row.original.id}-target`}
225 />
226 </form>
227 ),
228 },
229 {
230 accessorKey: "limit",
231 header: () => <div className="w-full text-right">Limit</div>,
232 cell: ({ row }) => (
233 <form
234 onSubmit={(e) => {
235 e.preventDefault()
236 toast.promise(new Promise((resolve) => setTimeout(resolve, 1000)), {
237 loading: `Saving ${row.original.header}`,
238 success: "Done",
239 error: "Error",
240 })
241 }}
242 >
243 <Label htmlFor={`${row.original.id}-limit`} className="sr-only">
244 Limit
245 </Label>
246 <Input
247 className="hover:bg-input/30 focus-visible:bg-background dark:hover:bg-input/30 dark:focus-visible:bg-input/30 h-8 w-16 border-transparent bg-transparent text-right shadow-none focus-visible:border dark:bg-transparent"
248 defaultValue={row.original.limit}
249 id={`${row.original.id}-limit`}
250 />
251 </form>
252 ),
253 },
254 {
255 accessorKey: "reviewer",
256 header: "Reviewer",
257 cell: ({ row }) => {
258 const isAssigned = row.original.reviewer !== "Assign reviewer"
259
260 if (isAssigned) {
261 return row.original.reviewer
262 }
263
264 return (
265 <>
266 <Label htmlFor={`${row.original.id}-reviewer`} className="sr-only">
267 Reviewer
268 </Label>
269 <Select>
270 <SelectTrigger
271 className="w-38 **:data-[slot=select-value]:block **:data-[slot=select-value]:truncate"
272 size="sm"
273 id={`${row.original.id}-reviewer`}
274 >
275 <SelectValue placeholder="Assign reviewer" />
276 </SelectTrigger>
277 <SelectContent align="end">
278 <SelectItem value="Eddie Lake">Eddie Lake</SelectItem>
279 <SelectItem value="Jamik Tashpulatov">
280 Jamik Tashpulatov
281 </SelectItem>
282 </SelectContent>
283 </Select>
284 </>
285 )
286 },
287 },
288 {
289 id: "actions",
290 cell: () => (
291 <DropdownMenu>
292 <DropdownMenuTrigger asChild>
293 <Button
294 variant="ghost"
295 className="data-[state=open]:bg-muted text-muted-foreground flex size-8"
296 size="icon"
297 >
298 <IconDotsVertical />
299 <span className="sr-only">Open menu</span>
300 </Button>
301 </DropdownMenuTrigger>
302 <DropdownMenuContent align="end" className="w-32">
303 <DropdownMenuItem>Edit</DropdownMenuItem>
304 <DropdownMenuItem>Make a copy</DropdownMenuItem>
305 <DropdownMenuItem>Favorite</DropdownMenuItem>
306 <DropdownMenuSeparator />
307 <DropdownMenuItem variant="destructive">Delete</DropdownMenuItem>
308 </DropdownMenuContent>
309 </DropdownMenu>
310 ),
311 },
312]
313
314function DraggableRow({ row }: { row: Row<z.infer<typeof schema>> }) {
315 const { transform, transition, setNodeRef, isDragging } = useSortable({
316 id: row.original.id,
317 })
318
319 return (
320 <TableRow
321 data-state={row.getIsSelected() && "selected"}
322 data-dragging={isDragging}
323 ref={setNodeRef}
324 className="relative z-0 data-[dragging=true]:z-10 data-[dragging=true]:opacity-80"
325 style={{
326 transform: CSS.Transform.toString(transform),
327 transition: transition,
328 }}
329 >
330 {row.getVisibleCells().map((cell) => (
331 <TableCell key={cell.id}>
332 {flexRender(cell.column.columnDef.cell, cell.getContext())}
333 </TableCell>
334 ))}
335 </TableRow>
336 )
337}
338
339export function DataTable({
340 data: initialData,
341}: {
342 data: z.infer<typeof schema>[]
343}) {
344 const [data, setData] = React.useState(() => initialData)
345 const [rowSelection, setRowSelection] = React.useState({})
346 const [columnVisibility, setColumnVisibility] =
347 React.useState<VisibilityState>({})
348 const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
349 []
350 )
351 const [sorting, setSorting] = React.useState<SortingState>([])
352 const [pagination, setPagination] = React.useState({
353 pageIndex: 0,
354 pageSize: 10,
355 })
356 const sortableId = React.useId()
357 const sensors = useSensors(
358 useSensor(MouseSensor, {}),
359 useSensor(TouchSensor, {}),
360 useSensor(KeyboardSensor, {})
361 )
362
363 const dataIds = React.useMemo<UniqueIdentifier[]>(
364 () => data?.map(({ id }) => id) || [],
365 [data]
366 )
367
368 const table = useReactTable({
369 data,
370 columns,
371 state: {
372 sorting,
373 columnVisibility,
374 rowSelection,
375 columnFilters,
376 pagination,
377 },
378 getRowId: (row) => row.id.toString(),
379 enableRowSelection: true,
380 onRowSelectionChange: setRowSelection,
381 onSortingChange: setSorting,
382 onColumnFiltersChange: setColumnFilters,
383 onColumnVisibilityChange: setColumnVisibility,
384 onPaginationChange: setPagination,
385 getCoreRowModel: getCoreRowModel(),
386 getFilteredRowModel: getFilteredRowModel(),
387 getPaginationRowModel: getPaginationRowModel(),
388 getSortedRowModel: getSortedRowModel(),
389 getFacetedRowModel: getFacetedRowModel(),
390 getFacetedUniqueValues: getFacetedUniqueValues(),
391 })
392
393 function handleDragEnd(event: DragEndEvent) {
394 const { active, over } = event
395 if (active && over && active.id !== over.id) {
396 setData((data) => {
397 const oldIndex = dataIds.indexOf(active.id)
398 const newIndex = dataIds.indexOf(over.id)
399 return arrayMove(data, oldIndex, newIndex)
400 })
401 }
402 }
403
404 return (
405 <Tabs
406 defaultValue="outline"
407 className="w-full flex-col justify-start gap-6"
408 >
409 <div className="flex items-center justify-between px-4 lg:px-6">
410 <Label htmlFor="view-selector" className="sr-only">
411 View
412 </Label>
413 <Select defaultValue="outline">
414 <SelectTrigger
415 className="flex w-fit @4xl/main:hidden"
416 size="sm"
417 id="view-selector"
418 >
419 <SelectValue placeholder="Select a view" />
420 </SelectTrigger>
421 <SelectContent>
422 <SelectItem value="outline">Outline</SelectItem>
423 <SelectItem value="past-performance">Past Performance</SelectItem>
424 <SelectItem value="key-personnel">Key Personnel</SelectItem>
425 <SelectItem value="focus-documents">Focus Documents</SelectItem>
426 </SelectContent>
427 </Select>
428 <TabsList className="**:data-[slot=badge]:bg-muted-foreground/30 hidden **:data-[slot=badge]:size-5 **:data-[slot=badge]:rounded-full **:data-[slot=badge]:px-1 @4xl/main:flex">
429 <TabsTrigger value="outline">Outline</TabsTrigger>
430 <TabsTrigger value="past-performance">
431 Past Performance <Badge variant="secondary">3</Badge>
432 </TabsTrigger>
433 <TabsTrigger value="key-personnel">
434 Key Personnel <Badge variant="secondary">2</Badge>
435 </TabsTrigger>
436 <TabsTrigger value="focus-documents">Focus Documents</TabsTrigger>
437 </TabsList>
438 <div className="flex items-center gap-2">
439 <DropdownMenu>
440 <DropdownMenuTrigger asChild>
441 <Button variant="outline" size="sm">
442 <IconLayoutColumns />
443 <span className="hidden lg:inline">Customize Columns</span>
444 <span className="lg:hidden">Columns</span>
445 <IconChevronDown />
446 </Button>
447 </DropdownMenuTrigger>
448 <DropdownMenuContent align="end" className="w-56">
449 {table
450 .getAllColumns()
451 .filter(
452 (column) =>
453 typeof column.accessorFn !== "undefined" &&
454 column.getCanHide()
455 )
456 .map((column) => {
457 return (
458 <DropdownMenuCheckboxItem
459 key={column.id}
460 className="capitalize"
461 checked={column.getIsVisible()}
462 onCheckedChange={(value) =>
463 column.toggleVisibility(!!value)
464 }
465 >
466 {column.id}
467 </DropdownMenuCheckboxItem>
468 )
469 })}
470 </DropdownMenuContent>
471 </DropdownMenu>
472 <Button variant="outline" size="sm">
473 <IconPlus />
474 <span className="hidden lg:inline">Add Section</span>
475 </Button>
476 </div>
477 </div>
478 <TabsContent
479 value="outline"
480 className="relative flex flex-col gap-4 overflow-auto px-4 lg:px-6"
481 >
482 <div className="overflow-hidden rounded-lg border">
483 <DndContext
484 collisionDetection={closestCenter}
485 modifiers={[restrictToVerticalAxis]}
486 onDragEnd={handleDragEnd}
487 sensors={sensors}
488 id={sortableId}
489 >
490 <Table>
491 <TableHeader className="bg-muted sticky top-0 z-10">
492 {table.getHeaderGroups().map((headerGroup) => (
493 <TableRow key={headerGroup.id}>
494 {headerGroup.headers.map((header) => {
495 return (
496 <TableHead key={header.id} colSpan={header.colSpan}>
497 {header.isPlaceholder
498 ? null
499 : flexRender(
500 header.column.columnDef.header,
501 header.getContext()
502 )}
503 </TableHead>
504 )
505 })}
506 </TableRow>
507 ))}
508 </TableHeader>
509 <TableBody className="**:data-[slot=table-cell]:first:w-8">
510 {table.getRowModel().rows?.length ? (
511 <SortableContext
512 items={dataIds}
513 strategy={verticalListSortingStrategy}
514 >
515 {table.getRowModel().rows.map((row) => (
516 <DraggableRow key={row.id} row={row} />
517 ))}
518 </SortableContext>
519 ) : (
520 <TableRow>
521 <TableCell
522 colSpan={columns.length}
523 className="h-24 text-center"
524 >
525 No results.
526 </TableCell>
527 </TableRow>
528 )}
529 </TableBody>
530 </Table>
531 </DndContext>
532 </div>
533 <div className="flex items-center justify-between px-4">
534 <div className="text-muted-foreground hidden flex-1 text-sm lg:flex">
535 {table.getFilteredSelectedRowModel().rows.length} of{" "}
536 {table.getFilteredRowModel().rows.length} row(s) selected.
537 </div>
538 <div className="flex w-full items-center gap-8 lg:w-fit">
539 <div className="hidden items-center gap-2 lg:flex">
540 <Label htmlFor="rows-per-page" className="text-sm font-medium">
541 Rows per page
542 </Label>
543 <Select
544 value={`${table.getState().pagination.pageSize}`}
545 onValueChange={(value) => {
546 table.setPageSize(Number(value))
547 }}
548 >
549 <SelectTrigger size="sm" className="w-20" id="rows-per-page">
550 <SelectValue
551 placeholder={table.getState().pagination.pageSize}
552 />
553 </SelectTrigger>
554 <SelectContent side="top">
555 {[10, 20, 30, 40, 50].map((pageSize) => (
556 <SelectItem key={pageSize} value={`${pageSize}`}>
557 {pageSize}
558 </SelectItem>
559 ))}
560 </SelectContent>
561 </Select>
562 </div>
563 <div className="flex w-fit items-center justify-center text-sm font-medium">
564 Page {table.getState().pagination.pageIndex + 1} of{" "}
565 {table.getPageCount()}
566 </div>
567 <div className="ml-auto flex items-center gap-2 lg:ml-0">
568 <Button
569 variant="outline"
570 className="hidden h-8 w-8 p-0 lg:flex"
571 onClick={() => table.setPageIndex(0)}
572 disabled={!table.getCanPreviousPage()}
573 >
574 <span className="sr-only">Go to first page</span>
575 <IconChevronsLeft />
576 </Button>
577 <Button
578 variant="outline"
579 className="size-8"
580 size="icon"
581 onClick={() => table.previousPage()}
582 disabled={!table.getCanPreviousPage()}
583 >
584 <span className="sr-only">Go to previous page</span>
585 <IconChevronLeft />
586 </Button>
587 <Button
588 variant="outline"
589 className="size-8"
590 size="icon"
591 onClick={() => table.nextPage()}
592 disabled={!table.getCanNextPage()}
593 >
594 <span className="sr-only">Go to next page</span>
595 <IconChevronRight />
596 </Button>
597 <Button
598 variant="outline"
599 className="hidden size-8 lg:flex"
600 size="icon"
601 onClick={() => table.setPageIndex(table.getPageCount() - 1)}
602 disabled={!table.getCanNextPage()}
603 >
604 <span className="sr-only">Go to last page</span>
605 <IconChevronsRight />
606 </Button>
607 </div>
608 </div>
609 </div>
610 </TabsContent>
611 <TabsContent
612 value="past-performance"
613 className="flex flex-col px-4 lg:px-6"
614 >
615 <div className="aspect-video w-full flex-1 rounded-lg border border-dashed"></div>
616 </TabsContent>
617 <TabsContent value="key-personnel" className="flex flex-col px-4 lg:px-6">
618 <div className="aspect-video w-full flex-1 rounded-lg border border-dashed"></div>
619 </TabsContent>
620 <TabsContent
621 value="focus-documents"
622 className="flex flex-col px-4 lg:px-6"
623 >
624 <div className="aspect-video w-full flex-1 rounded-lg border border-dashed"></div>
625 </TabsContent>
626 </Tabs>
627 )
628}
629
630const chartData = [
631 { month: "January", desktop: 186, mobile: 80 },
632 { month: "February", desktop: 305, mobile: 200 },
633 { month: "March", desktop: 237, mobile: 120 },
634 { month: "April", desktop: 73, mobile: 190 },
635 { month: "May", desktop: 209, mobile: 130 },
636 { month: "June", desktop: 214, mobile: 140 },
637]
638
639const chartConfig = {
640 desktop: {
641 label: "Desktop",
642 color: "var(--primary)",
643 },
644 mobile: {
645 label: "Mobile",
646 color: "var(--primary)",
647 },
648} satisfies ChartConfig
649
650function TableCellViewer({ item }: { item: z.infer<typeof schema> }) {
651 const isMobile = useIsMobile()
652
653 return (
654 <Drawer direction={isMobile ? "bottom" : "right"}>
655 <DrawerTrigger asChild>
656 <Button variant="link" className="text-foreground w-fit px-0 text-left">
657 {item.header}
658 </Button>
659 </DrawerTrigger>
660 <DrawerContent>
661 <DrawerHeader className="gap-1">
662 <DrawerTitle>{item.header}</DrawerTitle>
663 <DrawerDescription>
664 Showing total visitors for the last 6 months
665 </DrawerDescription>
666 </DrawerHeader>
667 <div className="flex flex-col gap-4 overflow-y-auto px-4 text-sm">
668 {!isMobile && (
669 <>
670 <ChartContainer config={chartConfig}>
671 <AreaChart
672 accessibilityLayer
673 data={chartData}
674 margin={{
675 left: 0,
676 right: 10,
677 }}
678 >
679 <CartesianGrid vertical={false} />
680 <XAxis
681 dataKey="month"
682 tickLine={false}
683 axisLine={false}
684 tickMargin={8}
685 tickFormatter={(value) => value.slice(0, 3)}
686 hide
687 />
688 <ChartTooltip
689 cursor={false}
690 content={<ChartTooltipContent indicator="dot" />}
691 />
692 <Area
693 dataKey="mobile"
694 type="natural"
695 fill="var(--color-mobile)"
696 fillOpacity={0.6}
697 stroke="var(--color-mobile)"
698 stackId="a"
699 />
700 <Area
701 dataKey="desktop"
702 type="natural"
703 fill="var(--color-desktop)"
704 fillOpacity={0.4}
705 stroke="var(--color-desktop)"
706 stackId="a"
707 />
708 </AreaChart>
709 </ChartContainer>
710 <Separator />
711 <div className="grid gap-2">
712 <div className="flex gap-2 leading-none font-medium">
713 Trending up by 5.2% this month{" "}
714 <IconTrendingUp className="size-4" />
715 </div>
716 <div className="text-muted-foreground">
717 Showing total visitors for the last 6 months. This is just
718 some random text to test the layout. It spans multiple lines
719 and should wrap around.
720 </div>
721 </div>
722 <Separator />
723 </>
724 )}
725 <form className="flex flex-col gap-4">
726 <div className="flex flex-col gap-3">
727 <Label htmlFor="header">Header</Label>
728 <Input id="header" defaultValue={item.header} />
729 </div>
730 <div className="grid grid-cols-2 gap-4">
731 <div className="flex flex-col gap-3">
732 <Label htmlFor="type">Type</Label>
733 <Select defaultValue={item.type}>
734 <SelectTrigger id="type" className="w-full">
735 <SelectValue placeholder="Select a type" />
736 </SelectTrigger>
737 <SelectContent>
738 <SelectItem value="Table of Contents">
739 Table of Contents
740 </SelectItem>
741 <SelectItem value="Executive Summary">
742 Executive Summary
743 </SelectItem>
744 <SelectItem value="Technical Approach">
745 Technical Approach
746 </SelectItem>
747 <SelectItem value="Design">Design</SelectItem>
748 <SelectItem value="Capabilities">Capabilities</SelectItem>
749 <SelectItem value="Focus Documents">
750 Focus Documents
751 </SelectItem>
752 <SelectItem value="Narrative">Narrative</SelectItem>
753 <SelectItem value="Cover Page">Cover Page</SelectItem>
754 </SelectContent>
755 </Select>
756 </div>
757 <div className="flex flex-col gap-3">
758 <Label htmlFor="status">Status</Label>
759 <Select defaultValue={item.status}>
760 <SelectTrigger id="status" className="w-full">
761 <SelectValue placeholder="Select a status" />
762 </SelectTrigger>
763 <SelectContent>
764 <SelectItem value="Done">Done</SelectItem>
765 <SelectItem value="In Progress">In Progress</SelectItem>
766 <SelectItem value="Not Started">Not Started</SelectItem>
767 </SelectContent>
768 </Select>
769 </div>
770 </div>
771 <div className="grid grid-cols-2 gap-4">
772 <div className="flex flex-col gap-3">
773 <Label htmlFor="target">Target</Label>
774 <Input id="target" defaultValue={item.target} />
775 </div>
776 <div className="flex flex-col gap-3">
777 <Label htmlFor="limit">Limit</Label>
778 <Input id="limit" defaultValue={item.limit} />
779 </div>
780 </div>
781 <div className="flex flex-col gap-3">
782 <Label htmlFor="reviewer">Reviewer</Label>
783 <Select defaultValue={item.reviewer}>
784 <SelectTrigger id="reviewer" className="w-full">
785 <SelectValue placeholder="Select a reviewer" />
786 </SelectTrigger>
787 <SelectContent>
788 <SelectItem value="Eddie Lake">Eddie Lake</SelectItem>
789 <SelectItem value="Jamik Tashpulatov">
790 Jamik Tashpulatov
791 </SelectItem>
792 <SelectItem value="Emily Whalen">Emily Whalen</SelectItem>
793 </SelectContent>
794 </Select>
795 </div>
796 </form>
797 </div>
798 <DrawerFooter>
799 <Button>Submit</Button>
800 <DrawerClose asChild>
801 <Button variant="outline">Done</Button>
802 </DrawerClose>
803 </DrawerFooter>
804 </DrawerContent>
805 </Drawer>
806 )
807}