87 | >(({ className, ...props }, ref) => (
88 | [role=checkbox]]:translate-y-[2px]",
92 | className
93 | )}
94 | {...props}
95 | />
96 | ))
97 | TableCell.displayName = "TableCell"
98 |
99 | const TableCaption = React.forwardRef<
100 | HTMLTableCaptionElement,
101 | React.HTMLAttributes
102 | >(({ className, ...props }, ref) => (
103 |
108 | ))
109 | TableCaption.displayName = "TableCaption"
110 |
111 | export {
112 | Table,
113 | TableHeader,
114 | TableBody,
115 | TableFooter,
116 | TableHead,
117 | TableRow,
118 | TableCell,
119 | TableCaption,
120 | }
121 |
--------------------------------------------------------------------------------
/src/pages/tasks/components/data-table-pagination.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | ChevronLeftIcon,
3 | ChevronRightIcon,
4 | DoubleArrowLeftIcon,
5 | DoubleArrowRightIcon,
6 | } from '@radix-ui/react-icons'
7 | import { Table } from '@tanstack/react-table'
8 |
9 | import { Button } from '@/components/custom/button'
10 | import {
11 | Select,
12 | SelectContent,
13 | SelectItem,
14 | SelectTrigger,
15 | SelectValue,
16 | } from '@/components/ui/select'
17 |
18 | interface DataTablePaginationProps {
19 | table: Table
20 | }
21 |
22 | export function DataTablePagination({
23 | table,
24 | }: DataTablePaginationProps) {
25 | return (
26 |
27 |
28 | {table.getFilteredSelectedRowModel().rows.length} of{' '}
29 | {table.getFilteredRowModel().rows.length} row(s) selected.
30 |
31 |
32 |
33 | Rows per page
34 |
51 |
52 |
53 | Page {table.getState().pagination.pageIndex + 1} of{' '}
54 | {table.getPageCount()}
55 |
56 |
57 |
66 |
75 |
84 |
93 |
94 |
95 |
96 | )
97 | }
98 |
--------------------------------------------------------------------------------
/src/pages/tasks/components/columns.tsx:
--------------------------------------------------------------------------------
1 | import { ColumnDef } from '@tanstack/react-table'
2 |
3 | import { Badge } from '@/components/ui/badge'
4 | import { Checkbox } from '@/components/ui/checkbox'
5 | import { DataTableColumnHeader } from './data-table-column-header'
6 | import { DataTableRowActions } from './data-table-row-actions'
7 |
8 | import { labels, priorities, statuses } from '../data/data'
9 | import { Task } from '../data/schema'
10 |
11 | export const columns: ColumnDef[] = [
12 | {
13 | id: 'select',
14 | header: ({ table }) => (
15 | table.toggleAllPageRowsSelected(!!value)}
21 | aria-label='Select all'
22 | className='translate-y-[2px]'
23 | />
24 | ),
25 | cell: ({ row }) => (
26 | row.toggleSelected(!!value)}
29 | aria-label='Select row'
30 | className='translate-y-[2px]'
31 | />
32 | ),
33 | enableSorting: false,
34 | enableHiding: false,
35 | },
36 | {
37 | accessorKey: 'id',
38 | header: ({ column }) => (
39 |
40 | ),
41 | cell: ({ row }) => {row.getValue('id')} ,
42 | enableSorting: false,
43 | enableHiding: false,
44 | },
45 | {
46 | accessorKey: 'title',
47 | header: ({ column }) => (
48 |
49 | ),
50 | cell: ({ row }) => {
51 | const label = labels.find((label) => label.value === row.original.label)
52 |
53 | return (
54 |
55 | {label && {label.label}}
56 |
57 | {row.getValue('title')}
58 |
59 |
60 | )
61 | },
62 | },
63 | {
64 | accessorKey: 'status',
65 | header: ({ column }) => (
66 |
67 | ),
68 | cell: ({ row }) => {
69 | const status = statuses.find(
70 | (status) => status.value === row.getValue('status')
71 | )
72 |
73 | if (!status) {
74 | return null
75 | }
76 |
77 | return (
78 |
79 | {status.icon && (
80 |
81 | )}
82 | {status.label}
83 |
84 | )
85 | },
86 | filterFn: (row, id, value) => {
87 | return value.includes(row.getValue(id))
88 | },
89 | },
90 | {
91 | accessorKey: 'priority',
92 | header: ({ column }) => (
93 |
94 | ),
95 | cell: ({ row }) => {
96 | const priority = priorities.find(
97 | (priority) => priority.value === row.getValue('priority')
98 | )
99 |
100 | if (!priority) {
101 | return null
102 | }
103 |
104 | return (
105 |
106 | {priority.icon && (
107 |
108 | )}
109 | {priority.label}
110 |
111 | )
112 | },
113 | filterFn: (row, id, value) => {
114 | return value.includes(row.getValue(id))
115 | },
116 | },
117 | {
118 | id: 'actions',
119 | cell: ({ row }) => ,
120 | },
121 | ]
122 |
--------------------------------------------------------------------------------
/src/data/sidelinks.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | IconApps,
3 | IconBarrierBlock,
4 | IconBoxSeam,
5 | IconChartHistogram,
6 | IconChecklist,
7 | IconComponents,
8 | IconError404,
9 | IconExclamationCircle,
10 | IconHexagonNumber1,
11 | IconHexagonNumber2,
12 | IconHexagonNumber3,
13 | IconHexagonNumber4,
14 | IconHexagonNumber5,
15 | IconLayoutDashboard,
16 | IconMessages,
17 | IconRouteAltLeft,
18 | IconServerOff,
19 | IconSettings,
20 | IconTruck,
21 | IconUserShield,
22 | IconUsers,
23 | } from '@tabler/icons-react'
24 |
25 | export interface NavLink {
26 | title: string
27 | label?: string
28 | href: string
29 | icon: JSX.Element
30 | }
31 |
32 | export interface SideLink extends NavLink {
33 | sub?: NavLink[]
34 | }
35 |
36 | export const sidelinks: SideLink[] = [
37 | {
38 | title: 'Dashboard',
39 | label: '',
40 | href: '/',
41 | icon: ,
42 | },
43 | {
44 | title: 'Tasks',
45 | label: '3',
46 | href: '/tasks',
47 | icon: ,
48 | },
49 | {
50 | title: 'Chats',
51 | label: '9',
52 | href: '/chats',
53 | icon: ,
54 | },
55 | {
56 | title: 'Apps',
57 | label: '',
58 | href: '/apps',
59 | icon: ,
60 | },
61 | {
62 | title: 'Authentication',
63 | label: '',
64 | href: '',
65 | icon: ,
66 | sub: [
67 | {
68 | title: 'Sign In (email + password)',
69 | label: '',
70 | href: '/sign-in',
71 | icon: ,
72 | },
73 | {
74 | title: 'Sign In (Box)',
75 | label: '',
76 | href: '/sign-in-2',
77 | icon: ,
78 | },
79 | {
80 | title: 'Sign Up',
81 | label: '',
82 | href: '/sign-up',
83 | icon: ,
84 | },
85 | {
86 | title: 'Forgot Password',
87 | label: '',
88 | href: '/forgot-password',
89 | icon: ,
90 | },
91 | {
92 | title: 'OTP',
93 | label: '',
94 | href: '/otp',
95 | icon: ,
96 | },
97 | ],
98 | },
99 | {
100 | title: 'Users',
101 | label: '',
102 | href: '/users',
103 | icon: ,
104 | },
105 | {
106 | title: 'Requests',
107 | label: '10',
108 | href: '/requests',
109 | icon: ,
110 | sub: [
111 | {
112 | title: 'Trucks',
113 | label: '9',
114 | href: '/trucks',
115 | icon: ,
116 | },
117 | {
118 | title: 'Cargos',
119 | label: '',
120 | href: '/cargos',
121 | icon: ,
122 | },
123 | ],
124 | },
125 | {
126 | title: 'Analysis',
127 | label: '',
128 | href: '/analysis',
129 | icon: ,
130 | },
131 | {
132 | title: 'Extra Components',
133 | label: '',
134 | href: '/extra-components',
135 | icon: ,
136 | },
137 | {
138 | title: 'Error Pages',
139 | label: '',
140 | href: '',
141 | icon: ,
142 | sub: [
143 | {
144 | title: 'Not Found',
145 | label: '',
146 | href: '/404',
147 | icon: ,
148 | },
149 | {
150 | title: 'Internal Server Error',
151 | label: '',
152 | href: '/500',
153 | icon: ,
154 | },
155 | {
156 | title: 'Maintenance Error',
157 | label: '',
158 | href: '/503',
159 | icon: ,
160 | },
161 | ],
162 | },
163 | {
164 | title: 'Settings',
165 | label: '',
166 | href: '/settings',
167 | icon: ,
168 | },
169 | ]
170 |
--------------------------------------------------------------------------------
/src/pages/tasks/components/data-table.tsx:
--------------------------------------------------------------------------------
1 | import * as React from 'react'
2 | import {
3 | ColumnDef,
4 | ColumnFiltersState,
5 | SortingState,
6 | VisibilityState,
7 | flexRender,
8 | getCoreRowModel,
9 | getFacetedRowModel,
10 | getFacetedUniqueValues,
11 | getFilteredRowModel,
12 | getPaginationRowModel,
13 | getSortedRowModel,
14 | useReactTable,
15 | } from '@tanstack/react-table'
16 |
17 | import {
18 | Table,
19 | TableBody,
20 | TableCell,
21 | TableHead,
22 | TableHeader,
23 | TableRow,
24 | } from '@/components/ui/table'
25 |
26 | import { DataTablePagination } from '../components/data-table-pagination'
27 | import { DataTableToolbar } from '../components/data-table-toolbar'
28 |
29 | interface DataTableProps {
30 | columns: ColumnDef[]
31 | data: TData[]
32 | }
33 |
34 | export function DataTable({
35 | columns,
36 | data,
37 | }: DataTableProps) {
38 | const [rowSelection, setRowSelection] = React.useState({})
39 | const [columnVisibility, setColumnVisibility] =
40 | React.useState({})
41 | const [columnFilters, setColumnFilters] = React.useState(
42 | []
43 | )
44 | const [sorting, setSorting] = React.useState([])
45 |
46 | const table = useReactTable({
47 | data,
48 | columns,
49 | state: {
50 | sorting,
51 | columnVisibility,
52 | rowSelection,
53 | columnFilters,
54 | },
55 | enableRowSelection: true,
56 | onRowSelectionChange: setRowSelection,
57 | onSortingChange: setSorting,
58 | onColumnFiltersChange: setColumnFilters,
59 | onColumnVisibilityChange: setColumnVisibility,
60 | getCoreRowModel: getCoreRowModel(),
61 | getFilteredRowModel: getFilteredRowModel(),
62 | getPaginationRowModel: getPaginationRowModel(),
63 | getSortedRowModel: getSortedRowModel(),
64 | getFacetedRowModel: getFacetedRowModel(),
65 | getFacetedUniqueValues: getFacetedUniqueValues(),
66 | })
67 |
68 | return (
69 |
70 |
71 |
72 |
73 |
74 | {table.getHeaderGroups().map((headerGroup) => (
75 |
76 | {headerGroup.headers.map((header) => {
77 | return (
78 |
79 | {header.isPlaceholder
80 | ? null
81 | : flexRender(
82 | header.column.columnDef.header,
83 | header.getContext()
84 | )}
85 |
86 | )
87 | })}
88 |
89 | ))}
90 |
91 |
92 | {table.getRowModel().rows?.length ? (
93 | table.getRowModel().rows.map((row) => (
94 |
98 | {row.getVisibleCells().map((cell) => (
99 |
100 | {flexRender(
101 | cell.column.columnDef.cell,
102 | cell.getContext()
103 | )}
104 |
105 | ))}
106 |
107 | ))
108 | ) : (
109 |
110 |
114 | No results.
115 |
116 |
117 | )}
118 |
119 |
120 |
121 |
122 |
123 | )
124 | }
125 |
--------------------------------------------------------------------------------
/src/pages/settings/display/display-form.tsx:
--------------------------------------------------------------------------------
1 | import { zodResolver } from '@hookform/resolvers/zod'
2 | import { useForm } from 'react-hook-form'
3 | import { z } from 'zod'
4 |
5 | import { Button } from '@/components/custom/button'
6 | import { Checkbox } from '@/components/ui/checkbox'
7 | import {
8 | Form,
9 | FormControl,
10 | FormDescription,
11 | FormField,
12 | FormItem,
13 | FormLabel,
14 | FormMessage,
15 | } from '@/components/ui/form'
16 | import { toast } from '@/components/ui/use-toast'
17 |
18 | const items = [
19 | {
20 | id: 'recents',
21 | label: 'Recents',
22 | },
23 | {
24 | id: 'home',
25 | label: 'Home',
26 | },
27 | {
28 | id: 'applications',
29 | label: 'Applications',
30 | },
31 | {
32 | id: 'desktop',
33 | label: 'Desktop',
34 | },
35 | {
36 | id: 'downloads',
37 | label: 'Downloads',
38 | },
39 | {
40 | id: 'documents',
41 | label: 'Documents',
42 | },
43 | ] as const
44 |
45 | const displayFormSchema = z.object({
46 | items: z.array(z.string()).refine((value) => value.some((item) => item), {
47 | message: 'You have to select at least one item.',
48 | }),
49 | })
50 |
51 | type DisplayFormValues = z.infer
52 |
53 | // This can come from your database or API.
54 | const defaultValues: Partial = {
55 | items: ['recents', 'home'],
56 | }
57 |
58 | export function DisplayForm() {
59 | const form = useForm({
60 | resolver: zodResolver(displayFormSchema),
61 | defaultValues,
62 | })
63 |
64 | function onSubmit(data: DisplayFormValues) {
65 | toast({
66 | title: 'You submitted the following values:',
67 | description: (
68 |
69 | {JSON.stringify(data, null, 2)}
70 |
71 | ),
72 | })
73 | }
74 |
75 | return (
76 |
128 |
129 | )
130 | }
131 |
--------------------------------------------------------------------------------
/src/components/ui/dialog.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 | import * as DialogPrimitive from "@radix-ui/react-dialog"
3 | import { Cross2Icon } from "@radix-ui/react-icons"
4 |
5 | import { cn } from "@/lib/utils"
6 |
7 | const Dialog = DialogPrimitive.Root
8 |
9 | const DialogTrigger = DialogPrimitive.Trigger
10 |
11 | const DialogPortal = DialogPrimitive.Portal
12 |
13 | const DialogClose = DialogPrimitive.Close
14 |
15 | const DialogOverlay = React.forwardRef<
16 | React.ElementRef,
17 | React.ComponentPropsWithoutRef
18 | >(({ className, ...props }, ref) => (
19 |
27 | ))
28 | DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
29 |
30 | const DialogContent = React.forwardRef<
31 | React.ElementRef,
32 | React.ComponentPropsWithoutRef
33 | >(({ className, children, ...props }, ref) => (
34 |
35 |
36 |
44 | {children}
45 |
46 |
47 | Close
48 |
49 |
50 |
51 | ))
52 | DialogContent.displayName = DialogPrimitive.Content.displayName
53 |
54 | const DialogHeader = ({
55 | className,
56 | ...props
57 | }: React.HTMLAttributes) => (
58 |
65 | )
66 | DialogHeader.displayName = "DialogHeader"
67 |
68 | const DialogFooter = ({
69 | className,
70 | ...props
71 | }: React.HTMLAttributes) => (
72 |
79 | )
80 | DialogFooter.displayName = "DialogFooter"
81 |
82 | const DialogTitle = React.forwardRef<
83 | React.ElementRef,
84 | React.ComponentPropsWithoutRef
85 | >(({ className, ...props }, ref) => (
86 |
94 | ))
95 | DialogTitle.displayName = DialogPrimitive.Title.displayName
96 |
97 | const DialogDescription = React.forwardRef<
98 | React.ElementRef,
99 | React.ComponentPropsWithoutRef
100 | >(({ className, ...props }, ref) => (
101 |
106 | ))
107 | DialogDescription.displayName = DialogPrimitive.Description.displayName
108 |
109 | export {
110 | Dialog,
111 | DialogPortal,
112 | DialogOverlay,
113 | DialogTrigger,
114 | DialogClose,
115 | DialogContent,
116 | DialogHeader,
117 | DialogFooter,
118 | DialogTitle,
119 | DialogDescription,
120 | }
121 |
--------------------------------------------------------------------------------
/src/router.tsx:
--------------------------------------------------------------------------------
1 | import { createBrowserRouter } from 'react-router-dom'
2 | import GeneralError from './pages/errors/general-error'
3 | import NotFoundError from './pages/errors/not-found-error'
4 | import MaintenanceError from './pages/errors/maintenance-error'
5 |
6 | const router = createBrowserRouter([
7 | // Auth routes
8 | {
9 | path: '/sign-in',
10 | lazy: async () => ({
11 | Component: (await import('./pages/auth/sign-in')).default,
12 | }),
13 | },
14 | {
15 | path: '/sign-in-2',
16 | lazy: async () => ({
17 | Component: (await import('./pages/auth/sign-in-2')).default,
18 | }),
19 | },
20 | {
21 | path: '/sign-up',
22 | lazy: async () => ({
23 | Component: (await import('./pages/auth/sign-up')).default,
24 | }),
25 | },
26 | {
27 | path: '/forgot-password',
28 | lazy: async () => ({
29 | Component: (await import('./pages/auth/forgot-password')).default,
30 | }),
31 | },
32 | {
33 | path: '/otp',
34 | lazy: async () => ({
35 | Component: (await import('./pages/auth/otp')).default,
36 | }),
37 | },
38 |
39 | // Main routes
40 | {
41 | path: '/',
42 | lazy: async () => {
43 | const AppShell = await import('./components/app-shell')
44 | return { Component: AppShell.default }
45 | },
46 | errorElement: ,
47 | children: [
48 | {
49 | index: true,
50 | lazy: async () => ({
51 | Component: (await import('./pages/dashboard')).default,
52 | }),
53 | },
54 | {
55 | path: 'tasks',
56 | lazy: async () => ({
57 | Component: (await import('@/pages/tasks')).default,
58 | }),
59 | },
60 | {
61 | path: 'chats',
62 | lazy: async () => ({
63 | Component: (await import('@/components/coming-soon')).default,
64 | }),
65 | },
66 | {
67 | path: 'apps',
68 | lazy: async () => ({
69 | Component: (await import('@/pages/apps')).default,
70 | }),
71 | },
72 | {
73 | path: 'users',
74 | lazy: async () => ({
75 | Component: (await import('@/components/coming-soon')).default,
76 | }),
77 | },
78 | {
79 | path: 'analysis',
80 | lazy: async () => ({
81 | Component: (await import('@/components/coming-soon')).default,
82 | }),
83 | },
84 | {
85 | path: 'extra-components',
86 | lazy: async () => ({
87 | Component: (await import('@/pages/extra-components')).default,
88 | }),
89 | },
90 | {
91 | path: 'settings',
92 | lazy: async () => ({
93 | Component: (await import('./pages/settings')).default,
94 | }),
95 | errorElement: ,
96 | children: [
97 | {
98 | index: true,
99 | lazy: async () => ({
100 | Component: (await import('./pages/settings/profile')).default,
101 | }),
102 | },
103 | {
104 | path: 'account',
105 | lazy: async () => ({
106 | Component: (await import('./pages/settings/account')).default,
107 | }),
108 | },
109 | {
110 | path: 'appearance',
111 | lazy: async () => ({
112 | Component: (await import('./pages/settings/appearance')).default,
113 | }),
114 | },
115 | {
116 | path: 'notifications',
117 | lazy: async () => ({
118 | Component: (await import('./pages/settings/notifications'))
119 | .default,
120 | }),
121 | },
122 | {
123 | path: 'display',
124 | lazy: async () => ({
125 | Component: (await import('./pages/settings/display')).default,
126 | }),
127 | },
128 | {
129 | path: 'error-example',
130 | lazy: async () => ({
131 | Component: (await import('./pages/settings/error-example'))
132 | .default,
133 | }),
134 | errorElement: ,
135 | },
136 | ],
137 | },
138 | ],
139 | },
140 |
141 | // Error routes
142 | { path: '/500', Component: GeneralError },
143 | { path: '/404', Component: NotFoundError },
144 | { path: '/503', Component: MaintenanceError },
145 |
146 | // Fallback 404 route
147 | { path: '*', Component: NotFoundError },
148 | ])
149 |
150 | export default router
151 |
--------------------------------------------------------------------------------
/src/components/sidebar.tsx:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from 'react'
2 | import { IconChevronsLeft, IconMenu2, IconX } from '@tabler/icons-react'
3 | import { Layout } from './custom/layout'
4 | import { Button } from './custom/button'
5 | import Nav from './nav'
6 | import { cn } from '@/lib/utils'
7 | import { sidelinks } from '@/data/sidelinks'
8 |
9 | interface SidebarProps extends React.HTMLAttributes {
10 | isCollapsed: boolean
11 | setIsCollapsed: React.Dispatch>
12 | }
13 |
14 | export default function Sidebar({
15 | className,
16 | isCollapsed,
17 | setIsCollapsed,
18 | }: SidebarProps) {
19 | const [navOpened, setNavOpened] = useState(false)
20 |
21 | /* Make body not scrollable when navBar is opened */
22 | useEffect(() => {
23 | if (navOpened) {
24 | document.body.classList.add('overflow-hidden')
25 | } else {
26 | document.body.classList.remove('overflow-hidden')
27 | }
28 | }, [navOpened])
29 |
30 | return (
31 | |