From 44f293b7661f1fd3c6f10e13ae56ab2067763afe Mon Sep 17 00:00:00 2001 From: Simon C Date: Thu, 4 Apr 2024 23:11:29 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20Ajout=20de=20la=20vue=20des=20t=C3=A2ch?= =?UTF-8?q?es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 105 ++- package.json | 7 +- src/blocks/Dashboard05.tsx | 6 +- src/blocks/tasks/TasksBlocks.tsx | 35 + src/blocks/tasks/components/columns.tsx | 123 +++ .../components/data-table-column-header.tsx | 71 ++ .../components/data-table-faceted-filter.tsx | 147 ++++ .../components/data-table-pagination.tsx | 97 +++ .../components/data-table-row-actions.tsx | 69 ++ .../tasks/components/data-table-toolbar.tsx | 61 ++ .../components/data-table-view-options.tsx | 59 ++ src/blocks/tasks/components/data-table.tsx | 126 ++++ src/blocks/tasks/components/user-nav.tsx | 62 ++ src/blocks/tasks/data/data.tsx | 71 ++ src/blocks/tasks/data/schema.ts | 13 + src/blocks/tasks/data/seed.ts | 20 + src/blocks/tasks/data/tasks.json | 702 ++++++++++++++++++ src/components/ui/checkbox.tsx | 30 + src/components/ui/command.tsx | 155 ++++ src/components/ui/dialog.tsx | 122 +++ src/main.tsx | 5 + vite.config.ts | 4 +- 22 files changed, 2084 insertions(+), 6 deletions(-) create mode 100644 src/blocks/tasks/TasksBlocks.tsx create mode 100644 src/blocks/tasks/components/columns.tsx create mode 100644 src/blocks/tasks/components/data-table-column-header.tsx create mode 100644 src/blocks/tasks/components/data-table-faceted-filter.tsx create mode 100644 src/blocks/tasks/components/data-table-pagination.tsx create mode 100644 src/blocks/tasks/components/data-table-row-actions.tsx create mode 100644 src/blocks/tasks/components/data-table-toolbar.tsx create mode 100644 src/blocks/tasks/components/data-table-view-options.tsx create mode 100644 src/blocks/tasks/components/data-table.tsx create mode 100644 src/blocks/tasks/components/user-nav.tsx create mode 100644 src/blocks/tasks/data/data.tsx create mode 100644 src/blocks/tasks/data/schema.ts create mode 100644 src/blocks/tasks/data/seed.ts create mode 100644 src/blocks/tasks/data/tasks.json create mode 100644 src/components/ui/checkbox.tsx create mode 100644 src/components/ui/command.tsx create mode 100644 src/components/ui/dialog.tsx diff --git a/package-lock.json b/package-lock.json index df77f62..fd34bef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.0", "dependencies": { "@radix-ui/react-avatar": "^1.0.4", + "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-icons": "^1.3.0", @@ -22,8 +23,10 @@ "@radix-ui/react-switch": "^1.0.3", "@radix-ui/react-tabs": "^1.0.4", "@radix-ui/react-tooltip": "^1.0.7", + "@tanstack/react-table": "^8.15.3", "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", + "cmdk": "^1.0.0", "date-fns": "^3.6.0", "jotai": "^2.7.2", "lucide-react": "^0.364.0", @@ -33,9 +36,11 @@ "react-resizable-panels": "^2.0.16", "react-router-dom": "^6.22.3", "tailwind-merge": "^2.2.2", - "tailwindcss-animate": "^1.0.7" + "tailwindcss-animate": "^1.0.7", + "zod": "^3.22.4" }, "devDependencies": { + "@faker-js/faker": "^8.4.1", "@types/node": "^20.12.4", "@types/react": "^18.2.66", "@types/react-dom": "^18.2.22", @@ -513,6 +518,22 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@faker-js/faker": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-8.4.1.tgz", + "integrity": "sha512-XQ3cU+Q8Uqmrbf2e0cIC/QN43sTBSC8KF12u29Mb47tWrt2hAgBXSgpZMj4Ao8Uk0iJcU99QsOCaIL8934obCg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/fakerjs" + } + ], + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0", + "npm": ">=6.14.13" + } + }, "node_modules/@floating-ui/core": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz", @@ -775,6 +796,36 @@ } } }, + "node_modules/@radix-ui/react-checkbox": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.0.4.tgz", + "integrity": "sha512-CBuGQa52aAYnADZVt/KBQzXrwx6TqnlwtcIPGtVt5JkkzQwMOLJjPukimhfKEr4GQNd43C+djUh5Ikopj8pSLg==", + "dependencies": { + "@babel/runtime": "^7.13.10", + "@radix-ui/primitive": "1.0.1", + "@radix-ui/react-compose-refs": "1.0.1", + "@radix-ui/react-context": "1.0.1", + "@radix-ui/react-presence": "1.0.1", + "@radix-ui/react-primitive": "1.0.3", + "@radix-ui/react-use-controllable-state": "1.0.1", + "@radix-ui/react-use-previous": "1.0.1", + "@radix-ui/react-use-size": "1.0.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0", + "react-dom": "^16.8 || ^17.0 || ^18.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-collection": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.0.3.tgz", @@ -1661,6 +1712,37 @@ "linux" ] }, + "node_modules/@tanstack/react-table": { + "version": "8.15.3", + "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.15.3.tgz", + "integrity": "sha512-aocQ4WpWiAh7R+yxNp+DGQYXeVACh5lv2kk96DjYgFiHDCB0cOFoYMT/pM6eDOzeMXR9AvPoLeumTgq8/0qX+w==", + "dependencies": { + "@tanstack/table-core": "8.15.3" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/@tanstack/table-core": { + "version": "8.15.3", + "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.15.3.tgz", + "integrity": "sha512-wOgV0HfEvuMOv8RlqdR9MdNNqq0uyvQtP39QOvGlggHvIObOE4exS+D5LGO8LZ3LUXxId2IlUKcHDHaGujWhUg==", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@types/babel__core": { "version": "7.20.5", "dev": true, @@ -2244,6 +2326,19 @@ "node": ">=6" } }, + "node_modules/cmdk": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.0.0.tgz", + "integrity": "sha512-gDzVf0a09TvoJ5jnuPvygTB77+XdOSwEmJ88L6XPFPlv7T3RxbP9jgenfylrAMD0+Le1aO0nVjQUzl2g+vjz5Q==", + "dependencies": { + "@radix-ui/react-dialog": "1.0.5", + "@radix-ui/react-primitive": "1.0.3" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, "node_modules/color-convert": { "version": "1.9.3", "dev": true, @@ -4588,6 +4683,14 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", + "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/package.json b/package.json index dc86a0b..61074b5 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ }, "dependencies": { "@radix-ui/react-avatar": "^1.0.4", + "@radix-ui/react-checkbox": "^1.0.4", "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-icons": "^1.3.0", @@ -25,8 +26,10 @@ "@radix-ui/react-switch": "^1.0.3", "@radix-ui/react-tabs": "^1.0.4", "@radix-ui/react-tooltip": "^1.0.7", + "@tanstack/react-table": "^8.15.3", "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", + "cmdk": "^1.0.0", "date-fns": "^3.6.0", "jotai": "^2.7.2", "lucide-react": "^0.364.0", @@ -36,9 +39,11 @@ "react-resizable-panels": "^2.0.16", "react-router-dom": "^6.22.3", "tailwind-merge": "^2.2.2", - "tailwindcss-animate": "^1.0.7" + "tailwindcss-animate": "^1.0.7", + "zod": "^3.22.4" }, "devDependencies": { + "@faker-js/faker": "^8.4.1", "@types/node": "^20.12.4", "@types/react": "^18.2.66", "@types/react-dom": "^18.2.22", diff --git a/src/blocks/Dashboard05.tsx b/src/blocks/Dashboard05.tsx index 85b7d61..017ee9c 100644 --- a/src/blocks/Dashboard05.tsx +++ b/src/blocks/Dashboard05.tsx @@ -105,13 +105,13 @@ import { - Orders - + Orders diff --git a/src/blocks/tasks/TasksBlocks.tsx b/src/blocks/tasks/TasksBlocks.tsx new file mode 100644 index 0000000..89f5a4e --- /dev/null +++ b/src/blocks/tasks/TasksBlocks.tsx @@ -0,0 +1,35 @@ +import { z } from "zod" + +import { columns } from "./components/columns.tsx" +import { DataTable } from "./components/data-table.tsx" +import { UserNav } from "./components/user-nav.tsx" +import { taskSchema } from "./data/schema" + +import tasks from "./data/tasks.json" + +// Simulate a database read for tasks. +function getTasks() { + return z.array(taskSchema).parse(tasks) +} + +export default function TaskPage() { + + return ( + <> +
+
+
+

Welcome back!

+

+ Here's a list of your tasks for this month! +

+
+
+ +
+
+ +
+ + ) +} diff --git a/src/blocks/tasks/components/columns.tsx b/src/blocks/tasks/components/columns.tsx new file mode 100644 index 0000000..351ea93 --- /dev/null +++ b/src/blocks/tasks/components/columns.tsx @@ -0,0 +1,123 @@ +"use client" + +import { ColumnDef } from "@tanstack/react-table" + +import { Badge } from "@/components/ui/badge" +import { Checkbox } from "@/components/ui/checkbox" + +import { labels, priorities, statuses } from "../data/data" +import { Task } from "../data/schema" +import { DataTableColumnHeader } from "./data-table-column-header.tsx" +import { DataTableRowActions } from "./data-table-row-actions.tsx" + +export const columns: ColumnDef[] = [ + { + id: "select", + header: ({ table }) => ( + table.toggleAllPageRowsSelected(!!value)} + aria-label="Select all" + className="translate-y-[2px]" + /> + ), + cell: ({ row }) => ( + row.toggleSelected(!!value)} + aria-label="Select row" + className="translate-y-[2px]" + /> + ), + enableSorting: false, + enableHiding: false, + }, + { + accessorKey: "id", + header: ({ column }) => ( + + ), + cell: ({ row }) =>
{row.getValue("id")}
, + enableSorting: false, + enableHiding: false, + }, + { + accessorKey: "title", + header: ({ column }) => ( + + ), + cell: ({ row }) => { + const label = labels.find((label) => label.value === row.original.label) + + return ( +
+ {label && {label.label}} + + {row.getValue("title")} + +
+ ) + }, + }, + { + accessorKey: "status", + header: ({ column }) => ( + + ), + cell: ({ row }) => { + const status = statuses.find( + (status) => status.value === row.getValue("status") + ) + + if (!status) { + return null + } + + return ( +
+ {status.icon && ( + + )} + {status.label} +
+ ) + }, + filterFn: (row, id, value) => { + return value.includes(row.getValue(id)) + }, + }, + { + accessorKey: "priority", + header: ({ column }) => ( + + ), + cell: ({ row }) => { + const priority = priorities.find( + (priority) => priority.value === row.getValue("priority") + ) + + if (!priority) { + return null + } + + return ( +
+ {priority.icon && ( + + )} + {priority.label} +
+ ) + }, + filterFn: (row, id, value) => { + return value.includes(row.getValue(id)) + }, + }, + { + id: "actions", + cell: ({ row }) => , + }, +] \ No newline at end of file diff --git a/src/blocks/tasks/components/data-table-column-header.tsx b/src/blocks/tasks/components/data-table-column-header.tsx new file mode 100644 index 0000000..3f7e73b --- /dev/null +++ b/src/blocks/tasks/components/data-table-column-header.tsx @@ -0,0 +1,71 @@ +import { + ArrowDownIcon, + ArrowUpIcon, + CaretSortIcon, + EyeNoneIcon, + } from "@radix-ui/react-icons" + import { Column } from "@tanstack/react-table" + + import { cn } from "@/lib/utils" + import { Button } from "@/components/ui/button" + import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, + } from "@/components/ui/dropdown-menu" + + interface DataTableColumnHeaderProps + extends React.HTMLAttributes { + column: Column + title: string + } + + export function DataTableColumnHeader({ + column, + title, + className, + }: DataTableColumnHeaderProps) { + if (!column.getCanSort()) { + return
{title}
+ } + + return ( +
+ + + + + + column.toggleSorting(false)}> + + Asc + + column.toggleSorting(true)}> + + Desc + + + column.toggleVisibility(false)}> + + Hide + + + +
+ ) + } \ No newline at end of file diff --git a/src/blocks/tasks/components/data-table-faceted-filter.tsx b/src/blocks/tasks/components/data-table-faceted-filter.tsx new file mode 100644 index 0000000..a5efee2 --- /dev/null +++ b/src/blocks/tasks/components/data-table-faceted-filter.tsx @@ -0,0 +1,147 @@ +import * as React from "react" +import { CheckIcon, PlusCircledIcon } from "@radix-ui/react-icons" +import { Column } from "@tanstack/react-table" + +import { cn } from "@/lib/utils" +import { Badge } from "@/components/ui/badge" +import { Button } from "@/components/ui/button" +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, + CommandSeparator, +} from "@/components/ui/command" +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover" +import { Separator } from "@/components/ui/separator" + +interface DataTableFacetedFilterProps { + column?: Column + title?: string + options: { + label: string + value: string + icon?: React.ComponentType<{ className?: string }> + }[] +} + +export function DataTableFacetedFilter({ + column, + title, + options, +}: DataTableFacetedFilterProps) { + const facets = column?.getFacetedUniqueValues() + const selectedValues = new Set(column?.getFilterValue() as string[]) + + return ( + + + + + + + + + No results found. + + {options.map((option) => { + const isSelected = selectedValues.has(option.value) + return ( + { + if (isSelected) { + selectedValues.delete(option.value) + } else { + selectedValues.add(option.value) + } + const filterValues = Array.from(selectedValues) + column?.setFilterValue( + filterValues.length ? filterValues : undefined + ) + }} + > +
+ +
+ {option.icon && ( + + )} + {option.label} + {facets?.get(option.value) && ( + + {facets.get(option.value)} + + )} +
+ ) + })} +
+ {selectedValues.size > 0 && ( + <> + + + column?.setFilterValue(undefined)} + className="justify-center text-center" + > + Clear filters + + + + )} +
+
+
+
+ ) +} \ No newline at end of file diff --git a/src/blocks/tasks/components/data-table-pagination.tsx b/src/blocks/tasks/components/data-table-pagination.tsx new file mode 100644 index 0000000..b04f3bd --- /dev/null +++ b/src/blocks/tasks/components/data-table-pagination.tsx @@ -0,0 +1,97 @@ +import { + ChevronLeftIcon, + ChevronRightIcon, + DoubleArrowLeftIcon, + DoubleArrowRightIcon, + } from "@radix-ui/react-icons" + import { Table } from "@tanstack/react-table" + + import { Button } from "@/components/ui/button" + import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, + } from "@/components/ui/select" + + interface DataTablePaginationProps { + table: Table + } + + export function DataTablePagination({ + table, + }: DataTablePaginationProps) { + return ( +
+
+ {table.getFilteredSelectedRowModel().rows.length} of{" "} + {table.getFilteredRowModel().rows.length} row(s) selected. +
+
+
+

Rows per page

+ +
+
+ Page {table.getState().pagination.pageIndex + 1} of{" "} + {table.getPageCount()} +
+
+ + + + +
+
+
+ ) + } \ No newline at end of file diff --git a/src/blocks/tasks/components/data-table-row-actions.tsx b/src/blocks/tasks/components/data-table-row-actions.tsx new file mode 100644 index 0000000..3e62cc1 --- /dev/null +++ b/src/blocks/tasks/components/data-table-row-actions.tsx @@ -0,0 +1,69 @@ +"use client" + +import { DotsHorizontalIcon } from "@radix-ui/react-icons" +import { Row } from "@tanstack/react-table" + +import { Button } from "@/components/ui/button" +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu" + +import { labels } from "../data/data" +import { taskSchema } from "../data/schema" + +interface DataTableRowActionsProps { + row: Row +} + +export function DataTableRowActions({ + row, +}: DataTableRowActionsProps) { + const task = taskSchema.parse(row.original) + + return ( + + + + + + Edit + Make a copy + Favorite + + + Labels + + + {labels.map((label) => ( + + {label.label} + + ))} + + + + + + Delete + ⌘⌫ + + + + ) +} \ No newline at end of file diff --git a/src/blocks/tasks/components/data-table-toolbar.tsx b/src/blocks/tasks/components/data-table-toolbar.tsx new file mode 100644 index 0000000..418cf9d --- /dev/null +++ b/src/blocks/tasks/components/data-table-toolbar.tsx @@ -0,0 +1,61 @@ +"use client" + +import { Cross2Icon } from "@radix-ui/react-icons" +import { Table } from "@tanstack/react-table" + +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { DataTableViewOptions } from "./data-table-view-options" + +import { priorities, statuses } from "../data/data" +import { DataTableFacetedFilter } from "./data-table-faceted-filter" + +interface DataTableToolbarProps { + table: Table +} + +export function DataTableToolbar({ + table, +}: DataTableToolbarProps) { + const isFiltered = table.getState().columnFilters.length > 0 + + return ( +
+
+ + table.getColumn("title")?.setFilterValue(event.target.value) + } + className="h-8 w-[150px] lg:w-[250px]" + /> + {table.getColumn("status") && ( + + )} + {table.getColumn("priority") && ( + + )} + {isFiltered && ( + + )} +
+ +
+ ) +} \ No newline at end of file diff --git a/src/blocks/tasks/components/data-table-view-options.tsx b/src/blocks/tasks/components/data-table-view-options.tsx new file mode 100644 index 0000000..6d56347 --- /dev/null +++ b/src/blocks/tasks/components/data-table-view-options.tsx @@ -0,0 +1,59 @@ +"use client" + +import { DropdownMenuTrigger } from "@radix-ui/react-dropdown-menu" +import { MixerHorizontalIcon } from "@radix-ui/react-icons" +import { Table } from "@tanstack/react-table" + +import { Button } from "@/components/ui/button" +import { + DropdownMenu, + DropdownMenuCheckboxItem, + DropdownMenuContent, + DropdownMenuLabel, + DropdownMenuSeparator, +} from "@/components/ui/dropdown-menu" + +interface DataTableViewOptionsProps { + table: Table +} + +export function DataTableViewOptions({ + table, +}: DataTableViewOptionsProps) { + return ( + + + + + + Toggle columns + + {table + .getAllColumns() + .filter( + (column) => + typeof column.accessorFn !== "undefined" && column.getCanHide() + ) + .map((column) => { + return ( + column.toggleVisibility(!!value)} + > + {column.id} + + ) + })} + + + ) +} \ No newline at end of file diff --git a/src/blocks/tasks/components/data-table.tsx b/src/blocks/tasks/components/data-table.tsx new file mode 100644 index 0000000..d120b56 --- /dev/null +++ b/src/blocks/tasks/components/data-table.tsx @@ -0,0 +1,126 @@ +"use client" + +import * as React from "react" +import { + ColumnDef, + ColumnFiltersState, + SortingState, + VisibilityState, + flexRender, + getCoreRowModel, + getFacetedRowModel, + getFacetedUniqueValues, + getFilteredRowModel, + getPaginationRowModel, + getSortedRowModel, + useReactTable, +} from "@tanstack/react-table" + +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table" + +import { DataTablePagination } from "./data-table-pagination" +import { DataTableToolbar } from "./data-table-toolbar" + +interface DataTableProps { + columns: ColumnDef[] + data: TData[] +} + +export function DataTable({ + columns, + data, +}: DataTableProps) { + const [rowSelection, setRowSelection] = React.useState({}) + const [columnVisibility, setColumnVisibility] = + React.useState({}) + const [columnFilters, setColumnFilters] = React.useState( + [] + ) + const [sorting, setSorting] = React.useState([]) + + const table = useReactTable({ + data, + columns, + state: { + sorting, + columnVisibility, + rowSelection, + columnFilters, + }, + enableRowSelection: true, + onRowSelectionChange: setRowSelection, + onSortingChange: setSorting, + onColumnFiltersChange: setColumnFilters, + onColumnVisibilityChange: setColumnVisibility, + getCoreRowModel: getCoreRowModel(), + getFilteredRowModel: getFilteredRowModel(), + getPaginationRowModel: getPaginationRowModel(), + getSortedRowModel: getSortedRowModel(), + getFacetedRowModel: getFacetedRowModel(), + getFacetedUniqueValues: getFacetedUniqueValues(), + }) + + return ( +
+ +
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext() + )} + + ) + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} + + ))} + + )) + ) : ( + + + No results. + + + )} + +
+
+ +
+ ) +} \ No newline at end of file diff --git a/src/blocks/tasks/components/user-nav.tsx b/src/blocks/tasks/components/user-nav.tsx new file mode 100644 index 0000000..088cfc9 --- /dev/null +++ b/src/blocks/tasks/components/user-nav.tsx @@ -0,0 +1,62 @@ +import { + Avatar, + AvatarFallback, + AvatarImage, + } from "@/components/ui/avatar" + import { Button } from "@/components/ui/button" + import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuTrigger, + } from "@/components/ui/dropdown-menu" + + export function UserNav() { + return ( + + + + + + +
+

shadcn

+

+ m@example.com +

+
+
+ + + + Profile + ⇧⌘P + + + Billing + ⌘B + + + Settings + ⌘S + + New Team + + + + Log out + ⇧⌘Q + +
+
+ ) + } \ No newline at end of file diff --git a/src/blocks/tasks/data/data.tsx b/src/blocks/tasks/data/data.tsx new file mode 100644 index 0000000..03743ba --- /dev/null +++ b/src/blocks/tasks/data/data.tsx @@ -0,0 +1,71 @@ +import { + ArrowDownIcon, + ArrowRightIcon, + ArrowUpIcon, + CheckCircledIcon, + CircleIcon, + CrossCircledIcon, + QuestionMarkCircledIcon, + StopwatchIcon, + } from "@radix-ui/react-icons" + + export const labels = [ + { + value: "bug", + label: "Bug", + }, + { + value: "feature", + label: "Feature", + }, + { + value: "documentation", + label: "Documentation", + }, + ] + + export const statuses = [ + { + value: "backlog", + label: "Backlog", + icon: QuestionMarkCircledIcon, + }, + { + value: "todo", + label: "Todo", + icon: CircleIcon, + }, + { + value: "in progress", + label: "In Progress", + icon: StopwatchIcon, + }, + { + value: "done", + label: "Done", + icon: CheckCircledIcon, + }, + { + value: "canceled", + label: "Canceled", + icon: CrossCircledIcon, + }, + ] + + export const priorities = [ + { + label: "Low", + value: "low", + icon: ArrowDownIcon, + }, + { + label: "Medium", + value: "medium", + icon: ArrowRightIcon, + }, + { + label: "High", + value: "high", + icon: ArrowUpIcon, + }, + ] \ No newline at end of file diff --git a/src/blocks/tasks/data/schema.ts b/src/blocks/tasks/data/schema.ts new file mode 100644 index 0000000..72000c0 --- /dev/null +++ b/src/blocks/tasks/data/schema.ts @@ -0,0 +1,13 @@ +import { z } from "zod" + +// We're keeping a simple non-relational schema here. +// IRL, you will have a schema for your data models. +export const taskSchema = z.object({ + id: z.string(), + title: z.string(), + status: z.string(), + label: z.string(), + priority: z.string(), +}) + +export type Task = z.infer diff --git a/src/blocks/tasks/data/seed.ts b/src/blocks/tasks/data/seed.ts new file mode 100644 index 0000000..11697fe --- /dev/null +++ b/src/blocks/tasks/data/seed.ts @@ -0,0 +1,20 @@ +import fs from "fs" +import path from "path" +import { faker } from "@faker-js/faker" + +import { labels, priorities, statuses } from "./data" + +const tasks = Array.from({ length: 100 }, () => ({ + id: `TASK-${faker.number.int({ min: 1000, max: 9999 })}`, + title: faker.hacker.phrase().replace(/^./, (letter) => letter.toUpperCase()), + status: faker.helpers.arrayElement(statuses).value, + label: faker.helpers.arrayElement(labels).value, + priority: faker.helpers.arrayElement(priorities).value, +})) + +fs.writeFileSync( + path.join(__dirname, "tasks.json"), + JSON.stringify(tasks, null, 2) +) + +console.log("✅ Tasks data generated.") \ No newline at end of file diff --git a/src/blocks/tasks/data/tasks.json b/src/blocks/tasks/data/tasks.json new file mode 100644 index 0000000..3f3b11b --- /dev/null +++ b/src/blocks/tasks/data/tasks.json @@ -0,0 +1,702 @@ +[ + { + "id": "TASK-8782", + "title": "You can't compress the program without quantifying the open-source SSD pixel!", + "status": "in progress", + "label": "documentation", + "priority": "medium" + }, + { + "id": "TASK-7878", + "title": "Try to calculate the EXE feed, maybe it will index the multi-byte pixel!", + "status": "backlog", + "label": "documentation", + "priority": "medium" + }, + { + "id": "TASK-7839", + "title": "We need to bypass the neural TCP card!", + "status": "todo", + "label": "bug", + "priority": "high" + }, + { + "id": "TASK-5562", + "title": "The SAS interface is down, bypass the open-source pixel so we can back up the PNG bandwidth!", + "status": "backlog", + "label": "feature", + "priority": "medium" + }, + { + "id": "TASK-8686", + "title": "I'll parse the wireless SSL protocol, that should driver the API panel!", + "status": "canceled", + "label": "feature", + "priority": "medium" + }, + { + "id": "TASK-1280", + "title": "Use the digital TLS panel, then you can transmit the haptic system!", + "status": "done", + "label": "bug", + "priority": "high" + }, + { + "id": "TASK-7262", + "title": "The UTF8 application is down, parse the neural bandwidth so we can back up the PNG firewall!", + "status": "done", + "label": "feature", + "priority": "high" + }, + { + "id": "TASK-1138", + "title": "Generating the driver won't do anything, we need to quantify the 1080p SMTP bandwidth!", + "status": "in progress", + "label": "feature", + "priority": "medium" + }, + { + "id": "TASK-7184", + "title": "We need to program the back-end THX pixel!", + "status": "todo", + "label": "feature", + "priority": "low" + }, + { + "id": "TASK-5160", + "title": "Calculating the bus won't do anything, we need to navigate the back-end JSON protocol!", + "status": "in progress", + "label": "documentation", + "priority": "high" + }, + { + "id": "TASK-5618", + "title": "Generating the driver won't do anything, we need to index the online SSL application!", + "status": "done", + "label": "documentation", + "priority": "medium" + }, + { + "id": "TASK-6699", + "title": "I'll transmit the wireless JBOD capacitor, that should hard drive the SSD feed!", + "status": "backlog", + "label": "documentation", + "priority": "medium" + }, + { + "id": "TASK-2858", + "title": "We need to override the online UDP bus!", + "status": "backlog", + "label": "bug", + "priority": "medium" + }, + { + "id": "TASK-9864", + "title": "I'll reboot the 1080p FTP panel, that should matrix the HEX hard drive!", + "status": "done", + "label": "bug", + "priority": "high" + }, + { + "id": "TASK-8404", + "title": "We need to generate the virtual HEX alarm!", + "status": "in progress", + "label": "bug", + "priority": "low" + }, + { + "id": "TASK-5365", + "title": "Backing up the pixel won't do anything, we need to transmit the primary IB array!", + "status": "in progress", + "label": "documentation", + "priority": "low" + }, + { + "id": "TASK-1780", + "title": "The CSS feed is down, index the bluetooth transmitter so we can compress the CLI protocol!", + "status": "todo", + "label": "documentation", + "priority": "high" + }, + { + "id": "TASK-6938", + "title": "Use the redundant SCSI application, then you can hack the optical alarm!", + "status": "todo", + "label": "documentation", + "priority": "high" + }, + { + "id": "TASK-9885", + "title": "We need to compress the auxiliary VGA driver!", + "status": "backlog", + "label": "bug", + "priority": "high" + }, + { + "id": "TASK-3216", + "title": "Transmitting the transmitter won't do anything, we need to compress the virtual HDD sensor!", + "status": "backlog", + "label": "documentation", + "priority": "medium" + }, + { + "id": "TASK-9285", + "title": "The IP monitor is down, copy the haptic alarm so we can generate the HTTP transmitter!", + "status": "todo", + "label": "bug", + "priority": "high" + }, + { + "id": "TASK-1024", + "title": "Overriding the microchip won't do anything, we need to transmit the digital OCR transmitter!", + "status": "in progress", + "label": "documentation", + "priority": "low" + }, + { + "id": "TASK-7068", + "title": "You can't generate the capacitor without indexing the wireless HEX pixel!", + "status": "canceled", + "label": "bug", + "priority": "low" + }, + { + "id": "TASK-6502", + "title": "Navigating the microchip won't do anything, we need to bypass the back-end SQL bus!", + "status": "todo", + "label": "bug", + "priority": "high" + }, + { + "id": "TASK-5326", + "title": "We need to hack the redundant UTF8 transmitter!", + "status": "todo", + "label": "bug", + "priority": "low" + }, + { + "id": "TASK-6274", + "title": "Use the virtual PCI circuit, then you can parse the bluetooth alarm!", + "status": "canceled", + "label": "documentation", + "priority": "low" + }, + { + "id": "TASK-1571", + "title": "I'll input the neural DRAM circuit, that should protocol the SMTP interface!", + "status": "in progress", + "label": "feature", + "priority": "medium" + }, + { + "id": "TASK-9518", + "title": "Compressing the interface won't do anything, we need to compress the online SDD matrix!", + "status": "canceled", + "label": "documentation", + "priority": "medium" + }, + { + "id": "TASK-5581", + "title": "I'll synthesize the digital COM pixel, that should transmitter the UTF8 protocol!", + "status": "backlog", + "label": "documentation", + "priority": "high" + }, + { + "id": "TASK-2197", + "title": "Parsing the feed won't do anything, we need to copy the bluetooth DRAM bus!", + "status": "todo", + "label": "documentation", + "priority": "low" + }, + { + "id": "TASK-8484", + "title": "We need to parse the solid state UDP firewall!", + "status": "in progress", + "label": "bug", + "priority": "low" + }, + { + "id": "TASK-9892", + "title": "If we back up the application, we can get to the UDP application through the multi-byte THX capacitor!", + "status": "done", + "label": "documentation", + "priority": "high" + }, + { + "id": "TASK-9616", + "title": "We need to synthesize the cross-platform ASCII pixel!", + "status": "in progress", + "label": "feature", + "priority": "medium" + }, + { + "id": "TASK-9744", + "title": "Use the back-end IP card, then you can input the solid state hard drive!", + "status": "done", + "label": "documentation", + "priority": "low" + }, + { + "id": "TASK-1376", + "title": "Generating the alarm won't do anything, we need to generate the mobile IP capacitor!", + "status": "backlog", + "label": "documentation", + "priority": "low" + }, + { + "id": "TASK-7382", + "title": "If we back up the firewall, we can get to the RAM alarm through the primary UTF8 pixel!", + "status": "todo", + "label": "feature", + "priority": "low" + }, + { + "id": "TASK-2290", + "title": "I'll compress the virtual JSON panel, that should application the UTF8 bus!", + "status": "canceled", + "label": "documentation", + "priority": "high" + }, + { + "id": "TASK-1533", + "title": "You can't input the firewall without overriding the wireless TCP firewall!", + "status": "done", + "label": "bug", + "priority": "high" + }, + { + "id": "TASK-4920", + "title": "Bypassing the hard drive won't do anything, we need to input the bluetooth JSON program!", + "status": "in progress", + "label": "bug", + "priority": "high" + }, + { + "id": "TASK-5168", + "title": "If we synthesize the bus, we can get to the IP panel through the virtual TLS array!", + "status": "in progress", + "label": "feature", + "priority": "low" + }, + { + "id": "TASK-7103", + "title": "We need to parse the multi-byte EXE bandwidth!", + "status": "canceled", + "label": "feature", + "priority": "low" + }, + { + "id": "TASK-4314", + "title": "If we compress the program, we can get to the XML alarm through the multi-byte COM matrix!", + "status": "in progress", + "label": "bug", + "priority": "high" + }, + { + "id": "TASK-3415", + "title": "Use the cross-platform XML application, then you can quantify the solid state feed!", + "status": "todo", + "label": "feature", + "priority": "high" + }, + { + "id": "TASK-8339", + "title": "Try to calculate the DNS interface, maybe it will input the bluetooth capacitor!", + "status": "in progress", + "label": "feature", + "priority": "low" + }, + { + "id": "TASK-6995", + "title": "Try to hack the XSS bandwidth, maybe it will override the bluetooth matrix!", + "status": "todo", + "label": "feature", + "priority": "high" + }, + { + "id": "TASK-8053", + "title": "If we connect the program, we can get to the UTF8 matrix through the digital UDP protocol!", + "status": "todo", + "label": "feature", + "priority": "medium" + }, + { + "id": "TASK-4336", + "title": "If we synthesize the microchip, we can get to the SAS sensor through the optical UDP program!", + "status": "todo", + "label": "documentation", + "priority": "low" + }, + { + "id": "TASK-8790", + "title": "I'll back up the optical COM alarm, that should alarm the RSS capacitor!", + "status": "done", + "label": "bug", + "priority": "medium" + }, + { + "id": "TASK-8980", + "title": "Try to navigate the SQL transmitter, maybe it will back up the virtual firewall!", + "status": "canceled", + "label": "bug", + "priority": "low" + }, + { + "id": "TASK-7342", + "title": "Use the neural CLI card, then you can parse the online port!", + "status": "backlog", + "label": "documentation", + "priority": "low" + }, + { + "id": "TASK-5608", + "title": "I'll hack the haptic SSL program, that should bus the UDP transmitter!", + "status": "canceled", + "label": "documentation", + "priority": "low" + }, + { + "id": "TASK-1606", + "title": "I'll generate the bluetooth PNG firewall, that should pixel the SSL driver!", + "status": "done", + "label": "feature", + "priority": "medium" + }, + { + "id": "TASK-7872", + "title": "Transmitting the circuit won't do anything, we need to reboot the 1080p RSS monitor!", + "status": "canceled", + "label": "feature", + "priority": "medium" + }, + { + "id": "TASK-4167", + "title": "Use the cross-platform SMS circuit, then you can synthesize the optical feed!", + "status": "canceled", + "label": "bug", + "priority": "medium" + }, + { + "id": "TASK-9581", + "title": "You can't index the port without hacking the cross-platform XSS monitor!", + "status": "backlog", + "label": "documentation", + "priority": "low" + }, + { + "id": "TASK-8806", + "title": "We need to bypass the back-end SSL panel!", + "status": "done", + "label": "bug", + "priority": "medium" + }, + { + "id": "TASK-6542", + "title": "Try to quantify the RSS firewall, maybe it will quantify the open-source system!", + "status": "done", + "label": "feature", + "priority": "low" + }, + { + "id": "TASK-6806", + "title": "The VGA protocol is down, reboot the back-end matrix so we can parse the CSS panel!", + "status": "canceled", + "label": "documentation", + "priority": "low" + }, + { + "id": "TASK-9549", + "title": "You can't bypass the bus without connecting the neural JBOD bus!", + "status": "todo", + "label": "feature", + "priority": "high" + }, + { + "id": "TASK-1075", + "title": "Backing up the driver won't do anything, we need to parse the redundant RAM pixel!", + "status": "done", + "label": "feature", + "priority": "medium" + }, + { + "id": "TASK-1427", + "title": "Use the auxiliary PCI circuit, then you can calculate the cross-platform interface!", + "status": "done", + "label": "documentation", + "priority": "high" + }, + { + "id": "TASK-1907", + "title": "Hacking the circuit won't do anything, we need to back up the online DRAM system!", + "status": "todo", + "label": "documentation", + "priority": "high" + }, + { + "id": "TASK-4309", + "title": "If we generate the system, we can get to the TCP sensor through the optical GB pixel!", + "status": "backlog", + "label": "bug", + "priority": "medium" + }, + { + "id": "TASK-3973", + "title": "I'll parse the back-end ADP array, that should bandwidth the RSS bandwidth!", + "status": "todo", + "label": "feature", + "priority": "medium" + }, + { + "id": "TASK-7962", + "title": "Use the wireless RAM program, then you can hack the cross-platform feed!", + "status": "canceled", + "label": "bug", + "priority": "low" + }, + { + "id": "TASK-3360", + "title": "You can't quantify the program without synthesizing the neural OCR interface!", + "status": "done", + "label": "feature", + "priority": "medium" + }, + { + "id": "TASK-9887", + "title": "Use the auxiliary ASCII sensor, then you can connect the solid state port!", + "status": "backlog", + "label": "bug", + "priority": "medium" + }, + { + "id": "TASK-3649", + "title": "I'll input the virtual USB system, that should circuit the DNS monitor!", + "status": "in progress", + "label": "feature", + "priority": "medium" + }, + { + "id": "TASK-3586", + "title": "If we quantify the circuit, we can get to the CLI feed through the mobile SMS hard drive!", + "status": "in progress", + "label": "bug", + "priority": "low" + }, + { + "id": "TASK-5150", + "title": "I'll hack the wireless XSS port, that should transmitter the IP interface!", + "status": "canceled", + "label": "feature", + "priority": "medium" + }, + { + "id": "TASK-3652", + "title": "The SQL interface is down, override the optical bus so we can program the ASCII interface!", + "status": "backlog", + "label": "feature", + "priority": "low" + }, + { + "id": "TASK-6884", + "title": "Use the digital PCI circuit, then you can synthesize the multi-byte microchip!", + "status": "canceled", + "label": "feature", + "priority": "high" + }, + { + "id": "TASK-1591", + "title": "We need to connect the mobile XSS driver!", + "status": "in progress", + "label": "feature", + "priority": "high" + }, + { + "id": "TASK-3802", + "title": "Try to override the ASCII protocol, maybe it will parse the virtual matrix!", + "status": "in progress", + "label": "feature", + "priority": "low" + }, + { + "id": "TASK-7253", + "title": "Programming the capacitor won't do anything, we need to bypass the neural IB hard drive!", + "status": "backlog", + "label": "bug", + "priority": "high" + }, + { + "id": "TASK-9739", + "title": "We need to hack the multi-byte HDD bus!", + "status": "done", + "label": "documentation", + "priority": "medium" + }, + { + "id": "TASK-4424", + "title": "Try to hack the HEX alarm, maybe it will connect the optical pixel!", + "status": "in progress", + "label": "documentation", + "priority": "medium" + }, + { + "id": "TASK-3922", + "title": "You can't back up the capacitor without generating the wireless PCI program!", + "status": "backlog", + "label": "bug", + "priority": "low" + }, + { + "id": "TASK-4921", + "title": "I'll index the open-source IP feed, that should system the GB application!", + "status": "canceled", + "label": "bug", + "priority": "low" + }, + { + "id": "TASK-5814", + "title": "We need to calculate the 1080p AGP feed!", + "status": "backlog", + "label": "bug", + "priority": "high" + }, + { + "id": "TASK-2645", + "title": "Synthesizing the system won't do anything, we need to navigate the multi-byte HDD firewall!", + "status": "todo", + "label": "documentation", + "priority": "medium" + }, + { + "id": "TASK-4535", + "title": "Try to copy the JSON circuit, maybe it will connect the wireless feed!", + "status": "in progress", + "label": "feature", + "priority": "low" + }, + { + "id": "TASK-4463", + "title": "We need to copy the solid state AGP monitor!", + "status": "done", + "label": "documentation", + "priority": "low" + }, + { + "id": "TASK-9745", + "title": "If we connect the protocol, we can get to the GB system through the bluetooth PCI microchip!", + "status": "canceled", + "label": "feature", + "priority": "high" + }, + { + "id": "TASK-2080", + "title": "If we input the bus, we can get to the RAM matrix through the auxiliary RAM card!", + "status": "todo", + "label": "bug", + "priority": "medium" + }, + { + "id": "TASK-3838", + "title": "I'll bypass the online TCP application, that should panel the AGP system!", + "status": "backlog", + "label": "bug", + "priority": "high" + }, + { + "id": "TASK-1340", + "title": "We need to navigate the virtual PNG circuit!", + "status": "todo", + "label": "bug", + "priority": "medium" + }, + { + "id": "TASK-6665", + "title": "If we parse the monitor, we can get to the SSD hard drive through the cross-platform AGP alarm!", + "status": "canceled", + "label": "feature", + "priority": "low" + }, + { + "id": "TASK-7585", + "title": "If we calculate the hard drive, we can get to the SSL program through the multi-byte CSS microchip!", + "status": "backlog", + "label": "feature", + "priority": "low" + }, + { + "id": "TASK-6319", + "title": "We need to copy the multi-byte SCSI program!", + "status": "backlog", + "label": "bug", + "priority": "high" + }, + { + "id": "TASK-4369", + "title": "Try to input the SCSI bus, maybe it will generate the 1080p pixel!", + "status": "backlog", + "label": "bug", + "priority": "high" + }, + { + "id": "TASK-9035", + "title": "We need to override the solid state PNG array!", + "status": "canceled", + "label": "documentation", + "priority": "low" + }, + { + "id": "TASK-3970", + "title": "You can't index the transmitter without quantifying the haptic ASCII card!", + "status": "todo", + "label": "documentation", + "priority": "medium" + }, + { + "id": "TASK-4473", + "title": "You can't bypass the protocol without overriding the neural RSS program!", + "status": "todo", + "label": "documentation", + "priority": "low" + }, + { + "id": "TASK-4136", + "title": "You can't hack the hard drive without hacking the primary JSON program!", + "status": "canceled", + "label": "bug", + "priority": "medium" + }, + { + "id": "TASK-3939", + "title": "Use the back-end SQL firewall, then you can connect the neural hard drive!", + "status": "done", + "label": "feature", + "priority": "low" + }, + { + "id": "TASK-2007", + "title": "I'll input the back-end USB protocol, that should bandwidth the PCI system!", + "status": "backlog", + "label": "bug", + "priority": "high" + }, + { + "id": "TASK-7516", + "title": "Use the primary SQL program, then you can generate the auxiliary transmitter!", + "status": "done", + "label": "documentation", + "priority": "medium" + }, + { + "id": "TASK-6906", + "title": "Try to back up the DRAM system, maybe it will reboot the online transmitter!", + "status": "done", + "label": "feature", + "priority": "high" + }, + { + "id": "TASK-5207", + "title": "The SMS interface is down, copy the bluetooth bus so we can quantify the VGA card!", + "status": "in progress", + "label": "bug", + "priority": "low" + } + ] \ No newline at end of file diff --git a/src/components/ui/checkbox.tsx b/src/components/ui/checkbox.tsx new file mode 100644 index 0000000..7d2b3c3 --- /dev/null +++ b/src/components/ui/checkbox.tsx @@ -0,0 +1,30 @@ +"use client" + +import * as React from "react" +import * as CheckboxPrimitive from "@radix-ui/react-checkbox" +import { CheckIcon } from "@radix-ui/react-icons" + +import { cn } from "@/lib/utils" + +const Checkbox = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + + +)) +Checkbox.displayName = CheckboxPrimitive.Root.displayName + +export { Checkbox } diff --git a/src/components/ui/command.tsx b/src/components/ui/command.tsx new file mode 100644 index 0000000..30ef57d --- /dev/null +++ b/src/components/ui/command.tsx @@ -0,0 +1,155 @@ +"use client" + +import * as React from "react" +import { type DialogProps } from "@radix-ui/react-dialog" +import { MagnifyingGlassIcon } from "@radix-ui/react-icons" +import { Command as CommandPrimitive } from "cmdk" + +import { cn } from "@/lib/utils" +import { Dialog, DialogContent } from "@/components/ui/dialog" + +const Command = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +Command.displayName = CommandPrimitive.displayName + +interface CommandDialogProps extends DialogProps {} + +const CommandDialog = ({ children, ...props }: CommandDialogProps) => { + return ( + + + + {children} + + + + ) +} + +const CommandInput = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( +
+ + +
+)) + +CommandInput.displayName = CommandPrimitive.Input.displayName + +const CommandList = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) + +CommandList.displayName = CommandPrimitive.List.displayName + +const CommandEmpty = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>((props, ref) => ( + +)) + +CommandEmpty.displayName = CommandPrimitive.Empty.displayName + +const CommandGroup = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) + +CommandGroup.displayName = CommandPrimitive.Group.displayName + +const CommandSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +CommandSeparator.displayName = CommandPrimitive.Separator.displayName + +const CommandItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) + +CommandItem.displayName = CommandPrimitive.Item.displayName + +const CommandShortcut = ({ + className, + ...props +}: React.HTMLAttributes) => { + return ( + + ) +} +CommandShortcut.displayName = "CommandShortcut" + +export { + Command, + CommandDialog, + CommandInput, + CommandList, + CommandEmpty, + CommandGroup, + CommandItem, + CommandShortcut, + CommandSeparator, +} diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx new file mode 100644 index 0000000..95b0d38 --- /dev/null +++ b/src/components/ui/dialog.tsx @@ -0,0 +1,122 @@ +"use client" + +import * as React from "react" +import * as DialogPrimitive from "@radix-ui/react-dialog" +import { Cross2Icon } from "@radix-ui/react-icons" + +import { cn } from "@/lib/utils" + +const Dialog = DialogPrimitive.Root + +const DialogTrigger = DialogPrimitive.Trigger + +const DialogPortal = DialogPrimitive.Portal + +const DialogClose = DialogPrimitive.Close + +const DialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogOverlay.displayName = DialogPrimitive.Overlay.displayName + +const DialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + {children} + + + Close + + + +)) +DialogContent.displayName = DialogPrimitive.Content.displayName + +const DialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +DialogHeader.displayName = "DialogHeader" + +const DialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +DialogFooter.displayName = "DialogFooter" + +const DialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogTitle.displayName = DialogPrimitive.Title.displayName + +const DialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogDescription.displayName = DialogPrimitive.Description.displayName + +export { + Dialog, + DialogPortal, + DialogOverlay, + DialogTrigger, + DialogClose, + DialogContent, + DialogHeader, + DialogFooter, + DialogTitle, + DialogDescription, +} diff --git a/src/main.tsx b/src/main.tsx index 6634584..ff54d61 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -10,6 +10,7 @@ import Root from "./routes/Root"; import Error from "./routes/Error"; import Dashboard05 from "./blocks/Dashboard05"; import MailBlocks from "./blocks/mail/MailBlocks"; +import TasksBlocks from "./blocks/tasks/TasksBlocks"; const router = createBrowserRouter([ { @@ -25,6 +26,10 @@ const router = createBrowserRouter([ path: "/mail", element: , }, + { + path: "/tasks", + element: , + }, ], }, ]); diff --git a/vite.config.ts b/vite.config.ts index 886aed5..942bf6f 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -4,7 +4,9 @@ import react from '@vitejs/plugin-react' // https://vitejs.dev/config/ export default defineConfig({ - plugins: [react()], + plugins: [ + react(), + ], resolve: { alias: { "@": path.resolve(__dirname, "./src"),