@phsa.tec/design-system-react 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (201) hide show
  1. package/.eslintrc.json +7 -0
  2. package/.storybook/main.ts +16 -0
  3. package/.storybook/preview.ts +15 -0
  4. package/README.md +36 -0
  5. package/components.json +21 -0
  6. package/jest.config.ts +25 -0
  7. package/next.config.ts +7 -0
  8. package/package.json +88 -0
  9. package/postcss.config.mjs +8 -0
  10. package/public/file.svg +1 -0
  11. package/public/globe.svg +1 -0
  12. package/public/next.svg +1 -0
  13. package/public/vercel.svg +1 -0
  14. package/public/window.svg +1 -0
  15. package/src/app/columns.tsx +178 -0
  16. package/src/app/favicon.ico +0 -0
  17. package/src/app/fonts/GeistMonoVF.woff +0 -0
  18. package/src/app/fonts/GeistVF.woff +0 -0
  19. package/src/app/globals.css +94 -0
  20. package/src/app/layout.tsx +35 -0
  21. package/src/app/page.tsx +7 -0
  22. package/src/components/actions/AlertDialog/AlertDialog.tsx +45 -0
  23. package/src/components/actions/AlertDialog/alert-dialog.stories.tsx +21 -0
  24. package/src/components/actions/AlertDialog/index.ts +1 -0
  25. package/src/components/actions/Button/Button.stories.ts +38 -0
  26. package/src/components/actions/Button/Button.tsx +23 -0
  27. package/src/components/actions/Button/index.ts +1 -0
  28. package/src/components/actions/Collapsible/index.ts +1 -0
  29. package/src/components/actions/Dialog/Dialog.stories.tsx +70 -0
  30. package/src/components/actions/Dialog/Dialog.tsx +87 -0
  31. package/src/components/actions/Dialog/components/DialogWithActions/index.tsx +40 -0
  32. package/src/components/actions/Dialog/index.ts +1 -0
  33. package/src/components/actions/Steps/Steps.stories.tsx +25 -0
  34. package/src/components/actions/Steps/Steps.tsx +51 -0
  35. package/src/components/actions/Steps/index.ts +1 -0
  36. package/src/components/actions/index.ts +5 -0
  37. package/src/components/dataDisplay/Avatar/Avatar.stories.tsx +22 -0
  38. package/src/components/dataDisplay/Avatar/Avatar.tsx +21 -0
  39. package/src/components/dataDisplay/Avatar/index.ts +2 -0
  40. package/src/components/dataDisplay/Badge/Badge.stories.tsx +36 -0
  41. package/src/components/dataDisplay/Badge/index.ts +1 -0
  42. package/src/components/dataDisplay/Card/Card.stories.tsx +24 -0
  43. package/src/components/dataDisplay/Card/Card.tsx +34 -0
  44. package/src/components/dataDisplay/Card/index.ts +1 -0
  45. package/src/components/dataDisplay/DataPairList/DataPairList.tsx +56 -0
  46. package/src/components/dataDisplay/DataPairList/data-pair-list.stories.tsx +87 -0
  47. package/src/components/dataDisplay/DataPairList/index.ts +2 -0
  48. package/src/components/dataDisplay/DataPairList/types.ts +10 -0
  49. package/src/components/dataDisplay/DropDownMenu/index.ts +1 -0
  50. package/src/components/dataDisplay/ErrorMessage/ErrorMessage.tsx +6 -0
  51. package/src/components/dataDisplay/ErrorMessage/index.ts +1 -0
  52. package/src/components/dataDisplay/Icon/Icon.stories.tsx +21 -0
  53. package/src/components/dataDisplay/Icon/Icon.tsx +47 -0
  54. package/src/components/dataDisplay/Icon/index.ts +1 -0
  55. package/src/components/dataDisplay/Icon/types.ts +6 -0
  56. package/src/components/dataDisplay/Label/Label.stories.tsx +21 -0
  57. package/src/components/dataDisplay/Label/Label.tsx +10 -0
  58. package/src/components/dataDisplay/Label/index.ts +1 -0
  59. package/src/components/dataDisplay/Table/Table.tsx +173 -0
  60. package/src/components/dataDisplay/Table/columns.tsx +223 -0
  61. package/src/components/dataDisplay/Table/components/DynamicTable/data-table-column-header.tsx +72 -0
  62. package/src/components/dataDisplay/Table/components/DynamicTable/data-table-pagination.tsx +91 -0
  63. package/src/components/dataDisplay/Table/components/DynamicTable/data-table-toolbar.tsx +17 -0
  64. package/src/components/dataDisplay/Table/components/DynamicTable/data-table-view-options.tsx +58 -0
  65. package/src/components/dataDisplay/Table/components/DynamicTable/data-table.stories.tsx +118 -0
  66. package/src/components/dataDisplay/Table/components/DynamicTable/index.tsx +136 -0
  67. package/src/components/dataDisplay/Table/components/DynamicTable/types.ts +43 -0
  68. package/src/components/dataDisplay/Table/custom/CustomTable/data-table-column-header.tsx +71 -0
  69. package/src/components/dataDisplay/Table/custom/CustomTable/data-table-faceted-filter.tsx +147 -0
  70. package/src/components/dataDisplay/Table/custom/CustomTable/data-table-pagination.tsx +97 -0
  71. package/src/components/dataDisplay/Table/custom/CustomTable/data-table-row-actions.tsx +78 -0
  72. package/src/components/dataDisplay/Table/custom/CustomTable/data-table-toolbar.tsx +60 -0
  73. package/src/components/dataDisplay/Table/custom/CustomTable/data-table-view-options.tsx +59 -0
  74. package/src/components/dataDisplay/Table/custom/CustomTable/data-table.tsx +145 -0
  75. package/src/components/dataDisplay/Table/custom/CustomTable/data.ts +71 -0
  76. package/src/components/dataDisplay/Table/custom/CustomTable/index.tsx +34 -0
  77. package/src/components/dataDisplay/Table/custom/CustomTable/schema.ts +11 -0
  78. package/src/components/dataDisplay/Table/index.ts +2 -0
  79. package/src/components/dataDisplay/Table/table.stories.tsx +147 -0
  80. package/src/components/dataDisplay/Table/types.ts +15 -0
  81. package/src/components/dataDisplay/Tabs/Tabs.stories.tsx +34 -0
  82. package/src/components/dataDisplay/Tabs/Tabs.tsx +53 -0
  83. package/src/components/dataDisplay/Tabs/index.ts +1 -0
  84. package/src/components/dataDisplay/Text/Text.stories.tsx +66 -0
  85. package/src/components/dataDisplay/Text/Text.tsx +56 -0
  86. package/src/components/dataDisplay/Text/index.ts +1 -0
  87. package/src/components/dataDisplay/index.ts +8 -0
  88. package/src/components/dataInput/Input/components/Input/Input.stories.tsx +99 -0
  89. package/src/components/dataInput/Input/components/Input/InputBase.tsx +50 -0
  90. package/src/components/dataInput/Input/components/Input/__tests__/Input.test.tsx +100 -0
  91. package/src/components/dataInput/Input/components/Input/index.tsx +257 -0
  92. package/src/components/dataInput/Input/components/InputBase/__tests__/InputBase.test.tsx +120 -0
  93. package/src/components/dataInput/Input/components/InputBase/index.tsx +89 -0
  94. package/src/components/dataInput/Input/components/MaskInput/__tests__/mask-input.test.tsx +67 -0
  95. package/src/components/dataInput/Input/components/MaskInput/index.ts +1 -0
  96. package/src/components/dataInput/Input/components/MaskInput/mask-input.stories.tsx +59 -0
  97. package/src/components/dataInput/Input/components/MaskInput/mask-input.tsx +43 -0
  98. package/src/components/dataInput/Input/components/MultipleInput/MultipleInput.tsx +36 -0
  99. package/src/components/dataInput/Input/components/MultipleInput/MultipleInputBase.tsx +100 -0
  100. package/src/components/dataInput/Input/components/MultipleInput/MultipleMaskInput.tsx +35 -0
  101. package/src/components/dataInput/Input/components/MultipleInput/MultipleNumberInput.tsx +35 -0
  102. package/src/components/dataInput/Input/components/MultipleInput/index.ts +2 -0
  103. package/src/components/dataInput/Input/components/MultipleInput/multiple-input.stories.tsx +71 -0
  104. package/src/components/dataInput/Input/components/NumberInput/__tests__/number-input.test.tsx +95 -0
  105. package/src/components/dataInput/Input/components/NumberInput/index.ts +1 -0
  106. package/src/components/dataInput/Input/components/NumberInput/number-input.stories.tsx +76 -0
  107. package/src/components/dataInput/Input/components/NumberInput/number-input.tsx +68 -0
  108. package/src/components/dataInput/Input/index.ts +4 -0
  109. package/src/components/dataInput/Select/MultiSelect/MultiSelect.stories.tsx +119 -0
  110. package/src/components/dataInput/Select/MultiSelect/MultiSelectBase.tsx +135 -0
  111. package/src/components/dataInput/Select/MultiSelect/index.tsx +75 -0
  112. package/src/components/dataInput/Select/Select.stories.tsx +61 -0
  113. package/src/components/dataInput/Select/Select.tsx +73 -0
  114. package/src/components/dataInput/Select/SelectBase.tsx +58 -0
  115. package/src/components/dataInput/Select/index.ts +2 -0
  116. package/src/components/dataInput/Switch/Switch.stories.tsx +75 -0
  117. package/src/components/dataInput/Switch/Switch.tsx +52 -0
  118. package/src/components/dataInput/Switch/index.ts +1 -0
  119. package/src/components/dataInput/checkbox/Checkbox.tsx +57 -0
  120. package/src/components/dataInput/checkbox/Checkbox_old.tsx +58 -0
  121. package/src/components/dataInput/checkbox/Checkout.stories.tsx +62 -0
  122. package/src/components/dataInput/checkbox/index.ts +1 -0
  123. package/src/components/dataInput/form/Form.tsx +47 -0
  124. package/src/components/dataInput/form/index.ts +3 -0
  125. package/src/components/dataInput/index.ts +5 -0
  126. package/src/components/feedback/Spinner/index.ts +1 -0
  127. package/src/components/feedback/Toast/Toast.stories.tsx +45 -0
  128. package/src/components/feedback/Toast/index.ts +2 -0
  129. package/src/components/feedback/index.ts +2 -0
  130. package/src/components/index.ts +6 -0
  131. package/src/components/layout/Crud/components/Table/index.tsx +183 -0
  132. package/src/components/layout/Crud/components/Table/types.ts +15 -0
  133. package/src/components/layout/Crud/crud.stories.tsx +317 -0
  134. package/src/components/layout/Crud/hook/useCrudLayout/index.tsx +94 -0
  135. package/src/components/layout/Crud/hook/useRequest/index.tsx +156 -0
  136. package/src/components/layout/Crud/index.tsx +295 -0
  137. package/src/components/layout/Crud/store/CrudLayoutStore.ts +75 -0
  138. package/src/components/layout/Crud/types.ts +14 -0
  139. package/src/components/layout/Drawer/CustomDrawer/index.tsx +33 -0
  140. package/src/components/layout/Drawer/Drawer.stories.tsx +80 -0
  141. package/src/components/layout/Drawer/index.ts +2 -0
  142. package/src/components/layout/PageLayout/PageLayout.stories.tsx +42 -0
  143. package/src/components/layout/PageLayout/index.tsx +28 -0
  144. package/src/components/layout/Separator/index.ts +1 -0
  145. package/src/components/layout/Sheet/Sheet.stories.tsx +28 -0
  146. package/src/components/layout/Sheet/Sheet.tsx +22 -0
  147. package/src/components/layout/Sheet/index.ts +1 -0
  148. package/src/components/layout/Sidebar/Sidebar.stories.tsx +116 -0
  149. package/src/components/layout/Sidebar/Sidebar.tsx +50 -0
  150. package/src/components/layout/Sidebar/components/app-sidebar.tsx +203 -0
  151. package/src/components/layout/Sidebar/components/footer-sidebar.tsx +17 -0
  152. package/src/components/layout/Sidebar/components/header-sidebar.tsx +90 -0
  153. package/src/components/layout/Sidebar/components/menus.tsx +55 -0
  154. package/src/components/layout/Sidebar/components/nav-projects.tsx +88 -0
  155. package/src/components/layout/Sidebar/components/nav-user.tsx +114 -0
  156. package/src/components/layout/Sidebar/components/team-switcher.tsx +85 -0
  157. package/src/components/layout/Sidebar/index.ts +2 -0
  158. package/src/components/layout/Sidebar/provider/index.tsx +51 -0
  159. package/src/components/layout/Tabs/Tabs.tsx +51 -0
  160. package/src/components/layout/Tabs/index.ts +1 -0
  161. package/src/components/layout/Tabs/tabs.stories.tsx +57 -0
  162. package/src/components/layout/index.ts +6 -0
  163. package/src/components/navigation/Breadcrumbs/Breadcrumbs.tsx +66 -0
  164. package/src/components/navigation/Breadcrumbs/index.ts +2 -0
  165. package/src/components/navigation/index.ts +1 -0
  166. package/src/components/ui/alert-dialog.tsx +141 -0
  167. package/src/components/ui/alert.tsx +59 -0
  168. package/src/components/ui/avatar.tsx +50 -0
  169. package/src/components/ui/badge.tsx +40 -0
  170. package/src/components/ui/breadcrumb.tsx +115 -0
  171. package/src/components/ui/button.tsx +57 -0
  172. package/src/components/ui/card.tsx +83 -0
  173. package/src/components/ui/checkbox.tsx +34 -0
  174. package/src/components/ui/collapsible.tsx +11 -0
  175. package/src/components/ui/command.tsx +153 -0
  176. package/src/components/ui/dialog.tsx +124 -0
  177. package/src/components/ui/drawer.tsx +117 -0
  178. package/src/components/ui/dropdown-menu.tsx +201 -0
  179. package/src/components/ui/form.tsx +179 -0
  180. package/src/components/ui/input.tsx +24 -0
  181. package/src/components/ui/label.tsx +30 -0
  182. package/src/components/ui/popover.tsx +33 -0
  183. package/src/components/ui/select.tsx +161 -0
  184. package/src/components/ui/separator.tsx +31 -0
  185. package/src/components/ui/sheet.tsx +140 -0
  186. package/src/components/ui/sidebar.tsx +763 -0
  187. package/src/components/ui/skeleton.tsx +15 -0
  188. package/src/components/ui/sonner.tsx +31 -0
  189. package/src/components/ui/spinner.tsx +54 -0
  190. package/src/components/ui/switch.tsx +33 -0
  191. package/src/components/ui/table.tsx +120 -0
  192. package/src/components/ui/tabs.tsx +55 -0
  193. package/src/components/ui/toast.tsx +130 -0
  194. package/src/components/ui/toaster.tsx +35 -0
  195. package/src/components/ui/tooltip.tsx +32 -0
  196. package/src/hooks/use-mobile.tsx +19 -0
  197. package/src/hooks/use-toast.ts +191 -0
  198. package/src/index.ts +1 -0
  199. package/src/lib/utils.ts +6 -0
  200. package/tailwind.config.ts +83 -0
  201. package/tsconfig.json +27 -0
