create-ej-app 11.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.
Files changed (141) hide show
  1. package/cli.js +5 -4
  2. package/cli.ts +9 -4
  3. package/package.json +1 -1
  4. package/template/base/package.json +14 -20
  5. package/template/base/src/app/not-found.tsx +1 -7
  6. package/template/base/terraform/artifact-registry.tf +5 -0
  7. package/template/base/terraform/backend-service.tf +13 -0
  8. package/template/base/terraform/cleanup.sh +9 -0
  9. package/template/base/terraform/cloudbuild.tf +70 -0
  10. package/template/base/terraform/cloudrun.tf +68 -0
  11. package/template/base/terraform/database.tf +60 -0
  12. package/template/base/terraform/forwarding-rule.tf +43 -0
  13. package/template/base/terraform/ip-address.tf +11 -0
  14. package/template/base/terraform/network-endpoint-group.tf +11 -0
  15. package/template/base/terraform/network.tf +21 -0
  16. package/template/base/terraform/pre.sh +3 -0
  17. package/template/base/terraform/providers.tf +18 -0
  18. package/template/base/terraform/secret.txt +11 -0
  19. package/template/base/terraform/setup.sh +25 -0
  20. package/template/base/terraform/ssl-certs.tf +32 -0
  21. package/template/base/terraform/target-https-proxy copy.tf +14 -0
  22. package/template/base/terraform/target-https-proxy.tf +14 -0
  23. package/template/base/terraform/terraform.tfvars +4 -0
  24. package/template/base/terraform/url-map.tf +23 -0
  25. package/template/base/terraform/variables.tf +21 -0
  26. package/template/old/.env.example +0 -17
  27. package/template/old/.eslintrc.cjs +0 -26
  28. package/template/old/components.json +0 -17
  29. package/template/old/drizzle.config.ts +0 -11
  30. package/template/old/next.config.mjs +0 -4
  31. package/template/old/package.json +0 -87
  32. package/template/old/postcss.config.mjs +0 -8
  33. package/template/old/prettier.config.mjs +0 -11
  34. package/template/old/public/next.svg +0 -1
  35. package/template/old/public/vercel.svg +0 -1
  36. package/template/old/src/app/(app)/dashboard/layout.tsx +0 -14
  37. package/template/old/src/app/(app)/dashboard/page.tsx +0 -12
  38. package/template/old/src/app/(app)/layout.tsx +0 -125
  39. package/template/old/src/app/(app)/roles/[id]/actions.ts +0 -59
  40. package/template/old/src/app/(app)/roles/[id]/page.tsx +0 -46
  41. package/template/old/src/app/(app)/roles/[id]/render.tsx +0 -264
  42. package/template/old/src/app/(app)/roles/[id]/validation.ts +0 -22
  43. package/template/old/src/app/(app)/roles/actions.ts +0 -33
  44. package/template/old/src/app/(app)/roles/page.tsx +0 -52
  45. package/template/old/src/app/(app)/roles/render.tsx +0 -80
  46. package/template/old/src/app/(app)/settings/actions.ts +0 -17
  47. package/template/old/src/app/(app)/settings/layout.tsx +0 -14
  48. package/template/old/src/app/(app)/settings/loading.tsx +0 -28
  49. package/template/old/src/app/(app)/settings/page.tsx +0 -19
  50. package/template/old/src/app/(app)/settings/render.tsx +0 -107
  51. package/template/old/src/app/(app)/settings/validation.ts +0 -5
  52. package/template/old/src/app/(app)/tasks/[id]/actions.ts +0 -92
  53. package/template/old/src/app/(app)/tasks/[id]/page.tsx +0 -59
  54. package/template/old/src/app/(app)/tasks/[id]/render.tsx +0 -482
  55. package/template/old/src/app/(app)/tasks/[id]/validation.ts +0 -18
  56. package/template/old/src/app/(app)/tasks/actions.ts +0 -39
  57. package/template/old/src/app/(app)/tasks/page.tsx +0 -59
  58. package/template/old/src/app/(app)/tasks/render.tsx +0 -129
  59. package/template/old/src/app/(app)/tasks/statusEnum.ts +0 -3
  60. package/template/old/src/app/(app)/users/[id]/actions.ts +0 -68
  61. package/template/old/src/app/(app)/users/[id]/page.tsx +0 -39
  62. package/template/old/src/app/(app)/users/[id]/render.tsx +0 -325
  63. package/template/old/src/app/(app)/users/[id]/validation.ts +0 -18
  64. package/template/old/src/app/(app)/users/actions.ts +0 -34
  65. package/template/old/src/app/(app)/users/page.tsx +0 -62
  66. package/template/old/src/app/(app)/users/render.tsx +0 -124
  67. package/template/old/src/app/(auth)/layout.tsx +0 -27
  68. package/template/old/src/app/(auth)/login/page.tsx +0 -49
  69. package/template/old/src/app/(auth)/login/render.tsx +0 -146
  70. package/template/old/src/app/(marketing)/layout.tsx +0 -42
  71. package/template/old/src/app/(marketing)/page.tsx +0 -97
  72. package/template/old/src/app/api/auth/[...nextauth]/route.ts +0 -7
  73. package/template/old/src/app/favicon.ico +0 -0
  74. package/template/old/src/app/globals.css +0 -76
  75. package/template/old/src/app/layout.tsx +0 -31
  76. package/template/old/src/components/app-nav-command.tsx +0 -178
  77. package/template/old/src/components/dark-mode-toggle.tsx +0 -43
  78. package/template/old/src/components/data-table/data-table-column-header.tsx +0 -91
  79. package/template/old/src/components/data-table/data-table-faceted-filter.tsx +0 -149
  80. package/template/old/src/components/data-table/data-table-loading.tsx +0 -77
  81. package/template/old/src/components/data-table/data-table-pagination.tsx +0 -94
  82. package/template/old/src/components/data-table/data-table-toolbar.tsx +0 -78
  83. package/template/old/src/components/data-table/data-table-types.ts +0 -17
  84. package/template/old/src/components/data-table/data-table-view-options.tsx +0 -59
  85. package/template/old/src/components/data-table/data-table.tsx +0 -301
  86. package/template/old/src/components/data-table/simple-data-table.tsx +0 -109
  87. package/template/old/src/components/empty-placeholder.tsx +0 -78
  88. package/template/old/src/components/heading.tsx +0 -17
  89. package/template/old/src/components/icons.tsx +0 -101
  90. package/template/old/src/components/main-nav.tsx +0 -64
  91. package/template/old/src/components/mobile-nav.tsx +0 -50
  92. package/template/old/src/components/shell.tsx +0 -13
  93. package/template/old/src/components/side-nav.tsx +0 -51
  94. package/template/old/src/components/site-footer.tsx +0 -31
  95. package/template/old/src/components/system-info.tsx +0 -36
  96. package/template/old/src/components/ui/alert-dialog.tsx +0 -141
  97. package/template/old/src/components/ui/avatar.tsx +0 -50
  98. package/template/old/src/components/ui/badge.tsx +0 -36
  99. package/template/old/src/components/ui/button.tsx +0 -56
  100. package/template/old/src/components/ui/card.tsx +0 -79
  101. package/template/old/src/components/ui/checkbox.tsx +0 -30
  102. package/template/old/src/components/ui/command.tsx +0 -155
  103. package/template/old/src/components/ui/dialog.tsx +0 -122
  104. package/template/old/src/components/ui/dropdown-menu.tsx +0 -200
  105. package/template/old/src/components/ui/form.tsx +0 -176
  106. package/template/old/src/components/ui/input.tsx +0 -25
  107. package/template/old/src/components/ui/label.tsx +0 -26
  108. package/template/old/src/components/ui/popover.tsx +0 -31
  109. package/template/old/src/components/ui/select.tsx +0 -160
  110. package/template/old/src/components/ui/separator.tsx +0 -31
  111. package/template/old/src/components/ui/skeleton.tsx +0 -15
  112. package/template/old/src/components/ui/sonner.tsx +0 -31
  113. package/template/old/src/components/ui/table.tsx +0 -117
  114. package/template/old/src/components/user-account-nav.tsx +0 -60
  115. package/template/old/src/components/user-avatar.tsx +0 -27
  116. package/template/old/src/data-access/resource.ts +0 -85
  117. package/template/old/src/data-access/role-permission.ts +0 -45
  118. package/template/old/src/data-access/role.ts +0 -105
  119. package/template/old/src/data-access/task.ts +0 -141
  120. package/template/old/src/data-access/user.ts +0 -141
  121. package/template/old/src/db/index.ts +0 -14
  122. package/template/old/src/db/schema.ts +0 -287
  123. package/template/old/src/env.mjs +0 -58
  124. package/template/old/src/hooks/use-debounce.tsx +0 -17
  125. package/template/old/src/hooks/use-lock-body.tsx +0 -13
  126. package/template/old/src/lib/auth.ts +0 -89
  127. package/template/old/src/lib/cn.ts +0 -4
  128. package/template/old/src/lib/currentUser.ts +0 -8
  129. package/template/old/src/lib/delay.ts +0 -2
  130. package/template/old/src/lib/formatDate.ts +0 -11
  131. package/template/old/src/lib/generateLabel.ts +0 -5
  132. package/template/old/src/lib/s3Client.ts +0 -156
  133. package/template/old/src/lib/safe-action.ts +0 -19
  134. package/template/old/src/lib/siteConfig.ts +0 -4
  135. package/template/old/src/lib/sortOrderEnum.ts +0 -2
  136. package/template/old/src/use-case/role-permission.ts +0 -19
  137. package/template/old/src/use-case/role.ts +0 -110
  138. package/template/old/src/use-case/task.ts +0 -166
  139. package/template/old/src/use-case/user.ts +0 -121
  140. package/template/old/tailwind.config.ts +0 -81
  141. package/template/old/tsconfig.json +0 -47
