create-ej-app 12.0.0 → 14.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli.js +5 -4
- package/cli.ts +9 -4
- package/package.json +1 -1
- package/template/base/package.json +9 -15
- package/template/base/terraform/artifact-registry.tf +5 -0
- package/template/base/terraform/backend-service.tf +13 -0
- package/template/base/terraform/cleanup.sh +9 -0
- package/template/base/terraform/cloudbuild.tf +70 -0
- package/template/base/terraform/cloudrun.tf +68 -0
- package/template/base/terraform/database.tf +60 -0
- package/template/base/terraform/forwarding-rule.tf +43 -0
- package/template/base/terraform/ip-address.tf +11 -0
- package/template/base/terraform/network-endpoint-group.tf +11 -0
- package/template/base/terraform/network.tf +21 -0
- package/template/base/terraform/pre.sh +3 -0
- package/template/base/terraform/providers.tf +18 -0
- package/template/base/terraform/secret.txt +11 -0
- package/template/base/terraform/setup.sh +25 -0
- package/template/base/terraform/ssl-certs.tf +32 -0
- package/template/base/terraform/target-https-proxy copy.tf +14 -0
- package/template/base/terraform/target-https-proxy.tf +14 -0
- package/template/base/terraform/terraform.tfvars +4 -0
- package/template/base/terraform/url-map.tf +23 -0
- package/template/base/terraform/variables.tf +21 -0
- package/template/old/.env.example +0 -17
- package/template/old/.eslintrc.cjs +0 -26
- package/template/old/components.json +0 -17
- package/template/old/drizzle.config.ts +0 -11
- package/template/old/next.config.mjs +0 -4
- package/template/old/package.json +0 -87
- package/template/old/postcss.config.mjs +0 -8
- package/template/old/prettier.config.mjs +0 -11
- package/template/old/public/next.svg +0 -1
- package/template/old/public/vercel.svg +0 -1
- package/template/old/src/app/(app)/dashboard/layout.tsx +0 -14
- package/template/old/src/app/(app)/dashboard/page.tsx +0 -12
- package/template/old/src/app/(app)/layout.tsx +0 -125
- package/template/old/src/app/(app)/roles/[id]/actions.ts +0 -59
- package/template/old/src/app/(app)/roles/[id]/page.tsx +0 -46
- package/template/old/src/app/(app)/roles/[id]/render.tsx +0 -264
- package/template/old/src/app/(app)/roles/[id]/validation.ts +0 -22
- package/template/old/src/app/(app)/roles/actions.ts +0 -33
- package/template/old/src/app/(app)/roles/page.tsx +0 -52
- package/template/old/src/app/(app)/roles/render.tsx +0 -80
- package/template/old/src/app/(app)/settings/actions.ts +0 -17
- package/template/old/src/app/(app)/settings/layout.tsx +0 -14
- package/template/old/src/app/(app)/settings/loading.tsx +0 -28
- package/template/old/src/app/(app)/settings/page.tsx +0 -19
- package/template/old/src/app/(app)/settings/render.tsx +0 -107
- package/template/old/src/app/(app)/settings/validation.ts +0 -5
- package/template/old/src/app/(app)/tasks/[id]/actions.ts +0 -92
- package/template/old/src/app/(app)/tasks/[id]/page.tsx +0 -59
- package/template/old/src/app/(app)/tasks/[id]/render.tsx +0 -482
- package/template/old/src/app/(app)/tasks/[id]/validation.ts +0 -18
- package/template/old/src/app/(app)/tasks/actions.ts +0 -39
- package/template/old/src/app/(app)/tasks/page.tsx +0 -59
- package/template/old/src/app/(app)/tasks/render.tsx +0 -129
- package/template/old/src/app/(app)/tasks/statusEnum.ts +0 -3
- package/template/old/src/app/(app)/users/[id]/actions.ts +0 -68
- package/template/old/src/app/(app)/users/[id]/page.tsx +0 -39
- package/template/old/src/app/(app)/users/[id]/render.tsx +0 -325
- package/template/old/src/app/(app)/users/[id]/validation.ts +0 -18
- package/template/old/src/app/(app)/users/actions.ts +0 -34
- package/template/old/src/app/(app)/users/page.tsx +0 -62
- package/template/old/src/app/(app)/users/render.tsx +0 -124
- package/template/old/src/app/(auth)/layout.tsx +0 -27
- package/template/old/src/app/(auth)/login/page.tsx +0 -49
- package/template/old/src/app/(auth)/login/render.tsx +0 -146
- package/template/old/src/app/(marketing)/layout.tsx +0 -42
- package/template/old/src/app/(marketing)/page.tsx +0 -97
- package/template/old/src/app/api/auth/[...nextauth]/route.ts +0 -7
- package/template/old/src/app/favicon.ico +0 -0
- package/template/old/src/app/globals.css +0 -76
- package/template/old/src/app/layout.tsx +0 -31
- package/template/old/src/components/app-nav-command.tsx +0 -178
- package/template/old/src/components/dark-mode-toggle.tsx +0 -43
- package/template/old/src/components/data-table/data-table-column-header.tsx +0 -91
- package/template/old/src/components/data-table/data-table-faceted-filter.tsx +0 -149
- package/template/old/src/components/data-table/data-table-loading.tsx +0 -77
- package/template/old/src/components/data-table/data-table-pagination.tsx +0 -94
- package/template/old/src/components/data-table/data-table-toolbar.tsx +0 -78
- package/template/old/src/components/data-table/data-table-types.ts +0 -17
- package/template/old/src/components/data-table/data-table-view-options.tsx +0 -59
- package/template/old/src/components/data-table/data-table.tsx +0 -301
- package/template/old/src/components/data-table/simple-data-table.tsx +0 -109
- package/template/old/src/components/empty-placeholder.tsx +0 -78
- package/template/old/src/components/heading.tsx +0 -17
- package/template/old/src/components/icons.tsx +0 -101
- package/template/old/src/components/main-nav.tsx +0 -64
- package/template/old/src/components/mobile-nav.tsx +0 -50
- package/template/old/src/components/shell.tsx +0 -13
- package/template/old/src/components/side-nav.tsx +0 -51
- package/template/old/src/components/site-footer.tsx +0 -31
- package/template/old/src/components/system-info.tsx +0 -36
- package/template/old/src/components/ui/alert-dialog.tsx +0 -141
- package/template/old/src/components/ui/avatar.tsx +0 -50
- package/template/old/src/components/ui/badge.tsx +0 -36
- package/template/old/src/components/ui/button.tsx +0 -56
- package/template/old/src/components/ui/card.tsx +0 -79
- package/template/old/src/components/ui/checkbox.tsx +0 -30
- package/template/old/src/components/ui/command.tsx +0 -155
- package/template/old/src/components/ui/dialog.tsx +0 -122
- package/template/old/src/components/ui/dropdown-menu.tsx +0 -200
- package/template/old/src/components/ui/form.tsx +0 -176
- package/template/old/src/components/ui/input.tsx +0 -25
- package/template/old/src/components/ui/label.tsx +0 -26
- package/template/old/src/components/ui/popover.tsx +0 -31
- package/template/old/src/components/ui/select.tsx +0 -160
- package/template/old/src/components/ui/separator.tsx +0 -31
- package/template/old/src/components/ui/skeleton.tsx +0 -15
- package/template/old/src/components/ui/sonner.tsx +0 -31
- package/template/old/src/components/ui/table.tsx +0 -117
- package/template/old/src/components/user-account-nav.tsx +0 -60
- package/template/old/src/components/user-avatar.tsx +0 -27
- package/template/old/src/data-access/resource.ts +0 -85
- package/template/old/src/data-access/role-permission.ts +0 -45
- package/template/old/src/data-access/role.ts +0 -105
- package/template/old/src/data-access/task.ts +0 -141
- package/template/old/src/data-access/user.ts +0 -141
- package/template/old/src/db/index.ts +0 -14
- package/template/old/src/db/schema.ts +0 -287
- package/template/old/src/env.mjs +0 -58
- package/template/old/src/hooks/use-debounce.tsx +0 -17
- package/template/old/src/hooks/use-lock-body.tsx +0 -13
- package/template/old/src/lib/auth.ts +0 -89
- package/template/old/src/lib/cn.ts +0 -4
- package/template/old/src/lib/currentUser.ts +0 -8
- package/template/old/src/lib/delay.ts +0 -2
- package/template/old/src/lib/formatDate.ts +0 -11
- package/template/old/src/lib/generateLabel.ts +0 -5
- package/template/old/src/lib/s3Client.ts +0 -156
- package/template/old/src/lib/safe-action.ts +0 -19
- package/template/old/src/lib/siteConfig.ts +0 -4
- package/template/old/src/lib/sortOrderEnum.ts +0 -2
- package/template/old/src/use-case/role-permission.ts +0 -19
- package/template/old/src/use-case/role.ts +0 -110
- package/template/old/src/use-case/task.ts +0 -166
- package/template/old/src/use-case/user.ts +0 -121
- package/template/old/tailwind.config.ts +0 -81
- package/template/old/tsconfig.json +0 -47
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
// import debounce from 'lodash/debounce'
|
|
4
|
-
import { signOut } from 'next-auth/react'
|
|
5
|
-
import { useTheme } from 'next-themes'
|
|
6
|
-
import { useRouter } from 'next/navigation'
|
|
7
|
-
import { FC, useCallback, useEffect, useState } from 'react'
|
|
8
|
-
|
|
9
|
-
import { Icons } from '@/components/icons'
|
|
10
|
-
import { Button } from '@/components/ui/button'
|
|
11
|
-
import {
|
|
12
|
-
Command,
|
|
13
|
-
CommandDialog,
|
|
14
|
-
CommandEmpty,
|
|
15
|
-
CommandGroup,
|
|
16
|
-
CommandInput,
|
|
17
|
-
CommandItem,
|
|
18
|
-
CommandList,
|
|
19
|
-
CommandSeparator
|
|
20
|
-
} from '@/components/ui/command'
|
|
21
|
-
// import { doSearch, type SearchResult } from '../app/(app)/actions/doSearch'
|
|
22
|
-
|
|
23
|
-
export const AppNavCommand: FC<{
|
|
24
|
-
modules: {
|
|
25
|
-
task: boolean
|
|
26
|
-
root: boolean
|
|
27
|
-
}
|
|
28
|
-
}> = ({ modules }) => {
|
|
29
|
-
const [open, setOpen] = useState(false)
|
|
30
|
-
// const [search, setSearch] = useState('')
|
|
31
|
-
// const [searchResults, setSearchResults] = useState<SearchResult[]>([])
|
|
32
|
-
const { setTheme } = useTheme()
|
|
33
|
-
const router = useRouter()
|
|
34
|
-
|
|
35
|
-
useEffect(() => {
|
|
36
|
-
const down = (e: KeyboardEvent) => {
|
|
37
|
-
if (e.key === 'k' && (e.metaKey || e.ctrlKey)) {
|
|
38
|
-
e.preventDefault()
|
|
39
|
-
setOpen(open => !open)
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
document.addEventListener('keydown', down)
|
|
44
|
-
return () => document.removeEventListener('keydown', down)
|
|
45
|
-
}, [])
|
|
46
|
-
|
|
47
|
-
// useEffect(() => {
|
|
48
|
-
// if (!search) {
|
|
49
|
-
// setSearchResults([])
|
|
50
|
-
// return
|
|
51
|
-
// }
|
|
52
|
-
// doSearch(search).then(results => setSearchResults(results))
|
|
53
|
-
// }, [search])
|
|
54
|
-
|
|
55
|
-
const runCommand = useCallback((command: () => unknown) => {
|
|
56
|
-
setOpen(false)
|
|
57
|
-
command()
|
|
58
|
-
}, [])
|
|
59
|
-
|
|
60
|
-
// const debouncedSearch = useMemo(
|
|
61
|
-
// () =>
|
|
62
|
-
// debounce((search: string) => {
|
|
63
|
-
// setSearch(search)
|
|
64
|
-
// }, 500),
|
|
65
|
-
// []
|
|
66
|
-
// )
|
|
67
|
-
|
|
68
|
-
return (
|
|
69
|
-
<>
|
|
70
|
-
<Button
|
|
71
|
-
variant="outline"
|
|
72
|
-
className="relative w-36 justify-start text-sm text-muted-foreground lg:w-64"
|
|
73
|
-
onClick={() => setOpen(true)}
|
|
74
|
-
>
|
|
75
|
-
<Icons.terminal className="mr-2 h-4 w-4" />
|
|
76
|
-
<span className="truncate">Command Menu</span>
|
|
77
|
-
<kbd className="pointer-events-none absolute right-2 top-2 hidden h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium opacity-100 lg:flex">
|
|
78
|
-
<span className="text-xs">⌘</span>K
|
|
79
|
-
</kbd>
|
|
80
|
-
</Button>
|
|
81
|
-
<CommandDialog open={open} onOpenChange={setOpen}>
|
|
82
|
-
<Command>
|
|
83
|
-
<CommandInput
|
|
84
|
-
placeholder="Search..."
|
|
85
|
-
// onChangeCapture={e => {
|
|
86
|
-
// debouncedSearch((e.target as any)?.value || '')
|
|
87
|
-
// }}
|
|
88
|
-
/>
|
|
89
|
-
<CommandList>
|
|
90
|
-
<CommandEmpty>No search results found.</CommandEmpty>
|
|
91
|
-
<CommandGroup heading="Suggestions">
|
|
92
|
-
<CommandItem
|
|
93
|
-
onSelect={() => runCommand(() => router.push('/dashboard'))}
|
|
94
|
-
>
|
|
95
|
-
<Icons.dashboard className="mr-2 h-4 w-4" />
|
|
96
|
-
Dashboard
|
|
97
|
-
</CommandItem>
|
|
98
|
-
{modules.task ? (
|
|
99
|
-
<CommandItem
|
|
100
|
-
onSelect={() => runCommand(() => router.push('/tasks'))}
|
|
101
|
-
>
|
|
102
|
-
<Icons.task className="mr-2 h-4 w-4" />
|
|
103
|
-
Tasks
|
|
104
|
-
</CommandItem>
|
|
105
|
-
) : undefined}
|
|
106
|
-
</CommandGroup>
|
|
107
|
-
{modules.root ? (
|
|
108
|
-
<CommandGroup heading="Root Actions">
|
|
109
|
-
<CommandItem
|
|
110
|
-
onSelect={() => runCommand(() => router.push('/users'))}
|
|
111
|
-
>
|
|
112
|
-
<Icons.mixer className="mr-2 h-4 w-4" />
|
|
113
|
-
Users
|
|
114
|
-
</CommandItem>
|
|
115
|
-
<CommandItem
|
|
116
|
-
onSelect={() => runCommand(() => router.push('/roles'))}
|
|
117
|
-
>
|
|
118
|
-
<Icons.user className="mr-2 h-4 w-4" />
|
|
119
|
-
Roles
|
|
120
|
-
</CommandItem>
|
|
121
|
-
</CommandGroup>
|
|
122
|
-
) : undefined}
|
|
123
|
-
<CommandGroup heading="Account">
|
|
124
|
-
<CommandItem
|
|
125
|
-
onSelect={() => runCommand(() => router.push('/settings'))}
|
|
126
|
-
>
|
|
127
|
-
<Icons.settings className="mr-2 h-4 w-4" />
|
|
128
|
-
Settings
|
|
129
|
-
</CommandItem>
|
|
130
|
-
<CommandItem
|
|
131
|
-
onSelect={() =>
|
|
132
|
-
runCommand(() =>
|
|
133
|
-
signOut({
|
|
134
|
-
callbackUrl: `${window.location.origin}/login`
|
|
135
|
-
})
|
|
136
|
-
)
|
|
137
|
-
}
|
|
138
|
-
>
|
|
139
|
-
<Icons.logout className="mr-2 h-4 w-4" />
|
|
140
|
-
Sign Out
|
|
141
|
-
</CommandItem>
|
|
142
|
-
</CommandGroup>
|
|
143
|
-
<CommandSeparator />
|
|
144
|
-
<CommandGroup heading="Theme">
|
|
145
|
-
<CommandItem onSelect={() => runCommand(() => setTheme('light'))}>
|
|
146
|
-
<Icons.sun className="mr-2 h-4 w-4" />
|
|
147
|
-
Light
|
|
148
|
-
</CommandItem>
|
|
149
|
-
<CommandItem onSelect={() => runCommand(() => setTheme('dark'))}>
|
|
150
|
-
<Icons.moon className="mr-2 h-4 w-4" />
|
|
151
|
-
Dark
|
|
152
|
-
</CommandItem>
|
|
153
|
-
<CommandItem
|
|
154
|
-
onSelect={() => runCommand(() => setTheme('system'))}
|
|
155
|
-
>
|
|
156
|
-
<Icons.laptop className="mr-2 h-4 w-4" />
|
|
157
|
-
System
|
|
158
|
-
</CommandItem>
|
|
159
|
-
</CommandGroup>
|
|
160
|
-
{/* {searchResults.length ? (
|
|
161
|
-
<CommandGroup heading="Search Results">
|
|
162
|
-
{searchResults.map((sr, i) => (
|
|
163
|
-
<CommandItem
|
|
164
|
-
key={i}
|
|
165
|
-
onSelect={() => runCommand(() => router.push(sr.link))}
|
|
166
|
-
value={sr.id}
|
|
167
|
-
>
|
|
168
|
-
{sr.title}
|
|
169
|
-
</CommandItem>
|
|
170
|
-
))}
|
|
171
|
-
</CommandGroup>
|
|
172
|
-
) : undefined} */}
|
|
173
|
-
</CommandList>
|
|
174
|
-
</Command>
|
|
175
|
-
</CommandDialog>
|
|
176
|
-
</>
|
|
177
|
-
)
|
|
178
|
-
}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { useTheme } from 'next-themes'
|
|
4
|
-
import { FC } from 'react'
|
|
5
|
-
|
|
6
|
-
import { Icons } from '@/components/icons'
|
|
7
|
-
import { Button } from '@/components/ui/button'
|
|
8
|
-
import {
|
|
9
|
-
DropdownMenu,
|
|
10
|
-
DropdownMenuContent,
|
|
11
|
-
DropdownMenuItem,
|
|
12
|
-
DropdownMenuTrigger
|
|
13
|
-
} from '@/components/ui/dropdown-menu'
|
|
14
|
-
|
|
15
|
-
export const DarkModeToggle: FC = () => {
|
|
16
|
-
const { setTheme } = useTheme()
|
|
17
|
-
|
|
18
|
-
return (
|
|
19
|
-
<DropdownMenu>
|
|
20
|
-
<DropdownMenuTrigger asChild>
|
|
21
|
-
<Button variant="ghost" size="icon">
|
|
22
|
-
<Icons.sun className="rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
|
|
23
|
-
<Icons.moon className="absolute rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
|
|
24
|
-
<span className="sr-only">Toggle theme</span>
|
|
25
|
-
</Button>
|
|
26
|
-
</DropdownMenuTrigger>
|
|
27
|
-
<DropdownMenuContent align="end">
|
|
28
|
-
<DropdownMenuItem onClick={() => setTheme('light')}>
|
|
29
|
-
<Icons.sun className="mr-2 h-4 w-4" />
|
|
30
|
-
<span>Light</span>
|
|
31
|
-
</DropdownMenuItem>
|
|
32
|
-
<DropdownMenuItem onClick={() => setTheme('dark')}>
|
|
33
|
-
<Icons.moon className="mr-2 h-4 w-4" />
|
|
34
|
-
<span>Dark</span>
|
|
35
|
-
</DropdownMenuItem>
|
|
36
|
-
<DropdownMenuItem onClick={() => setTheme('system')}>
|
|
37
|
-
<Icons.laptop className="mr-2 h-4 w-4" />
|
|
38
|
-
<span>System</span>
|
|
39
|
-
</DropdownMenuItem>
|
|
40
|
-
</DropdownMenuContent>
|
|
41
|
-
</DropdownMenu>
|
|
42
|
-
)
|
|
43
|
-
}
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import { type Column } from '@tanstack/react-table'
|
|
2
|
-
|
|
3
|
-
import { Icons } from '@/components/icons'
|
|
4
|
-
import { Button } from '@/components/ui/button'
|
|
5
|
-
import {
|
|
6
|
-
DropdownMenu,
|
|
7
|
-
DropdownMenuContent,
|
|
8
|
-
DropdownMenuItem,
|
|
9
|
-
DropdownMenuSeparator,
|
|
10
|
-
DropdownMenuTrigger
|
|
11
|
-
} from '@/components/ui/dropdown-menu'
|
|
12
|
-
import { cn } from '@/lib/cn'
|
|
13
|
-
|
|
14
|
-
interface DataTableColumnHeaderProps<TData, TValue>
|
|
15
|
-
extends React.HTMLAttributes<HTMLDivElement> {
|
|
16
|
-
column: Column<TData, TValue>
|
|
17
|
-
title: string
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function DataTableColumnHeader<TData, TValue>({
|
|
21
|
-
column,
|
|
22
|
-
title,
|
|
23
|
-
className
|
|
24
|
-
}: DataTableColumnHeaderProps<TData, TValue>) {
|
|
25
|
-
if (!column.getCanSort()) {
|
|
26
|
-
return <div className={cn(className)}>{title}</div>
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return (
|
|
30
|
-
<div className={cn('flex items-center space-x-2', className)}>
|
|
31
|
-
<DropdownMenu>
|
|
32
|
-
<DropdownMenuTrigger asChild>
|
|
33
|
-
<Button
|
|
34
|
-
aria-label={
|
|
35
|
-
column.getIsSorted() === 'desc'
|
|
36
|
-
? `Sorted descending. Click to sort ascending.`
|
|
37
|
-
: column.getIsSorted() === 'asc'
|
|
38
|
-
? `Sorted ascending. Click to sort descending.`
|
|
39
|
-
: `Not sorted. Click to sort ascending.`
|
|
40
|
-
}
|
|
41
|
-
variant="ghost"
|
|
42
|
-
size="sm"
|
|
43
|
-
className="-ml-3 h-8 data-[state=open]:bg-accent"
|
|
44
|
-
>
|
|
45
|
-
<span>{title}</span>
|
|
46
|
-
{column.getIsSorted() === 'desc' ? (
|
|
47
|
-
<Icons.arrowDown className="ml-2 h-4 w-4" aria-hidden="true" />
|
|
48
|
-
) : column.getIsSorted() === 'asc' ? (
|
|
49
|
-
<Icons.arrowUp className="ml-2 h-4 w-4" aria-hidden="true" />
|
|
50
|
-
) : (
|
|
51
|
-
<Icons.arrowUpDown className="ml-2 h-4 w-4" aria-hidden="true" />
|
|
52
|
-
)}
|
|
53
|
-
</Button>
|
|
54
|
-
</DropdownMenuTrigger>
|
|
55
|
-
<DropdownMenuContent align="start">
|
|
56
|
-
<DropdownMenuItem
|
|
57
|
-
aria-label="Sort ascending"
|
|
58
|
-
onClick={() => column.toggleSorting(false)}
|
|
59
|
-
>
|
|
60
|
-
<Icons.arrowUp
|
|
61
|
-
className="mr-2 h-3.5 w-3.5 text-muted-foreground/70"
|
|
62
|
-
aria-hidden="true"
|
|
63
|
-
/>
|
|
64
|
-
Asc
|
|
65
|
-
</DropdownMenuItem>
|
|
66
|
-
<DropdownMenuItem
|
|
67
|
-
aria-label="Sort descending"
|
|
68
|
-
onClick={() => column.toggleSorting(true)}
|
|
69
|
-
>
|
|
70
|
-
<Icons.arrowDown
|
|
71
|
-
className="mr-2 h-3.5 w-3.5 text-muted-foreground/70"
|
|
72
|
-
aria-hidden="true"
|
|
73
|
-
/>
|
|
74
|
-
Desc
|
|
75
|
-
</DropdownMenuItem>
|
|
76
|
-
<DropdownMenuSeparator />
|
|
77
|
-
<DropdownMenuItem
|
|
78
|
-
aria-label="Hide column"
|
|
79
|
-
onClick={() => column.toggleVisibility(false)}
|
|
80
|
-
>
|
|
81
|
-
<Icons.eyeOff
|
|
82
|
-
className="mr-2 h-3.5 w-3.5 text-muted-foreground/70"
|
|
83
|
-
aria-hidden="true"
|
|
84
|
-
/>
|
|
85
|
-
Hide
|
|
86
|
-
</DropdownMenuItem>
|
|
87
|
-
</DropdownMenuContent>
|
|
88
|
-
</DropdownMenu>
|
|
89
|
-
</div>
|
|
90
|
-
)
|
|
91
|
-
}
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
import { type Column } from '@tanstack/react-table'
|
|
2
|
-
import { ComponentType } from 'react'
|
|
3
|
-
|
|
4
|
-
import { Icons } from '@/components/icons'
|
|
5
|
-
import { Badge } from '@/components/ui/badge'
|
|
6
|
-
import { Button } from '@/components/ui/button'
|
|
7
|
-
import {
|
|
8
|
-
Command,
|
|
9
|
-
CommandEmpty,
|
|
10
|
-
CommandGroup,
|
|
11
|
-
CommandInput,
|
|
12
|
-
CommandItem,
|
|
13
|
-
CommandList,
|
|
14
|
-
CommandSeparator
|
|
15
|
-
} from '@/components/ui/command'
|
|
16
|
-
import {
|
|
17
|
-
Popover,
|
|
18
|
-
PopoverContent,
|
|
19
|
-
PopoverTrigger
|
|
20
|
-
} from '@/components/ui/popover'
|
|
21
|
-
import { Separator } from '@/components/ui/separator'
|
|
22
|
-
import { cn } from '@/lib/cn'
|
|
23
|
-
|
|
24
|
-
export type FilterOption = {
|
|
25
|
-
label: string
|
|
26
|
-
value: string
|
|
27
|
-
icon?: ComponentType<{ className?: string }>
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
interface DataTableFacetedFilter<TData, TValue> {
|
|
31
|
-
column?: Column<TData, TValue>
|
|
32
|
-
title?: string
|
|
33
|
-
options: FilterOption[]
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function DataTableFacetedFilter<TData, TValue>({
|
|
37
|
-
column,
|
|
38
|
-
title,
|
|
39
|
-
options
|
|
40
|
-
}: DataTableFacetedFilter<TData, TValue>) {
|
|
41
|
-
const selectedValues = new Set(column?.getFilterValue() as string[])
|
|
42
|
-
|
|
43
|
-
return (
|
|
44
|
-
<Popover>
|
|
45
|
-
<PopoverTrigger asChild>
|
|
46
|
-
<Button variant="outline" size="sm" className="h-8 border-dashed">
|
|
47
|
-
<Icons.plusCircle className="mr-2 h-4 w-4" />
|
|
48
|
-
{title}
|
|
49
|
-
{selectedValues?.size > 0 && (
|
|
50
|
-
<>
|
|
51
|
-
<Separator orientation="vertical" className="mx-2 h-4" />
|
|
52
|
-
<Badge
|
|
53
|
-
variant="secondary"
|
|
54
|
-
className="rounded-sm px-1 font-normal lg:hidden"
|
|
55
|
-
>
|
|
56
|
-
{selectedValues.size}
|
|
57
|
-
</Badge>
|
|
58
|
-
<div className="hidden space-x-1 lg:flex">
|
|
59
|
-
{selectedValues.size > 2 ? (
|
|
60
|
-
<Badge
|
|
61
|
-
variant="secondary"
|
|
62
|
-
className="rounded-sm px-1 font-normal"
|
|
63
|
-
>
|
|
64
|
-
{selectedValues.size} selected
|
|
65
|
-
</Badge>
|
|
66
|
-
) : (
|
|
67
|
-
options
|
|
68
|
-
.filter(option => selectedValues.has(option.value))
|
|
69
|
-
.map(option => (
|
|
70
|
-
<Badge
|
|
71
|
-
variant="secondary"
|
|
72
|
-
key={option.value}
|
|
73
|
-
className="rounded-sm px-1 font-normal"
|
|
74
|
-
>
|
|
75
|
-
{option.label}
|
|
76
|
-
</Badge>
|
|
77
|
-
))
|
|
78
|
-
)}
|
|
79
|
-
</div>
|
|
80
|
-
</>
|
|
81
|
-
)}
|
|
82
|
-
</Button>
|
|
83
|
-
</PopoverTrigger>
|
|
84
|
-
<PopoverContent className="w-[200px] p-0" align="start">
|
|
85
|
-
<Command>
|
|
86
|
-
<CommandInput placeholder={title} />
|
|
87
|
-
<CommandList>
|
|
88
|
-
<CommandEmpty>No results found.</CommandEmpty>
|
|
89
|
-
<CommandGroup>
|
|
90
|
-
{options.map(option => {
|
|
91
|
-
const isSelected = selectedValues.has(option.value)
|
|
92
|
-
return (
|
|
93
|
-
<CommandItem
|
|
94
|
-
key={option.value}
|
|
95
|
-
onSelect={() => {
|
|
96
|
-
if (isSelected) {
|
|
97
|
-
selectedValues.delete(option.value)
|
|
98
|
-
} else {
|
|
99
|
-
selectedValues.add(option.value)
|
|
100
|
-
}
|
|
101
|
-
const filterValues = Array.from(selectedValues)
|
|
102
|
-
column?.setFilterValue(
|
|
103
|
-
filterValues.length ? filterValues : undefined
|
|
104
|
-
)
|
|
105
|
-
}}
|
|
106
|
-
>
|
|
107
|
-
<div
|
|
108
|
-
className={cn(
|
|
109
|
-
'mr-2 flex h-4 w-4 items-center justify-center rounded-sm border border-primary',
|
|
110
|
-
isSelected
|
|
111
|
-
? 'bg-primary text-primary-foreground'
|
|
112
|
-
: 'opacity-50 [&_svg]:invisible'
|
|
113
|
-
)}
|
|
114
|
-
>
|
|
115
|
-
<Icons.check
|
|
116
|
-
className={cn('h-4 w-4')}
|
|
117
|
-
aria-hidden="true"
|
|
118
|
-
/>
|
|
119
|
-
</div>
|
|
120
|
-
{option.icon && (
|
|
121
|
-
<option.icon
|
|
122
|
-
className="mr-2 h-4 w-4 text-muted-foreground"
|
|
123
|
-
aria-hidden="true"
|
|
124
|
-
/>
|
|
125
|
-
)}
|
|
126
|
-
<span>{option.label}</span>
|
|
127
|
-
</CommandItem>
|
|
128
|
-
)
|
|
129
|
-
})}
|
|
130
|
-
</CommandGroup>
|
|
131
|
-
{selectedValues.size > 0 && (
|
|
132
|
-
<>
|
|
133
|
-
<CommandSeparator />
|
|
134
|
-
<CommandGroup>
|
|
135
|
-
<CommandItem
|
|
136
|
-
onSelect={() => column?.setFilterValue(undefined)}
|
|
137
|
-
className="justify-center text-center"
|
|
138
|
-
>
|
|
139
|
-
Clear filters
|
|
140
|
-
</CommandItem>
|
|
141
|
-
</CommandGroup>
|
|
142
|
-
</>
|
|
143
|
-
)}
|
|
144
|
-
</CommandList>
|
|
145
|
-
</Command>
|
|
146
|
-
</PopoverContent>
|
|
147
|
-
</Popover>
|
|
148
|
-
)
|
|
149
|
-
}
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { Skeleton } from '@/components/ui/skeleton'
|
|
2
|
-
import {
|
|
3
|
-
Table,
|
|
4
|
-
TableBody,
|
|
5
|
-
TableCell,
|
|
6
|
-
TableHead,
|
|
7
|
-
TableHeader,
|
|
8
|
-
TableRow
|
|
9
|
-
} from '@/components/ui/table'
|
|
10
|
-
|
|
11
|
-
interface DataTableLoadingProps {
|
|
12
|
-
columnCount: number
|
|
13
|
-
rowCount?: number
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function DataTableLoading({
|
|
17
|
-
columnCount,
|
|
18
|
-
rowCount = 10
|
|
19
|
-
}: DataTableLoadingProps) {
|
|
20
|
-
return (
|
|
21
|
-
<div className="w-full space-y-3 overflow-auto">
|
|
22
|
-
<div className="flex w-full items-center justify-between space-x-2 overflow-auto p-1">
|
|
23
|
-
<div className="flex flex-1 items-center space-x-2">
|
|
24
|
-
<Skeleton className="h-7 w-[150px] lg:w-[250px]" />
|
|
25
|
-
<Skeleton className="h-7 w-[70px] border-dashed" />
|
|
26
|
-
</div>
|
|
27
|
-
<Skeleton className="ml-auto hidden h-7 w-[70px] lg:flex" />
|
|
28
|
-
</div>
|
|
29
|
-
<div className="rounded-md border">
|
|
30
|
-
<Table>
|
|
31
|
-
<TableHeader>
|
|
32
|
-
{Array.from({ length: 1 }).map((_, i) => (
|
|
33
|
-
<TableRow key={i} className="hover:bg-transparent">
|
|
34
|
-
{Array.from({ length: columnCount }).map((_, i) => (
|
|
35
|
-
<TableHead key={i}>
|
|
36
|
-
<Skeleton className="h-6 w-full" />
|
|
37
|
-
</TableHead>
|
|
38
|
-
))}
|
|
39
|
-
</TableRow>
|
|
40
|
-
))}
|
|
41
|
-
</TableHeader>
|
|
42
|
-
<TableBody>
|
|
43
|
-
{Array.from({ length: rowCount }).map((_, i) => (
|
|
44
|
-
<TableRow key={i} className="hover:bg-transparent">
|
|
45
|
-
{Array.from({ length: columnCount }).map((_, i) => (
|
|
46
|
-
<TableCell key={i}>
|
|
47
|
-
<Skeleton className="h-6 w-full" />
|
|
48
|
-
</TableCell>
|
|
49
|
-
))}
|
|
50
|
-
</TableRow>
|
|
51
|
-
))}
|
|
52
|
-
</TableBody>
|
|
53
|
-
</Table>
|
|
54
|
-
</div>
|
|
55
|
-
<div className="flex w-full flex-col items-center justify-between gap-4 overflow-auto px-2 py-1 sm:flex-row sm:gap-8">
|
|
56
|
-
<div className="flex-1">
|
|
57
|
-
<Skeleton className="h-8 w-40" />
|
|
58
|
-
</div>
|
|
59
|
-
<div className="flex flex-col items-center gap-4 sm:flex-row sm:gap-6 lg:gap-8">
|
|
60
|
-
<div className="flex items-center space-x-2">
|
|
61
|
-
<Skeleton className="h-8 w-24" />
|
|
62
|
-
<Skeleton className="h-8 w-[70px]" />
|
|
63
|
-
</div>
|
|
64
|
-
<div className="flex w-[100px] items-center justify-center text-sm font-medium">
|
|
65
|
-
<Skeleton className="h-8 w-20" />
|
|
66
|
-
</div>
|
|
67
|
-
<div className="flex items-center space-x-2">
|
|
68
|
-
<Skeleton className="hidden h-8 w-8 lg:block" />
|
|
69
|
-
<Skeleton className="h-8 w-8" />
|
|
70
|
-
<Skeleton className="h-8 w-8" />
|
|
71
|
-
<Skeleton className="hidden h-8 w-8 lg:block" />
|
|
72
|
-
</div>
|
|
73
|
-
</div>
|
|
74
|
-
</div>
|
|
75
|
-
</div>
|
|
76
|
-
)
|
|
77
|
-
}
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { type Table } from '@tanstack/react-table'
|
|
2
|
-
|
|
3
|
-
import { Icons } from '@/components/icons'
|
|
4
|
-
import { Button } from '@/components/ui/button'
|
|
5
|
-
import {
|
|
6
|
-
Select,
|
|
7
|
-
SelectContent,
|
|
8
|
-
SelectItem,
|
|
9
|
-
SelectTrigger,
|
|
10
|
-
SelectValue
|
|
11
|
-
} from '@/components/ui/select'
|
|
12
|
-
|
|
13
|
-
interface DataTablePaginationProps<TData> {
|
|
14
|
-
table: Table<TData>
|
|
15
|
-
pageSizeOptions?: number[]
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function DataTablePagination<TData>({
|
|
19
|
-
table,
|
|
20
|
-
pageSizeOptions = [10, 20, 30, 50]
|
|
21
|
-
}: DataTablePaginationProps<TData>) {
|
|
22
|
-
return (
|
|
23
|
-
<div className="flex w-full flex-col items-center justify-between gap-4 overflow-auto px-2 py-1 sm:flex-row sm:gap-8">
|
|
24
|
-
<div className="flex-1 whitespace-nowrap text-sm text-muted-foreground">
|
|
25
|
-
{table.getFilteredSelectedRowModel().rows.length} of{' '}
|
|
26
|
-
{table.getFilteredRowModel().rows.length} row(s) selected.
|
|
27
|
-
</div>
|
|
28
|
-
<div className="flex flex-col items-center gap-4 sm:flex-row sm:gap-6 lg:gap-8">
|
|
29
|
-
<div className="flex items-center space-x-2">
|
|
30
|
-
<p className="whitespace-nowrap text-sm font-medium">Rows per page</p>
|
|
31
|
-
<Select
|
|
32
|
-
value={`${table.getState().pagination.pageSize}`}
|
|
33
|
-
onValueChange={value => {
|
|
34
|
-
table.setPageSize(Number(value))
|
|
35
|
-
}}
|
|
36
|
-
>
|
|
37
|
-
<SelectTrigger className="h-8 w-[70px]">
|
|
38
|
-
<SelectValue placeholder={table.getState().pagination.pageSize} />
|
|
39
|
-
</SelectTrigger>
|
|
40
|
-
<SelectContent side="top">
|
|
41
|
-
{pageSizeOptions.map(pageSize => (
|
|
42
|
-
<SelectItem key={pageSize} value={`${pageSize}`}>
|
|
43
|
-
{pageSize}
|
|
44
|
-
</SelectItem>
|
|
45
|
-
))}
|
|
46
|
-
</SelectContent>
|
|
47
|
-
</Select>
|
|
48
|
-
</div>
|
|
49
|
-
<div className="flex w-[100px] items-center justify-center text-sm font-medium">
|
|
50
|
-
Page {table.getState().pagination.pageIndex + 1} of{' '}
|
|
51
|
-
{table.getPageCount()}
|
|
52
|
-
</div>
|
|
53
|
-
<div className="flex items-center space-x-2">
|
|
54
|
-
<Button
|
|
55
|
-
aria-label="Go to first page"
|
|
56
|
-
variant="outline"
|
|
57
|
-
className="hidden h-8 w-8 p-0 lg:flex"
|
|
58
|
-
onClick={() => table.setPageIndex(0)}
|
|
59
|
-
disabled={!table.getCanPreviousPage()}
|
|
60
|
-
>
|
|
61
|
-
<Icons.chevronsLeft className="h-4 w-4" aria-hidden="true" />
|
|
62
|
-
</Button>
|
|
63
|
-
<Button
|
|
64
|
-
aria-label="Go to previous page"
|
|
65
|
-
variant="outline"
|
|
66
|
-
className="h-8 w-8 p-0"
|
|
67
|
-
onClick={() => table.previousPage()}
|
|
68
|
-
disabled={!table.getCanPreviousPage()}
|
|
69
|
-
>
|
|
70
|
-
<Icons.chevronLeft className="h-4 w-4" aria-hidden="true" />
|
|
71
|
-
</Button>
|
|
72
|
-
<Button
|
|
73
|
-
aria-label="Go to next page"
|
|
74
|
-
variant="outline"
|
|
75
|
-
className="h-8 w-8 p-0"
|
|
76
|
-
onClick={() => table.nextPage()}
|
|
77
|
-
disabled={!table.getCanNextPage()}
|
|
78
|
-
>
|
|
79
|
-
<Icons.chevronRight className="h-4 w-4" aria-hidden="true" />
|
|
80
|
-
</Button>
|
|
81
|
-
<Button
|
|
82
|
-
aria-label="Go to last page"
|
|
83
|
-
variant="outline"
|
|
84
|
-
className="hidden h-8 w-8 p-0 lg:flex"
|
|
85
|
-
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
|
|
86
|
-
disabled={!table.getCanNextPage()}
|
|
87
|
-
>
|
|
88
|
-
<Icons.chevronsRight className="h-4 w-4" aria-hidden="true" />
|
|
89
|
-
</Button>
|
|
90
|
-
</div>
|
|
91
|
-
</div>
|
|
92
|
-
</div>
|
|
93
|
-
)
|
|
94
|
-
}
|