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 (
+
+ )
+}
+
+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"),