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.
Files changed (140) 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 +9 -15
  5. package/template/base/terraform/artifact-registry.tf +5 -0
  6. package/template/base/terraform/backend-service.tf +13 -0
  7. package/template/base/terraform/cleanup.sh +9 -0
  8. package/template/base/terraform/cloudbuild.tf +70 -0
  9. package/template/base/terraform/cloudrun.tf +68 -0
  10. package/template/base/terraform/database.tf +60 -0
  11. package/template/base/terraform/forwarding-rule.tf +43 -0
  12. package/template/base/terraform/ip-address.tf +11 -0
  13. package/template/base/terraform/network-endpoint-group.tf +11 -0
  14. package/template/base/terraform/network.tf +21 -0
  15. package/template/base/terraform/pre.sh +3 -0
  16. package/template/base/terraform/providers.tf +18 -0
  17. package/template/base/terraform/secret.txt +11 -0
  18. package/template/base/terraform/setup.sh +25 -0
  19. package/template/base/terraform/ssl-certs.tf +32 -0
  20. package/template/base/terraform/target-https-proxy copy.tf +14 -0
  21. package/template/base/terraform/target-https-proxy.tf +14 -0
  22. package/template/base/terraform/terraform.tfvars +4 -0
  23. package/template/base/terraform/url-map.tf +23 -0
  24. package/template/base/terraform/variables.tf +21 -0
  25. package/template/old/.env.example +0 -17
  26. package/template/old/.eslintrc.cjs +0 -26
  27. package/template/old/components.json +0 -17
  28. package/template/old/drizzle.config.ts +0 -11
  29. package/template/old/next.config.mjs +0 -4
  30. package/template/old/package.json +0 -87
  31. package/template/old/postcss.config.mjs +0 -8
  32. package/template/old/prettier.config.mjs +0 -11
  33. package/template/old/public/next.svg +0 -1
  34. package/template/old/public/vercel.svg +0 -1
  35. package/template/old/src/app/(app)/dashboard/layout.tsx +0 -14
  36. package/template/old/src/app/(app)/dashboard/page.tsx +0 -12
  37. package/template/old/src/app/(app)/layout.tsx +0 -125
  38. package/template/old/src/app/(app)/roles/[id]/actions.ts +0 -59
  39. package/template/old/src/app/(app)/roles/[id]/page.tsx +0 -46
  40. package/template/old/src/app/(app)/roles/[id]/render.tsx +0 -264
  41. package/template/old/src/app/(app)/roles/[id]/validation.ts +0 -22
  42. package/template/old/src/app/(app)/roles/actions.ts +0 -33
  43. package/template/old/src/app/(app)/roles/page.tsx +0 -52
  44. package/template/old/src/app/(app)/roles/render.tsx +0 -80
  45. package/template/old/src/app/(app)/settings/actions.ts +0 -17
  46. package/template/old/src/app/(app)/settings/layout.tsx +0 -14
  47. package/template/old/src/app/(app)/settings/loading.tsx +0 -28
  48. package/template/old/src/app/(app)/settings/page.tsx +0 -19
  49. package/template/old/src/app/(app)/settings/render.tsx +0 -107
  50. package/template/old/src/app/(app)/settings/validation.ts +0 -5
  51. package/template/old/src/app/(app)/tasks/[id]/actions.ts +0 -92
  52. package/template/old/src/app/(app)/tasks/[id]/page.tsx +0 -59
  53. package/template/old/src/app/(app)/tasks/[id]/render.tsx +0 -482
  54. package/template/old/src/app/(app)/tasks/[id]/validation.ts +0 -18
  55. package/template/old/src/app/(app)/tasks/actions.ts +0 -39
  56. package/template/old/src/app/(app)/tasks/page.tsx +0 -59
  57. package/template/old/src/app/(app)/tasks/render.tsx +0 -129
  58. package/template/old/src/app/(app)/tasks/statusEnum.ts +0 -3
  59. package/template/old/src/app/(app)/users/[id]/actions.ts +0 -68
  60. package/template/old/src/app/(app)/users/[id]/page.tsx +0 -39
  61. package/template/old/src/app/(app)/users/[id]/render.tsx +0 -325
  62. package/template/old/src/app/(app)/users/[id]/validation.ts +0 -18
  63. package/template/old/src/app/(app)/users/actions.ts +0 -34
  64. package/template/old/src/app/(app)/users/page.tsx +0 -62
  65. package/template/old/src/app/(app)/users/render.tsx +0 -124
  66. package/template/old/src/app/(auth)/layout.tsx +0 -27
  67. package/template/old/src/app/(auth)/login/page.tsx +0 -49
  68. package/template/old/src/app/(auth)/login/render.tsx +0 -146
  69. package/template/old/src/app/(marketing)/layout.tsx +0 -42
  70. package/template/old/src/app/(marketing)/page.tsx +0 -97
  71. package/template/old/src/app/api/auth/[...nextauth]/route.ts +0 -7
  72. package/template/old/src/app/favicon.ico +0 -0
  73. package/template/old/src/app/globals.css +0 -76
  74. package/template/old/src/app/layout.tsx +0 -31
  75. package/template/old/src/components/app-nav-command.tsx +0 -178
  76. package/template/old/src/components/dark-mode-toggle.tsx +0 -43
  77. package/template/old/src/components/data-table/data-table-column-header.tsx +0 -91
  78. package/template/old/src/components/data-table/data-table-faceted-filter.tsx +0 -149
  79. package/template/old/src/components/data-table/data-table-loading.tsx +0 -77
  80. package/template/old/src/components/data-table/data-table-pagination.tsx +0 -94
  81. package/template/old/src/components/data-table/data-table-toolbar.tsx +0 -78
  82. package/template/old/src/components/data-table/data-table-types.ts +0 -17
  83. package/template/old/src/components/data-table/data-table-view-options.tsx +0 -59
  84. package/template/old/src/components/data-table/data-table.tsx +0 -301
  85. package/template/old/src/components/data-table/simple-data-table.tsx +0 -109
  86. package/template/old/src/components/empty-placeholder.tsx +0 -78
  87. package/template/old/src/components/heading.tsx +0 -17
  88. package/template/old/src/components/icons.tsx +0 -101
  89. package/template/old/src/components/main-nav.tsx +0 -64
  90. package/template/old/src/components/mobile-nav.tsx +0 -50
  91. package/template/old/src/components/shell.tsx +0 -13
  92. package/template/old/src/components/side-nav.tsx +0 -51
  93. package/template/old/src/components/site-footer.tsx +0 -31
  94. package/template/old/src/components/system-info.tsx +0 -36
  95. package/template/old/src/components/ui/alert-dialog.tsx +0 -141
  96. package/template/old/src/components/ui/avatar.tsx +0 -50
  97. package/template/old/src/components/ui/badge.tsx +0 -36
  98. package/template/old/src/components/ui/button.tsx +0 -56
  99. package/template/old/src/components/ui/card.tsx +0 -79
  100. package/template/old/src/components/ui/checkbox.tsx +0 -30
  101. package/template/old/src/components/ui/command.tsx +0 -155
  102. package/template/old/src/components/ui/dialog.tsx +0 -122
  103. package/template/old/src/components/ui/dropdown-menu.tsx +0 -200
  104. package/template/old/src/components/ui/form.tsx +0 -176
  105. package/template/old/src/components/ui/input.tsx +0 -25
  106. package/template/old/src/components/ui/label.tsx +0 -26
  107. package/template/old/src/components/ui/popover.tsx +0 -31
  108. package/template/old/src/components/ui/select.tsx +0 -160
  109. package/template/old/src/components/ui/separator.tsx +0 -31
  110. package/template/old/src/components/ui/skeleton.tsx +0 -15
  111. package/template/old/src/components/ui/sonner.tsx +0 -31
  112. package/template/old/src/components/ui/table.tsx +0 -117
  113. package/template/old/src/components/user-account-nav.tsx +0 -60
  114. package/template/old/src/components/user-avatar.tsx +0 -27
  115. package/template/old/src/data-access/resource.ts +0 -85
  116. package/template/old/src/data-access/role-permission.ts +0 -45
  117. package/template/old/src/data-access/role.ts +0 -105
  118. package/template/old/src/data-access/task.ts +0 -141
  119. package/template/old/src/data-access/user.ts +0 -141
  120. package/template/old/src/db/index.ts +0 -14
  121. package/template/old/src/db/schema.ts +0 -287
  122. package/template/old/src/env.mjs +0 -58
  123. package/template/old/src/hooks/use-debounce.tsx +0 -17
  124. package/template/old/src/hooks/use-lock-body.tsx +0 -13
  125. package/template/old/src/lib/auth.ts +0 -89
  126. package/template/old/src/lib/cn.ts +0 -4
  127. package/template/old/src/lib/currentUser.ts +0 -8
  128. package/template/old/src/lib/delay.ts +0 -2
  129. package/template/old/src/lib/formatDate.ts +0 -11
  130. package/template/old/src/lib/generateLabel.ts +0 -5
  131. package/template/old/src/lib/s3Client.ts +0 -156
  132. package/template/old/src/lib/safe-action.ts +0 -19
  133. package/template/old/src/lib/siteConfig.ts +0 -4
  134. package/template/old/src/lib/sortOrderEnum.ts +0 -2
  135. package/template/old/src/use-case/role-permission.ts +0 -19
  136. package/template/old/src/use-case/role.ts +0 -110
  137. package/template/old/src/use-case/task.ts +0 -166
  138. package/template/old/src/use-case/user.ts +0 -121
  139. package/template/old/tailwind.config.ts +0 -81
  140. package/template/old/tsconfig.json +0 -47