@@ -1,59 +0,0 @@
1
- import { Metadata } from 'next'
2
- import { notFound } from 'next/navigation'
3
-
4
- import { authGuard, getAuthSession } from '@/lib/auth'
5
- import { siteConfig } from '@/lib/siteConfig'
6
- import { getTasksAction } from './actions'
7
- import { Render } from './render'
8
-
9
- export const metadata: Metadata = {
10
- title: 'Tasks' + ' | ' + siteConfig.name
11
- }
12
-
13
- const TasksPage = async ({
14
- searchParams
15
- }: {
16
- searchParams: {
17
- page?: string
18
- limit?: string
19
- sort?: string
20
- title?: string
21
- status?: string
22
- }
23
- }) => {
24
- const session = await getAuthSession()
25
- if (!session) return notFound()
26
-
27
- const g = await authGuard(session, 'TaskList')
28
- if (!g) return notFound()
29
-
30
- const page = searchParams.page ? parseInt(searchParams.page) : 1
31
- const limit = searchParams.limit ? parseInt(searchParams.limit) : 10
32
-
33
- const [sortBy, sortOrder] =
34
- typeof searchParams.sort === 'string'
35
- ? (searchParams.sort.split('.') as [any, 'asc' | 'desc' | undefined])
36
- : []
37
-
38
- const title = searchParams.title
39
-
40
- const status =
41
- typeof searchParams.status === 'string'
42
- ? (searchParams.status.split('.') as any[])
43
- : undefined
44
-
45
- const tasks = await getTasksAction({
46
- page,
47
- limit,
48
- sortBy,
49
- sortOrder,
50
- title,
51
- status
52
- })
53
-
54
- if (!tasks?.data) throw new Error('Tasks data not found')
55
-
56
- return <Render data={tasks.data} />
57
- }
58
-
59
- export default TasksPage
@@ -1,129 +0,0 @@
1
- 'use client'
2
-
3
- import { InferSelectModel } from 'drizzle-orm'
4
- import Link from 'next/link'
5
- import { FC } from 'react'
6
-
7
- import { DataTable } from '@/components/data-table/data-table'
8
- import { DataTableColumnHeader } from '@/components/data-table/data-table-column-header'
9
- import { Heading } from '@/components/heading'
10
- import { Icons } from '@/components/icons'
11
- import { Shell } from '@/components/shell'
12
- import { Button } from '@/components/ui/button'
13
- import { Task } from '@/db/schema'
14
- import { formatDateTime, timesAgo } from '@/lib/formatDate'
15
- import { generateLabel } from '@/lib/generateLabel'
16
- import { TaskStatusEnumArr } from './statusEnum'
17
-
18
- const statuses = TaskStatusEnumArr.map(x => ({
19
- label: generateLabel(x),
20
- value: x
21
- }))
22
-
23
- export const Render: FC<{
24
- data: {
25
- tasks: {
26
- id: string
27
- title: string
28
- status: InferSelectModel<typeof Task>['status']
29
- createdBy: {
30
- id: string
31
- name?: string | null
32
- }
33
- updatedBy?: {
34
- id: string
35
- name?: string | null
36
- } | null
37
- createdAt: Date
38
- updatedAt?: Date | null
39
- }[]
40
- total: number
41
- }
42
- }> = ({ data }) => {
43
- return (
44
- <Shell>
45
- <Heading heading="Tasks" text="List of all the tasks">
46
- <Link href="/tasks/new">
47
- <Button>
48
- <Icons.add className="mr-2 h-4 w-4" />
49
- New
50
- </Button>
51
- </Link>
52
- </Heading>
53
- <DataTable
54
- columns={[
55
- {
56
- accessorKey: 'title',
57
- header: ({ column }) => (
58
- <DataTableColumnHeader column={column} title="Title" />
59
- ),
60
- cell: ({ row }) => (
61
- <Link
62
- className="underline underline-offset-4"
63
- href={`/tasks/${row.original.id}`}
64
- >
65
- {row.original.title}
66
- </Link>
67
- )
68
- },
69
- {
70
- accessorKey: 'status',
71
- header: ({ column }) => (
72
- <DataTableColumnHeader column={column} title="Status" />
73
- ),
74
- cell: ({ row }) => row.original.status.replace('_', ' ')
75
- },
76
- {
77
- accessorKey: 'createdBy',
78
- header: ({ column }) => (
79
- <DataTableColumnHeader column={column} title="Created By" />
80
- ),
81
- cell: ({ row }) => row.original.createdBy.name,
82
- enableSorting: false
83
- },
84
- {
85
- accessorKey: 'updatedBy',
86
- header: ({ column }) => (
87
- <DataTableColumnHeader column={column} title="Updated By" />
88
- ),
89
- cell: ({ row }) =>
90
- row.original.updatedBy ? row.original.updatedBy.name : undefined,
91
- enableSorting: false
92
- },
93
- {
94
- accessorKey: 'createdAt',
95
- header: ({ column }) => (
96
- <DataTableColumnHeader column={column} title="Created At" />
97
- ),
98
- cell: ({ row }) => formatDateTime(row.original.createdAt)
99
- },
100
- {
101
- accessorKey: 'updatedAt',
102
- header: ({ column }) => (
103
- <DataTableColumnHeader column={column} title="Updated At" />
104
- ),
105
- cell: ({ row }) =>
106
- row.original.updatedAt
107
- ? timesAgo(row.original.updatedAt)
108
- : undefined
109
- }
110
- ]}
111
- data={data.tasks}
112
- total={data.total}
113
- searchableColumns={[
114
- {
115
- id: 'title',
116
- title: 'Title'
117
- }
118
- ]}
119
- filterableColumns={[
120
- {
121
- id: 'status',
122
- title: 'Status',
123
- options: statuses
124
- }
125
- ]}
126
- />
127
- </Shell>
128
- )
129
- }
@@ -1,3 +0,0 @@
1
- import { Task } from '@/db/schema'
2
-
3
- export const TaskStatusEnumArr = Task.status.enumValues
@@ -1,68 +0,0 @@
1
- 'use server'
2
-
3
- import { revalidatePath } from 'next/cache'
4
- import { z } from 'zod'
5
-
6
- import { authActionClient } from '@/lib/safe-action'
7
- import { getRolesMiniUseCase } from '@/use-case/role'
8
- import {
9
- createUserUseCase,
10
- deleteUserUseCase,
11
- getUserByIdUseCase,
12
- updateUserUseCase
13
- } from '@/use-case/user'
14
- import { UserCreateUpdateSchema, UserUpdateServerSchema } from './validation'
15
-
16
- export const getUserAction = authActionClient
17
- .schema(z.string())
18
- .action(async ({ parsedInput: id, ctx }) => {
19
- return await getUserByIdUseCase(ctx.user.type, id)
20
- })
21
-
22
- export const createUserAction = authActionClient
23
- .schema(UserCreateUpdateSchema)
24
- .action(async ({ parsedInput, ctx }) => {
25
- const id = await createUserUseCase(ctx.user.type, parsedInput)
26
-
27
- revalidatePath('/users')
28
- revalidatePath(`/users/${id}`)
29
-
30
- return {
31
- success: true,
32
- id
33
- }
34
- })
35
-
36
- export const updateUserAction = authActionClient
37
- .schema(UserUpdateServerSchema)
38
- .action(async ({ parsedInput: { id, name, type, roleId }, ctx }) => {
39
- await updateUserUseCase(ctx.user.type, id, {
40
- name,
41
- type,
42
- roleId
43
- })
44
-
45
- revalidatePath('/users')
46
- revalidatePath(`/users/${id}`)
47
-
48
- return {
49
- success: true
50
- }
51
- })
52
-
53
- export const deleteUserAction = authActionClient
54
- .schema(z.string())
55
- .action(async ({ parsedInput: id, ctx }) => {
56
- await deleteUserUseCase(ctx.user.type, id)
57
-
58
- revalidatePath('/users')
59
- revalidatePath(`/users/${id}`)
60
-
61
- return { success: true }
62
- })
63
-
64
- export const getRolesMiniAction = authActionClient
65
- .schema(z.undefined())
66
- .action(async ({ ctx }) => {
67
- return await getRolesMiniUseCase(ctx.user.type)
68
- })
@@ -1,39 +0,0 @@
1
- import { Metadata } from 'next'
2
- import { notFound } from 'next/navigation'
3
-
4
- import { authGuard, getAuthSession } from '@/lib/auth'
5
- import { siteConfig } from '@/lib/siteConfig'
6
- import { getRolesMiniAction, getUserAction } from './actions'
7
- import { Render } from './render'
8
-
9
- export const generateMetadata = async (props: {
10
- params: { id: string }
11
- }): Promise<Metadata> => ({
12
- title: 'User - ' + props.params.id + ' | ' + siteConfig.name
13
- })
14
-
15
- const UserPage = async ({
16
- params: { id }
17
- }: {
18
- params: {
19
- id: string
20
- }
21
- }) => {
22
- const session = await getAuthSession()
23
- if (!session) return notFound()
24
-
25
- const g = await authGuard(session)
26
- if (!g) return notFound()
27
-
28
- const roles = await getRolesMiniAction(undefined)
29
- if (!roles?.data) return notFound()
30
-
31
- if (id === 'new') return <Render roles={roles.data} />
32
-
33
- const user = await getUserAction(id)
34
- if (!user?.data) return notFound()
35
-
36
- return <Render user={user.data} roles={roles.data} />
37
- }
38
-
39
- export default UserPage
@@ -1,325 +0,0 @@
1
- 'use client'
2
-
3
- import { zodResolver } from '@hookform/resolvers/zod'
4
- import Link from 'next/link'
5
- import { useRouter } from 'next/navigation'
6
- import { FC, useState } from 'react'
7
- import { useForm } from 'react-hook-form'
8
- import { toast } from 'sonner'
9
- import { z } from 'zod'
10
-
11
- import { Heading } from '@/components/heading'
12
- import { Icons } from '@/components/icons'
13
- import { Shell } from '@/components/shell'
14
- import { SystemInfo } from '@/components/system-info'
15
- import {
16
- AlertDialog,
17
- AlertDialogAction,
18
- AlertDialogCancel,
19
- AlertDialogContent,
20
- AlertDialogDescription,
21
- AlertDialogFooter,
22
- AlertDialogHeader,
23
- AlertDialogTitle,
24
- AlertDialogTrigger
25
- } from '@/components/ui/alert-dialog'
26
- import { Button } from '@/components/ui/button'
27
- import {
28
- Form,
29
- FormControl,
30
- FormField,
31
- FormItem,
32
- FormLabel,
33
- FormMessage
34
- } from '@/components/ui/form'
35
- import { Input } from '@/components/ui/input'
36
- import {
37
- Select,
38
- SelectContent,
39
- SelectItem,
40
- SelectTrigger,
41
- SelectValue
42
- } from '@/components/ui/select'
43
- import { User } from '@/db/schema'
44
- import { formatDateTime, timesAgo } from '@/lib/formatDate'
45
- import { generateLabel } from '@/lib/generateLabel'
46
- import { InferSelectModel } from 'drizzle-orm'
47
- import { createUserAction, deleteUserAction, updateUserAction } from './actions'
48
- import { UserCreateUpdateSchema, UserTypeEnumArr } from './validation'
49
-
50
- type FormData = z.infer<typeof UserCreateUpdateSchema>
51
-
52
- const types = UserTypeEnumArr.map(x => ({
53
- label: generateLabel(x),
54
- value: x
55
- }))
56
-
57
- export const Render: FC<{
58
- user?: {
59
- id: string
60
- name?: string | null
61
- email: string
62
- type: InferSelectModel<typeof User>['type']
63
- roleId?: string | null
64
- createdAt: Date
65
- updatedAt?: Date | null
66
- } | null
67
- roles: {
68
- id: string
69
- name: string
70
- }[]
71
- }> = ({ user, roles }) => {
72
- const form = useForm<FormData>({
73
- resolver: zodResolver(UserCreateUpdateSchema),
74
- defaultValues: {
75
- name: user?.name ?? '',
76
- email: user?.email ?? '',
77
- type: user?.type ?? 'Normal',
78
- roleId: user?.roleId
79
- }
80
- })
81
- const router = useRouter()
82
- const [isSaving, setIsSaving] = useState(false)
83
- const [isDeleting, setIsDeleting] = useState(false)
84
-
85
- async function onSubmit(data: FormData) {
86
- setIsSaving(true)
87
- try {
88
- if (!user) {
89
- const res = await createUserAction(data)
90
- if (!res?.data?.success)
91
- throw new Error(res?.serverError || 'No success returned from server')
92
- if (!res?.data?.id) throw new Error('No id returned from server')
93
- router.replace(`/users/${res.data.id}`)
94
- } else {
95
- const res = await updateUserAction({
96
- id: user.id,
97
- ...data
98
- })
99
- if (!res?.data?.success)
100
- throw new Error(res?.serverError || 'No success returned from server')
101
- }
102
- toast('User saved')
103
- } catch (err) {
104
- toast('Error saving user', {
105
- description: (err as any).message || undefined
106
- })
107
- }
108
- setIsSaving(false)
109
- }
110
-
111
- return (
112
- <Shell>
113
- <Heading
114
- heading={user ? user.name || user.email || user.id : 'New User'}
115
- />
116
- <Form {...form}>
117
- <form
118
- className="grid grid-cols-1 gap-3 md:grid-cols-2"
119
- onSubmit={form.handleSubmit(onSubmit)}
120
- >
121
- <div className="col-span-1 mb-1 grid grid-cols-2 gap-2 sm:flex md:col-span-2">
122
- <Button type="button" onClick={() => router.back()} variant="ghost">
123
- <Icons.chevronLeft className="mr-2 h-4 w-4" />
124
- Back
125
- </Button>
126
- <Button type="submit" disabled={isSaving || isDeleting}>
127
- {isSaving ? (
128
- <Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
129
- ) : (
130
- <Icons.save className="mr-2 h-4 w-4" />
131
- )}
132
- <span>Save</span>
133
- </Button>
134
- {user ? (
135
- <AlertDialog>
136
- <AlertDialogTrigger asChild>
137
- <Button
138
- type="button"
139
- variant="destructive"
140
- disabled={isSaving || isDeleting}
141
- >
142
- {isDeleting ? (
143
- <Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
144
- ) : (
145
- <Icons.delete className="mr-2 h-4 w-4" />
146
- )}
147
- Delete
148
- </Button>
149
- </AlertDialogTrigger>
150
- <AlertDialogContent>
151
- <AlertDialogHeader>
152
- <AlertDialogTitle>
153
- Are you absolutely sure?
154
- </AlertDialogTitle>
155
- <AlertDialogDescription>
156
- This action cannot be undone.
157
- </AlertDialogDescription>
158
- </AlertDialogHeader>
159
- <AlertDialogFooter>
160
- <AlertDialogCancel>Cancel</AlertDialogCancel>
161
- <AlertDialogAction
162
- onClick={async () => {
163
- setIsDeleting(true)
164
- try {
165
- const res = await deleteUserAction(user.id)
166
- if (!res?.data?.success)
167
- throw new Error(
168
- res?.serverError ||
169
- 'No success returned from server'
170
- )
171
- toast('User deleted')
172
- router.replace('/users')
173
- } catch (err) {
174
- toast('Error deleting user', {
175
- description:
176
- (err as any).message || 'Something went wrong'
177
- })
178
- }
179
- setIsDeleting(false)
180
- }}
181
- >
182
- Continue
183
- </AlertDialogAction>
184
- </AlertDialogFooter>
185
- </AlertDialogContent>
186
- </AlertDialog>
187
- ) : undefined}
188
- {user ? (
189
- <Link href="/users/new">
190
- <Button
191
- type="button"
192
- disabled={isSaving || isDeleting}
193
- variant="secondary"
194
- className="w-full"
195
- >
196
- <Icons.add className="mr-2 h-4 w-4" />
197
- New
198
- </Button>
199
- </Link>
200
- ) : undefined}
201
- </div>
202
-
203
- <FormField
204
- control={form.control}
205
- name="name"
206
- render={({ field }) => (
207
- <FormItem>
208
- <FormLabel>Name</FormLabel>
209
- <FormControl>
210
- <Input
211
- type="text"
212
- placeholder="Enter a name"
213
- disabled={isSaving}
214
- {...field}
215
- />
216
- </FormControl>
217
- <FormMessage />
218
- </FormItem>
219
- )}
220
- />
221
-
222
- <FormField
223
- control={form.control}
224
- name="email"
225
- render={({ field }) => (
226
- <FormItem>
227
- <FormLabel>Email</FormLabel>
228
- <FormControl>
229
- <Input
230
- type="email"
231
- placeholder="Enter an email"
232
- autoCapitalize="none"
233
- autoComplete="email"
234
- autoCorrect="off"
235
- disabled={isSaving}
236
- {...field}
237
- />
238
- </FormControl>
239
- <FormMessage />
240
- </FormItem>
241
- )}
242
- />
243
-
244
- <FormField
245
- control={form.control}
246
- name="type"
247
- render={({ field }) => (
248
- <FormItem>
249
- <FormLabel>Type</FormLabel>
250
- <Select
251
- onValueChange={field.onChange}
252
- defaultValue={field.value}
253
- disabled={isSaving}
254
- >
255
- <FormControl>
256
- <SelectTrigger>
257
- <SelectValue placeholder="Select type" />
258
- </SelectTrigger>
259
- </FormControl>
260
- <SelectContent>
261
- {types.map(({ label, value }) => (
262
- <SelectItem key={value} value={value}>
263
- {label}
264
- </SelectItem>
265
- ))}
266
- </SelectContent>
267
- </Select>
268
- <FormMessage />
269
- </FormItem>
270
- )}
271
- />
272
-
273
- {form.getValues('type') === 'Normal' ? (
274
- <FormField
275
- control={form.control}
276
- name="roleId"
277
- render={({ field }) => (
278
- <FormItem>
279
- <FormLabel>Role</FormLabel>
280
- <Select
281
- onValueChange={field.onChange}
282
- defaultValue={field.value || undefined}
283
- disabled={isSaving}
284
- >
285
- <FormControl>
286
- <SelectTrigger>
287
- <SelectValue placeholder="Select role" />
288
- </SelectTrigger>
289
- </FormControl>
290
- <SelectContent>
291
- {roles.map(({ id, name }) => (
292
- <SelectItem key={id} value={id}>
293
- {name}
294
- </SelectItem>
295
- ))}
296
- </SelectContent>
297
- </Select>
298
- <FormMessage />
299
- </FormItem>
300
- )}
301
- />
302
- ) : undefined}
303
- </form>
304
- </Form>
305
- {user ? (
306
- <SystemInfo
307
- items={[
308
- {
309
- label: 'Id',
310
- value: user.id
311
- },
312
- {
313
- label: 'Created At',
314
- value: formatDateTime(user.createdAt)
315
- },
316
- {
317
- label: 'Updated At',
318
- value: user.updatedAt ? timesAgo(user.updatedAt) : '-'
319
- }
320
- ]}
321
- />
322
- ) : undefined}
323
- </Shell>
324
- )
325
- }
@@ -1,18 +0,0 @@
1
- import { z } from 'zod'
2
-
3
- import { User } from '@/db/schema'
4
-
5
- export const UserTypeEnumArr = User.type.enumValues
6
-
7
- export const UserCreateUpdateSchema = z.object({
8
- name: z.string().min(1).trim(),
9
- email: z.string().email().toLowerCase().trim(),
10
- type: z.enum(UserTypeEnumArr),
11
- roleId: z.string().optional().nullable()
12
- })
13
-
14
- export const UserUpdateServerSchema = UserCreateUpdateSchema.merge(
15
- z.object({
16
- id: z.string().min(1)
17
- })
18
- )
@@ -1,34 +0,0 @@
1
- 'use server'
2
-
3
- import { z } from 'zod'
4
-
5
- import { User } from '@/db/schema'
6
- import { authActionClient } from '@/lib/safe-action'
7
- import { SortOrderEnum } from '@/lib/sortOrderEnum'
8
- import { getUsersWithRoleUseCase } from '@/use-case/user'
9
-
10
- export const getUsersAction = authActionClient
11
- .schema(
12
- z.object({
13
- page: z.number().gte(1),
14
- limit: z.number().gte(1).lte(1000),
15
- sortBy: z.enum(['name']).optional(),
16
- sortOrder: z.enum(SortOrderEnum).optional(),
17
- name: z.string().optional(),
18
- email: z.string().optional(),
19
- type: z.array(z.enum(User.type.enumValues)).optional()
20
- })
21
- )
22
- .action(async ({ ctx, parsedInput }) => {
23
- return await getUsersWithRoleUseCase(ctx.user.type, {
24
- page: parsedInput.page,
25
- limit: parsedInput.limit,
26
- sortBy: parsedInput.sortBy,
27
- sortOrder: parsedInput.sortOrder,
28
- filters: {
29
- name: parsedInput.name,
30
- email: parsedInput.email,
31
- type: parsedInput.type
32
- }
33
- })
34
- })