@@ -0,0 +1,58 @@
1
+ "use client";
2
+
3
+ import { DropdownMenuTrigger } from "@radix-ui/react-dropdown-menu";
4
+ import { MixerHorizontalIcon } from "@radix-ui/react-icons";
5
+ import { Table } from "@tanstack/react-table";
6
+ import { Button } from "../../../../../components/ui/button";
7
+ import {
8
+ DropdownMenu,
9
+ DropdownMenuCheckboxItem,
10
+ DropdownMenuContent,
11
+ DropdownMenuLabel,
12
+ DropdownMenuSeparator,
13
+ } from "../../../../../components/ui/dropdown-menu";
14
+
15
+ interface DataTableViewOptionsProps<TData> {
16
+ table: Table<TData>;
17
+ }
18
+
19
+ export function DataTableViewOptions<TData>({
20
+ table,
21
+ }: DataTableViewOptionsProps<TData>) {
22
+ return (
23
+ <DropdownMenu>
24
+ <DropdownMenuTrigger asChild>
25
+ <Button
26
+ variant="outline"
27
+ size="sm"
28
+ className="ml-auto hidden h-8 lg:flex"
29
+ >
30
+ <MixerHorizontalIcon className="mr-2 h-4 w-4" />
31
+ Colunas
32
+ </Button>
33
+ </DropdownMenuTrigger>
34
+ <DropdownMenuContent align="end" className="w-[150px]">
35
+ <DropdownMenuLabel>Colunas visíveis</DropdownMenuLabel>
36
+ <DropdownMenuSeparator />
37
+ {table
38
+ .getAllColumns()
39
+ .filter(
40
+ (column) =>
41
+ typeof column.accessorFn !== "undefined" && column.getCanHide()
42
+ )
43
+ .map((column) => {
44
+ return (
45
+ <DropdownMenuCheckboxItem
46
+ key={column.id}
47
+ className="capitalize"
48
+ checked={column.getIsVisible()}
49
+ onCheckedChange={(value) => column.toggleVisibility(!!value)}
50
+ >
51
+ {column.id}
52
+ </DropdownMenuCheckboxItem>
53
+ );
54
+ })}
55
+ </DropdownMenuContent>
56
+ </DropdownMenu>
57
+ );
58
+ }
@@ -0,0 +1,118 @@
1
+ import type { Meta, StoryObj } from "@storybook/nextjs";
2
+ import { DynamicTable } from "./index";
3
+ import { Button } from "../../../../../components/ui/button";
4
+ import { Input } from "../../../../../components/ui/input";
5
+ import { ColumnDef } from "@tanstack/react-table";
6
+
7
+ interface User {
8
+ id: number;
9
+ name: string;
10
+ email: string;
11
+ role: string;
12
+ status: "active" | "inactive";
13
+ }
14
+
15
+ const meta: Meta<typeof DynamicTable> = {
16
+ title: "DataDisplay/DynamicTable",
17
+ component: DynamicTable,
18
+ parameters: {
19
+ layout: "centered",
20
+ },
21
+ tags: ["autodocs"],
22
+ } satisfies Meta<typeof DynamicTable>;
23
+
24
+ export default meta;
25
+ type Story = StoryObj<typeof DynamicTable<User>>;
26
+
27
+ const data: User[] = [
28
+ {
29
+ id: 1,
30
+ name: "John Doe",
31
+ email: "john@example.com",
32
+ role: "Admin",
33
+ status: "active",
34
+ },
35
+ {
36
+ id: 2,
37
+ name: "Jane Smith",
38
+ email: "jane@example.com",
39
+ role: "User",
40
+ status: "inactive",
41
+ },
42
+ ];
43
+
44
+ const columns: ColumnDef<User>[] = [
45
+ {
46
+ accessorKey: "id",
47
+ header: "ID",
48
+ },
49
+ {
50
+ accessorKey: "name",
51
+ header: "Nome",
52
+ },
53
+ {
54
+ accessorKey: "email",
55
+ header: "Email",
56
+ },
57
+ {
58
+ accessorKey: "role",
59
+ header: "Função",
60
+ },
61
+ {
62
+ accessorKey: "status",
63
+ header: "Status",
64
+ cell: ({ row }) => (
65
+ <span
66
+ className={`px-2 py-1 rounded-full text-xs font-medium ${
67
+ row.original.status === "active"
68
+ ? "bg-green-100 text-green-800"
69
+ : "bg-red-100 text-red-800"
70
+ }`}
71
+ >
72
+ {row.original.status === "active" ? "Ativo" : "Inativo"}
73
+ </span>
74
+ ),
75
+ },
76
+ {
77
+ id: "actions",
78
+ cell: () => (
79
+ <Button variant="ghost" size="sm">
80
+ Editar
81
+ </Button>
82
+ ),
83
+ },
84
+ ];
85
+
86
+ export const Default: Story = {
87
+ args: {
88
+ data,
89
+ columns,
90
+ },
91
+ };
92
+
93
+ export const WithFilters: Story = {
94
+ args: {
95
+ ...Default.args,
96
+ filters: (
97
+ <>
98
+ <Input placeholder="Buscar por nome..." className="max-w-xs" />
99
+ <Button>Filtrar</Button>
100
+ </>
101
+ ),
102
+ },
103
+ };
104
+
105
+ export const WithPagination: Story = {
106
+ args: {
107
+ ...Default.args,
108
+ pagination: true,
109
+ rowsPerPage: [5, 10, 20],
110
+ },
111
+ };
112
+
113
+ export const WithColumnVisibility: Story = {
114
+ args: {
115
+ ...Default.args,
116
+ columnVisibility: true,
117
+ },
118
+ };
@@ -0,0 +1,136 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import {
5
+ flexRender,
6
+ getCoreRowModel,
7
+ getSortedRowModel,
8
+ getPaginationRowModel,
9
+ useReactTable,
10
+ SortingState,
11
+ VisibilityState,
12
+ PaginationState,
13
+ } from "@tanstack/react-table";
14
+ import {
15
+ Table,
16
+ TableBody,
17
+ TableCell,
18
+ TableHead,
19
+ TableHeader,
20
+ TableRow,
21
+ } from "../../../../../components/ui/table";
22
+ import { DataTableToolbar } from "./data-table-toolbar";
23
+ import { DataTablePagination } from "./data-table-pagination";
24
+ import { cn } from "../../../../../lib/utils";
25
+ import { DataTableColumnHeader } from "./data-table-column-header";
26
+ import { DynamicTableProps } from "./types";
27
+
28
+ export function DynamicTable<TData>({
29
+ data,
30
+ columns,
31
+ className,
32
+ toolbar,
33
+ pagination: showPagination = true,
34
+ sorting: showSorting = true,
35
+ columnVisibility: showColumnVisibility = true,
36
+ filters,
37
+ rowsPerPage = [10, 20, 30, 40, 50],
38
+ defaultSort = [],
39
+ defaultVisibility = {},
40
+ }: DynamicTableProps<TData>) {
41
+ const [sorting, setSorting] = React.useState<SortingState>(defaultSort);
42
+ const [columnVisibility, setColumnVisibility] =
43
+ React.useState<VisibilityState>(defaultVisibility);
44
+ const [{ pageIndex, pageSize }, setPagination] =
45
+ React.useState<PaginationState>({
46
+ pageIndex: 0,
47
+ pageSize: rowsPerPage[0],
48
+ });
49
+
50
+ const pagination = React.useMemo(
51
+ () => ({
52
+ pageIndex,
53
+ pageSize,
54
+ }),
55
+ [pageIndex, pageSize]
56
+ );
57
+
58
+ const table = useReactTable({
59
+ data,
60
+ columns,
61
+ getCoreRowModel: getCoreRowModel(),
62
+ onSortingChange: setSorting,
63
+ getSortedRowModel: showSorting ? getSortedRowModel() : undefined,
64
+ getPaginationRowModel: getPaginationRowModel(),
65
+ onColumnVisibilityChange: setColumnVisibility,
66
+ onPaginationChange: setPagination,
67
+ manualPagination: false,
68
+ pageCount: Math.ceil(data.length / pageSize),
69
+ state: {
70
+ sorting,
71
+ columnVisibility,
72
+ pagination,
73
+ },
74
+ enableSorting: showSorting,
75
+ });
76
+
77
+ return (
78
+ <div className="space-y-4">
79
+ {(toolbar || filters || showColumnVisibility) && (
80
+ <DataTableToolbar
81
+ table={table}
82
+ filters={filters}
83
+ showColumnVisibility={showColumnVisibility}
84
+ />
85
+ )}
86
+ <div className={cn("rounded-md border", className)}>
87
+ <Table>
88
+ <TableHeader>
89
+ {table.getHeaderGroups().map((headerGroup) => (
90
+ <TableRow key={headerGroup.id}>
91
+ {headerGroup.headers.map((header) => (
92
+ <TableHead key={header.id}>
93
+ {header.isPlaceholder ? null : (
94
+ <DataTableColumnHeader
95
+ column={header.column}
96
+ title={header.column.columnDef.header as string}
97
+ />
98
+ )}
99
+ </TableHead>
100
+ ))}
101
+ </TableRow>
102
+ ))}
103
+ </TableHeader>
104
+ <TableBody>
105
+ {table.getRowModel().rows?.length ? (
106
+ table.getRowModel().rows.map((row) => (
107
+ <TableRow key={row.id}>
108
+ {row.getVisibleCells().map((cell) => (
109
+ <TableCell key={cell.id}>
110
+ {flexRender(
111
+ cell.column.columnDef.cell,
112
+ cell.getContext()
113
+ )}
114
+ </TableCell>
115
+ ))}
116
+ </TableRow>
117
+ ))
118
+ ) : (
119
+ <TableRow>
120
+ <TableCell
121
+ colSpan={columns.length}
122
+ className="h-24 text-center"
123
+ >
124
+ Nenhum resultado encontrado.
125
+ </TableCell>
126
+ </TableRow>
127
+ )}
128
+ </TableBody>
129
+ </Table>
130
+ </div>
131
+ {showPagination && (
132
+ <DataTablePagination table={table} pageSizeOptions={rowsPerPage} />
133
+ )}
134
+ </div>
135
+ );
136
+ }
@@ -0,0 +1,43 @@
1
+ import {
2
+ ColumnDef,
3
+ SortingState,
4
+ Table,
5
+ VisibilityState,
6
+ } from "@tanstack/react-table";
7
+
8
+ export interface DynamicTableProps<TData> {
9
+ data: TData[];
10
+ columns: ColumnDef<TData>[];
11
+ className?: string;
12
+ toolbar?: React.ReactNode;
13
+ pagination?: boolean;
14
+ sorting?: boolean;
15
+ columnVisibility?: boolean;
16
+ filters?: React.ReactNode;
17
+ rowsPerPage?: number[];
18
+ defaultSort?: SortingState;
19
+ defaultVisibility?: VisibilityState;
20
+ }
21
+
22
+ export interface DataTableToolbarProps<TData> {
23
+ table: Table<TData>;
24
+ filters?: React.ReactNode;
25
+ showColumnVisibility?: boolean;
26
+ }
27
+
28
+ export interface DynamicTablePaginationProps<TData> {
29
+ table: Table<TData>;
30
+ pageSizeOptions?: number[];
31
+ }
32
+
33
+ export interface DynamicTableToolbarProps<TData> {
34
+ table: Table<TData>;
35
+ filters?: React.ReactNode;
36
+ showColumnVisibility?: boolean;
37
+ }
38
+
39
+ export interface DynamicTablePaginationProps<TData> {
40
+ table: Table<TData>;
41
+ filters?: React.ReactNode;
42
+ showColumnVisibility?: boolean;
43
+ }
@@ -0,0 +1,71 @@
1
+ import {
2
+ ArrowDownIcon,
3
+ ArrowUpIcon,
4
+ CaretSortIcon,
5
+ EyeNoneIcon,
6
+ } from "@radix-ui/react-icons";
7
+ import { Column } from "@tanstack/react-table";
8
+
9
+ import {
10
+ DropdownMenu,
11
+ DropdownMenuContent,
12
+ DropdownMenuItem,
13
+ DropdownMenuSeparator,
14
+ DropdownMenuTrigger,
15
+ } from "../../../../../components/ui/dropdown-menu";
16
+ import { Button } from "../../../../../components/ui/button";
17
+ import { cn } from "../../../../../lib/utils";
18
+
19
+ interface DataTableColumnHeaderProps<TData, TValue>
20
+ extends React.HTMLAttributes<HTMLDivElement> {
21
+ column: Column<TData, TValue>;
22
+ title: string;
23
+ }
24
+
25
+ export function DataTableColumnHeader<TData, TValue>({
26
+ column,
27
+ title,
28
+ className,
29
+ }: DataTableColumnHeaderProps<TData, TValue>) {
30
+ if (!column.getCanSort()) {
31
+ return <div className={cn(className)}>{title}</div>;
32
+ }
33
+
34
+ return (
35
+ <div className={cn("flex items-center space-x-2", className)}>
36
+ <DropdownMenu>
37
+ <DropdownMenuTrigger asChild>
38
+ <Button
39
+ variant="ghost"
40
+ size="sm"
41
+ className="-ml-3 h-8 data-[state=open]:bg-accent"
42
+ >
43
+ <span>{title}</span>
44
+ {column.getIsSorted() === "desc" ? (
45
+ <ArrowDownIcon className="ml-2 h-4 w-4" />
46
+ ) : column.getIsSorted() === "asc" ? (
47
+ <ArrowUpIcon className="ml-2 h-4 w-4" />
48
+ ) : (
49
+ <CaretSortIcon className="ml-2 h-4 w-4" />
50
+ )}
51
+ </Button>
52
+ </DropdownMenuTrigger>
53
+ <DropdownMenuContent align="start">
54
+ <DropdownMenuItem onClick={() => column.toggleSorting(false)}>
55
+ <ArrowUpIcon className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
56
+ Asc
57
+ </DropdownMenuItem>
58
+ <DropdownMenuItem onClick={() => column.toggleSorting(true)}>
59
+ <ArrowDownIcon className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
60
+ Desc
61
+ </DropdownMenuItem>
62
+ <DropdownMenuSeparator />
63
+ <DropdownMenuItem onClick={() => column.toggleVisibility(false)}>
64
+ <EyeNoneIcon className="mr-2 h-3.5 w-3.5 text-muted-foreground/70" />
65
+ Hide
66
+ </DropdownMenuItem>
67
+ </DropdownMenuContent>
68
+ </DropdownMenu>
69
+ </div>
70
+ );
71
+ }
@@ -0,0 +1,147 @@
1
+ import * as React from "react";
2
+ import { CheckIcon, PlusCircledIcon } from "@radix-ui/react-icons";
3
+ import { Column } from "@tanstack/react-table";
4
+
5
+ import {
6
+ Command,
7
+ CommandEmpty,
8
+ CommandGroup,
9
+ CommandInput,
10
+ CommandItem,
11
+ CommandList,
12
+ CommandSeparator,
13
+ } from "../../../../ui/command";
14
+ import {
15
+ Popover,
16
+ PopoverContent,
17
+ PopoverTrigger,
18
+ } from "../../../../ui/popover";
19
+ import { Separator } from "../../../../ui/separator";
20
+ import { Button } from "../../../../ui/button";
21
+ import { Badge } from "../../../../ui/badge";
22
+ import { cn } from "../../../../../lib/utils";
23
+
24
+ interface DataTableFacetedFilterProps<TData, TValue> {
25
+ column?: Column<TData, TValue>;
26
+ title?: string;
27
+ options: {
28
+ label: string;
29
+ value: string;
30
+ icon?: React.ComponentType<{ className?: string }>;
31
+ }[];
32
+ }
33
+
34
+ export function DataTableFacetedFilter<TData, TValue>({
35
+ column,
36
+ title,
37
+ options,
38
+ }: DataTableFacetedFilterProps<TData, TValue>) {
39
+ const facets = column?.getFacetedUniqueValues();
40
+ const selectedValues = new Set(column?.getFilterValue() as string[]);
41
+
42
+ return (
43
+ <Popover>
44
+ <PopoverTrigger asChild>
45
+ <Button variant="outline" size="sm" className="h-8 border-dashed">
46
+ <PlusCircledIcon className="mr-2 h-4 w-4" />
47
+ {title}
48
+ {selectedValues?.size > 0 && (
49
+ <>
50
+ <Separator orientation="vertical" className="mx-2 h-4" />
51
+ <Badge
52
+ variant="secondary"
53
+ className="rounded-sm px-1 font-normal lg:hidden"
54
+ >
55
+ {selectedValues.size}
56
+ </Badge>
57
+ <div className="hidden space-x-1 lg:flex">
58
+ {selectedValues.size > 2 ? (
59
+ <Badge
60
+ variant="secondary"
61
+ className="rounded-sm px-1 font-normal"
62
+ >
63
+ {selectedValues.size} selected
64
+ </Badge>
65
+ ) : (
66
+ options
67
+ .filter((option) => selectedValues.has(option.value))
68
+ .map((option) => (
69
+ <Badge
70
+ variant="secondary"
71
+ key={option.value}
72
+ className="rounded-sm px-1 font-normal"
73
+ >
74
+ {option.label}
75
+ </Badge>
76
+ ))
77
+ )}
78
+ </div>
79
+ </>
80
+ )}
81
+ </Button>
82
+ </PopoverTrigger>
83
+ <PopoverContent className="w-[200px] p-0" align="start">
84
+ <Command>
85
+ <CommandInput placeholder={title} />
86
+ <CommandList>
87
+ <CommandEmpty>No results found.</CommandEmpty>
88
+ <CommandGroup>
89
+ {options.map((option) => {
90
+ const isSelected = selectedValues.has(option.value);
91
+ return (
92
+ <CommandItem
93
+ key={option.value}
94
+ onSelect={() => {
95
+ if (isSelected) {
96
+ selectedValues.delete(option.value);
97
+ } else {
98
+ selectedValues.add(option.value);
99
+ }
100
+ const filterValues = Array.from(selectedValues);
101
+ column?.setFilterValue(
102
+ filterValues.length ? filterValues : undefined
103
+ );
104
+ }}
105
+ >
106
+ <div
107
+ className={cn(
108
+ "mr-2 flex h-4 w-4 items-center justify-center rounded-sm border border-primary",
109
+ isSelected
110
+ ? "bg-primary text-primary-foreground"
111
+ : "opacity-50 [&_svg]:invisible"
112
+ )}
113
+ >
114
+ <CheckIcon className={cn("h-4 w-4")} />
115
+ </div>
116
+ {option.icon && (
117
+ <option.icon className="mr-2 h-4 w-4 text-muted-foreground" />
118
+ )}
119
+ <span>{option.label}</span>
120
+ {facets?.get(option.value) && (
121
+ <span className="ml-auto flex h-4 w-4 items-center justify-center font-mono text-xs">
122
+ {facets.get(option.value)}
123
+ </span>
124
+ )}
125
+ </CommandItem>
126
+ );
127
+ })}
128
+ </CommandGroup>
129
+ {selectedValues.size > 0 && (
130
+ <>
131
+ <CommandSeparator />
132
+ <CommandGroup>
133
+ <CommandItem
134
+ onSelect={() => column?.setFilterValue(undefined)}
135
+ className="justify-center text-center"
136
+ >
137
+ Clear filters
138
+ </CommandItem>
139
+ </CommandGroup>
140
+ </>
141
+ )}
142
+ </CommandList>
143
+ </Command>
144
+ </PopoverContent>
145
+ </Popover>
146
+ );
147
+ }
@@ -0,0 +1,97 @@
1
+ import { Table } from "@tanstack/react-table";
2
+ import {
3
+ ChevronLeft,
4
+ ChevronRight,
5
+ ChevronsLeft,
6
+ ChevronsRight,
7
+ } from "lucide-react";
8
+
9
+ import { Button } from "../../../../ui/button";
10
+ import {
11
+ Select,
12
+ SelectContent,
13
+ SelectItem,
14
+ SelectTrigger,
15
+ SelectValue,
16
+ } from "../../../../ui/select";
17
+
18
+ interface DataTablePaginationProps<TData> {
19
+ table: Table<TData>;
20
+ }
21
+
22
+ export function DataTablePagination<TData>({
23
+ table,
24
+ }: DataTablePaginationProps<TData>) {
25
+ return (
26
+ <div className="flex items-center justify-between px-2">
27
+ <div className="flex-1 text-sm text-muted-foreground">
28
+ {/* {table.getFilteredSelectedRowModel().rows.length} of{" "}
29
+ {table.getFilteredRowModel().rows.length} row(s) selected. */}
30
+ </div>
31
+ <div className="flex items-center space-x-6 lg:space-x-8">
32
+ <div className="flex items-center space-x-2">
33
+ <p className="text-sm font-medium">Linhas por página</p>
34
+ <Select
35
+ value={`${table.getState().pagination.pageSize}`}
36
+ onValueChange={(value) => {
37
+ table.setPageSize(Number(value));
38
+ }}
39
+ >
40
+ <SelectTrigger className="h-8 w-[70px]">
41
+ <SelectValue placeholder={table.getState().pagination.pageSize} />
42
+ </SelectTrigger>
43
+ <SelectContent side="top">
44
+ {[10, 20, 30, 40, 50].map((pageSize) => (
45
+ <SelectItem key={pageSize} value={`${pageSize}`}>
46
+ {pageSize}
47
+ </SelectItem>
48
+ ))}
49
+ </SelectContent>
50
+ </Select>
51
+ </div>
52
+ <div className="flex w-[100px] items-center justify-center text-sm font-medium">
53
+ Página {table.getState().pagination.pageIndex + 1} de{" "}
54
+ {table.getPageCount()}
55
+ </div>
56
+ <div className="flex items-center space-x-2">
57
+ <Button
58
+ variant="outline"
59
+ className="hidden h-8 w-8 p-0 lg:flex"
60
+ onClick={() => table.setPageIndex(0)}
61
+ disabled={!table.getCanPreviousPage()}
62
+ >
63
+ <span className="sr-only">Go to first page</span>
64
+ <ChevronsLeft />
65
+ </Button>
66
+ <Button
67
+ variant="outline"
68
+ className="h-8 w-8 p-0"
69
+ onClick={() => table.previousPage()}
70
+ disabled={!table.getCanPreviousPage()}
71
+ >
72
+ <span className="sr-only">Go to previous page</span>
73
+ <ChevronLeft />
74
+ </Button>
75
+ <Button
76
+ variant="outline"
77
+ className="h-8 w-8 p-0"
78
+ onClick={() => table.nextPage()}
79
+ disabled={!table.getCanNextPage()}
80
+ >
81
+ <span className="sr-only">Go to next page</span>
82
+ <ChevronRight />
83
+ </Button>
84
+ <Button
85
+ variant="outline"
86
+ className="hidden h-8 w-8 p-0 lg:flex"
87
+ onClick={() => table.setPageIndex(table.getPageCount() - 1)}
88
+ disabled={!table.getCanNextPage()}
89
+ >
90
+ <span className="sr-only">Go to last page</span>
91
+ <ChevronsRight />
92
+ </Button>
93
+ </div>
94
+ </div>
95
+ </div>
96
+ );
97
+ }