@@ -1,482 +0,0 @@
1
- 'use client'
2
-
3
- import EditorJS from '@editorjs/editorjs'
4
- import { zodResolver } from '@hookform/resolvers/zod'
5
- import { InferSelectModel } from 'drizzle-orm'
6
- import edjsHTML from 'editorjs-html'
7
- import Link from 'next/link'
8
- import { useRouter } from 'next/navigation'
9
- import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'
10
- import { useForm } from 'react-hook-form'
11
- import { toast } from 'sonner'
12
- import { z } from 'zod'
13
-
14
- import { Heading } from '@/components/heading'
15
- import { Icons } from '@/components/icons'
16
- import { Shell } from '@/components/shell'
17
- import { SystemInfo } from '@/components/system-info'
18
- import {
19
- AlertDialog,
20
- AlertDialogAction,
21
- AlertDialogCancel,
22
- AlertDialogContent,
23
- AlertDialogDescription,
24
- AlertDialogFooter,
25
- AlertDialogHeader,
26
- AlertDialogTitle,
27
- AlertDialogTrigger
28
- } from '@/components/ui/alert-dialog'
29
- import { Button } from '@/components/ui/button'
30
- import {
31
- Form,
32
- FormControl,
33
- FormField,
34
- FormItem,
35
- FormLabel,
36
- FormMessage
37
- } from '@/components/ui/form'
38
- import { Input } from '@/components/ui/input'
39
- import {
40
- Select,
41
- SelectContent,
42
- SelectItem,
43
- SelectTrigger,
44
- SelectValue
45
- } from '@/components/ui/select'
46
- import { Task } from '@/db/schema'
47
- import { formatDateTime, timesAgo } from '@/lib/formatDate'
48
- import { generateLabel } from '@/lib/generateLabel'
49
- import {
50
- createTaskAction,
51
- deleteTaskAction,
52
- getTaskImageUploadPresignedUrlAction,
53
- markTaskImageUploadedAction,
54
- updateTaskAction
55
- } from './actions'
56
- import { TaskCreateUpdateSchema, TaskStatusEnumArr } from './validation'
57
-
58
- type FormData = z.infer<typeof TaskCreateUpdateSchema>
59
-
60
- const statuses = TaskStatusEnumArr.map(x => ({
61
- label: generateLabel(x),
62
- value: x
63
- }))
64
-
65
- const edjsParser = edjsHTML()
66
-
67
- export const Render: FC<{
68
- task?: {
69
- id: string
70
- title: string
71
- description?: string | null
72
- status: InferSelectModel<typeof Task>['status']
73
- createdAt: Date
74
- updatedAt?: Date | null
75
- createdBy: {
76
- id: string
77
- name?: string | null
78
- }
79
- updatedBy?: {
80
- id: string
81
- name?: string | null
82
- } | null
83
- } | null
84
- canCreate: boolean
85
- canUpdate: boolean
86
- canDelete: boolean
87
- }> = ({ task, canCreate, canUpdate, canDelete }) => {
88
- const form = useForm<FormData>({
89
- resolver: zodResolver(TaskCreateUpdateSchema),
90
- defaultValues: {
91
- title: task?.title || '',
92
- status: task?.status
93
- }
94
- })
95
- const router = useRouter()
96
-
97
- const [isSaving, setIsSaving] = useState(false)
98
- const [isDeleting, setIsDeleting] = useState(false)
99
- const [isEditorMounted, setIsMounted] = useState<boolean>(false)
100
-
101
- const editorRef = useRef<EditorJS>()
102
-
103
- const initializeEditor = useCallback(async () => {
104
- const EditorJS = (await import('@editorjs/editorjs')).default
105
-
106
- // @ts-ignore
107
- const Header = (await import('@editorjs/header')).default
108
-
109
- // @ts-ignore
110
- const List = (await import('@editorjs/list')).default
111
-
112
- // @ts-ignore
113
- const NestedList = (await import('@editorjs/nested-list')).default
114
-
115
- // @ts-ignore
116
- const CheckList = (await import('@editorjs/checklist')).default
117
-
118
- // @ts-ignore
119
- const Table = (await import('@editorjs/table')).default
120
-
121
- // @ts-ignore
122
- const Quote = (await import('@editorjs/quote')).default
123
-
124
- // @ts-ignore
125
- const Delimiter = (await import('@editorjs/delimiter')).default
126
-
127
- // @ts-ignore
128
- const InlineCode = (await import('@editorjs/inline-code')).default
129
-
130
- // @ts-ignore
131
- const Code = (await import('@editorjs/code')).default
132
-
133
- // @ts-ignore
134
- const Image = (await import('@editorjs/image')).default
135
-
136
- if (!editorRef.current) {
137
- const editor = new EditorJS({
138
- holder: 'editor',
139
- onReady() {
140
- editorRef.current = editor
141
- },
142
- placeholder: 'Type here to write...',
143
- inlineToolbar: true,
144
- data: task?.description ? JSON.parse(task.description) : undefined,
145
- tools: {
146
- header: Header,
147
- list: List,
148
- nestedList: NestedList,
149
- checklist: CheckList,
150
- table: Table,
151
- quote: Quote,
152
- delimiter: Delimiter,
153
- inlineCode: InlineCode,
154
- code: Code,
155
- image: {
156
- class: Image,
157
- config: {
158
- uploader: {
159
- async uploadByFile(file: File) {
160
- try {
161
- const res = await getTaskImageUploadPresignedUrlAction({
162
- filename: file.name,
163
- contentType: file.type
164
- })
165
- if (res?.data) {
166
- const formData = new FormData()
167
-
168
- for (const [key, value] of Object.entries(
169
- res.data.presigned.fields
170
- )) {
171
- formData.append(key, value)
172
- }
173
-
174
- formData.append('file', file)
175
-
176
- const response = await fetch(res.data.presigned.url, {
177
- method: 'POST',
178
- body: formData
179
- })
180
-
181
- if (!response.ok)
182
- throw new Error('Failed to upload image to s3')
183
-
184
- const res2 = await markTaskImageUploadedAction(
185
- res.data.id
186
- )
187
-
188
- if (!res2?.data?.success)
189
- throw new Error('Failed to mark image uploaded')
190
-
191
- return {
192
- success: true,
193
- file: {
194
- id: res.data.id,
195
- url: res.data.url
196
- }
197
- }
198
- }
199
- throw new Error('No success returned from server')
200
- } catch (err) {
201
- return {
202
- success: false
203
- }
204
- }
205
- }
206
- }
207
- }
208
- }
209
- }
210
- })
211
- }
212
- }, [task?.description])
213
-
214
- useEffect(() => {
215
- if (typeof window !== 'undefined') setIsMounted(true)
216
- }, [])
217
-
218
- useEffect(() => {
219
- if (!isEditorMounted) return
220
-
221
- initializeEditor()
222
-
223
- return () => {
224
- editorRef.current?.destroy()
225
- editorRef.current = undefined
226
- }
227
- }, [isEditorMounted, initializeEditor])
228
-
229
- const editorHTML = useMemo(() => {
230
- if (!task?.description) return ''
231
- return edjsParser.parse(JSON.parse(task.description)).join('')
232
- }, [task?.description])
233
- console.log(editorHTML)
234
-
235
- async function onSubmit(data: FormData) {
236
- setIsSaving(true)
237
- try {
238
- const editorData = await editorRef.current?.save()
239
- const imageIds: string[] = []
240
- if (editorData?.blocks)
241
- for (const b of editorData.blocks)
242
- if (
243
- b.type === 'image' &&
244
- b.data &&
245
- b.data.file &&
246
- b.data.file.id &&
247
- b.data.file.url
248
- )
249
- imageIds.push(b.data.file.id)
250
-
251
- if (!task) {
252
- const res = await createTaskAction({
253
- ...data,
254
- description: editorData ? JSON.stringify(editorData) : null,
255
- imageIds
256
- })
257
- if (!res?.data?.success)
258
- throw new Error(res?.serverError || 'No success returned from server')
259
- if (!res?.data?.id) throw new Error('No id returned from server')
260
- router.replace(`/tasks/${res.data.id}`)
261
- } else {
262
- const res = await updateTaskAction({
263
- id: task.id,
264
- ...data,
265
- description: editorData ? JSON.stringify(editorData) : null,
266
- imageIds
267
- })
268
- if (!res?.data?.success)
269
- throw new Error(res?.serverError || 'No success returned from server')
270
- }
271
- toast('User saved')
272
- } catch (err) {
273
- toast('Error saving user', {
274
- description: (err as any).message || undefined
275
- })
276
- }
277
- setIsSaving(false)
278
- }
279
-
280
- return (
281
- <Shell>
282
- <Heading heading={task ? task.title : 'New Task'} />
283
- <Form {...form}>
284
- <form
285
- className="grid grid-cols-1 gap-3 md:grid-cols-2"
286
- onSubmit={form.handleSubmit(onSubmit)}
287
- >
288
- <div className="col-span-1 mb-1 grid grid-cols-2 gap-2 sm:flex md:col-span-2">
289
- <Button type="button" onClick={() => router.back()} variant="ghost">
290
- <Icons.chevronLeft className="mr-2 h-4 w-4" />
291
- Back
292
- </Button>
293
- {(task ? canUpdate : canCreate) ? (
294
- <Button type="submit" disabled={isSaving || isDeleting}>
295
- {isSaving ? (
296
- <Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
297
- ) : (
298
- <Icons.save className="mr-2 h-4 w-4" />
299
- )}
300
- <span>Save</span>
301
- </Button>
302
- ) : undefined}
303
- {task && canDelete ? (
304
- <AlertDialog>
305
- <AlertDialogTrigger asChild>
306
- <Button
307
- type="button"
308
- variant="destructive"
309
- disabled={isSaving || isDeleting}
310
- >
311
- {isDeleting ? (
312
- <Icons.spinner className="mr-2 h-4 w-4 animate-spin" />
313
- ) : (
314
- <Icons.delete className="mr-2 h-4 w-4" />
315
- )}
316
- Delete
317
- </Button>
318
- </AlertDialogTrigger>
319
- <AlertDialogContent>
320
- <AlertDialogHeader>
321
- <AlertDialogTitle>
322
- Are you absolutely sure?
323
- </AlertDialogTitle>
324
- <AlertDialogDescription>
325
- This action cannot be undone.
326
- </AlertDialogDescription>
327
- </AlertDialogHeader>
328
- <AlertDialogFooter>
329
- <AlertDialogCancel>Cancel</AlertDialogCancel>
330
- <AlertDialogAction
331
- onClick={async () => {
332
- setIsDeleting(true)
333
- try {
334
- const res = await deleteTaskAction(task.id)
335
- if (!res?.data?.success)
336
- throw new Error(
337
- res?.serverError ||
338
- 'No success returned from server'
339
- )
340
- toast('Task deleted')
341
- router.replace('/tasks')
342
- } catch (err) {
343
- toast('Error deleting task', {
344
- description:
345
- (err as any).message || 'Something went wrong'
346
- })
347
- }
348
- setIsDeleting(false)
349
- }}
350
- >
351
- Continue
352
- </AlertDialogAction>
353
- </AlertDialogFooter>
354
- </AlertDialogContent>
355
- </AlertDialog>
356
- ) : undefined}
357
- {task && canCreate ? (
358
- <Link href="/tasks/new">
359
- <Button
360
- type="button"
361
- disabled={isSaving || isDeleting}
362
- variant="secondary"
363
- className="w-full"
364
- >
365
- <Icons.add className="mr-2 h-4 w-4" />
366
- New
367
- </Button>
368
- </Link>
369
- ) : undefined}
370
- </div>
371
-
372
- <FormField
373
- control={form.control}
374
- name="title"
375
- render={({ field }) => (
376
- <FormItem>
377
- <FormLabel>Title</FormLabel>
378
- <FormControl>
379
- <Input
380
- type="text"
381
- placeholder="Enter a title"
382
- disabled={isSaving}
383
- {...field}
384
- />
385
- </FormControl>
386
- <FormMessage />
387
- </FormItem>
388
- )}
389
- />
390
-
391
- <FormField
392
- control={form.control}
393
- name="status"
394
- render={({ field }) => (
395
- <FormItem>
396
- <FormLabel>Status</FormLabel>
397
- <Select
398
- onValueChange={field.onChange}
399
- defaultValue={field.value}
400
- disabled={isSaving}
401
- >
402
- <FormControl>
403
- <SelectTrigger>
404
- <SelectValue placeholder="Select status" />
405
- </SelectTrigger>
406
- </FormControl>
407
- <SelectContent>
408
- {statuses.map(({ label, value }) => (
409
- <SelectItem key={value} value={value}>
410
- {label}
411
- </SelectItem>
412
- ))}
413
- </SelectContent>
414
- </Select>
415
- <FormMessage />
416
- </FormItem>
417
- )}
418
- />
419
- </form>
420
- </Form>
421
-
422
- {isEditorMounted ? (
423
- <div className="prose prose-neutral mx-auto mt-5 w-full dark:prose-invert">
424
- <h3 className="w-full bg-transparent text-3xl font-bold">
425
- Description
426
- </h3>
427
- <div id="editor" className="min-h-[360px] w-full" />
428
- <p className="text-sm text-gray-500">
429
- Use{' '}
430
- <kbd className="rounded-md border bg-muted px-1 text-xs uppercase">
431
- /
432
- </kbd>{' '}
433
- to open the command menu.
434
- </p>
435
- </div>
436
- ) : undefined}
437
-
438
- {task ? (
439
- <SystemInfo
440
- items={[
441
- {
442
- label: 'Id',
443
- value: task.id
444
- },
445
- {
446
- label: 'Created At',
447
- value: formatDateTime(task.createdAt)
448
- },
449
- {
450
- label: 'Updated At',
451
- value: task.updatedAt ? timesAgo(task.updatedAt) : '-'
452
- },
453
- {
454
- label: 'Created By',
455
- value: (
456
- <Link
457
- href={`/users/${task.createdBy.id}`}
458
- className="underline underline-offset-4 transition-all hover:text-foreground"
459
- >
460
- {task.createdBy.name ?? 'Unknown'}
461
- </Link>
462
- )
463
- },
464
- {
465
- label: 'Updated By',
466
- value: task.updatedBy ? (
467
- <Link
468
- href={`/users/${task.updatedBy.id}`}
469
- className="underline underline-offset-4 transition-all hover:text-foreground"
470
- >
471
- {task.updatedBy.name ?? 'Unknown'}
472
- </Link>
473
- ) : (
474
- '-'
475
- )
476
- }
477
- ]}
478
- />
479
- ) : undefined}
480
- </Shell>
481
- )
482
- }
@@ -1,18 +0,0 @@
1
- import { z } from 'zod'
2
-
3
- import { Task } from '@/db/schema'
4
-
5
- export const TaskStatusEnumArr = Task.status.enumValues
6
-
7
- export const TaskCreateUpdateSchema = z.object({
8
- title: z.string().min(1).trim(),
9
- description: z.string().optional().nullable(),
10
- status: z.enum(TaskStatusEnumArr),
11
- imageIds: z.array(z.string()).optional().nullable()
12
- })
13
-
14
- export const TaskUpdateServerSchema = TaskCreateUpdateSchema.merge(
15
- z.object({
16
- id: z.string().min(1)
17
- })
18
- )
@@ -1,39 +0,0 @@
1
- 'use server'
2
-
3
- import { z } from 'zod'
4
-
5
- import { authActionClient } from '@/lib/safe-action'
6
- import { SortOrderEnum } from '@/lib/sortOrderEnum'
7
- import { getTasksWithUserUseCase } from '@/use-case/task'
8
- import { TaskStatusEnumArr } from './statusEnum'
9
-
10
- export const getTasksAction = authActionClient
11
- .schema(
12
- z.object({
13
- page: z.number().gte(1),
14
- limit: z.number().gte(1).lte(1000),
15
- sortBy: z
16
- .enum(['id', 'title', 'status', 'createdAt', 'updatedAt'])
17
- .optional(),
18
- sortOrder: z.enum(SortOrderEnum).optional(),
19
- title: z.string().optional(),
20
- status: z.array(z.enum(TaskStatusEnumArr)).optional()
21
- })
22
- )
23
- .action(async ({ parsedInput, ctx }) => {
24
- return await getTasksWithUserUseCase(ctx.user, {
25
- page: parsedInput.page,
26
- limit: parsedInput.limit,
27
- sortBy: parsedInput.sortBy,
28
- sortOrder: parsedInput.sortOrder,
29
- filters:
30
- parsedInput.title || parsedInput.status?.length
31
- ? {
32
- title: parsedInput.title || undefined,
33
- status: parsedInput.status?.length
34
- ? parsedInput.status
35
- : undefined
36
- }
37
- : undefined
38
- })
39
- })
@@ -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