create-nextjs-cms 0.9.30 → 0.9.31

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 (143) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +71 -71
  3. package/dist/helpers/utils.js +16 -16
  4. package/dist/lib/section-creators.js +166 -166
  5. package/package.json +2 -2
  6. package/templates/default/.eslintrc.json +5 -5
  7. package/templates/default/.prettierignore +7 -7
  8. package/templates/default/.prettierrc.json +27 -27
  9. package/templates/default/CHANGELOG.md +140 -140
  10. package/templates/default/_gitignore +57 -57
  11. package/templates/default/app/(auth)/auth/login/LoginPage.tsx +192 -192
  12. package/templates/default/app/(auth)/auth/login/page.tsx +11 -11
  13. package/templates/default/app/(auth)/auth-language-provider.tsx +34 -34
  14. package/templates/default/app/(auth)/layout.tsx +81 -81
  15. package/templates/default/app/(rootLayout)/(plugins)/[...slug]/plugin-server-registry.ts +10 -6
  16. package/templates/default/app/(rootLayout)/admins/page.tsx +10 -10
  17. package/templates/default/app/(rootLayout)/browse/[section]/[page]/page.tsx +22 -22
  18. package/templates/default/app/(rootLayout)/categorized/[section]/page.tsx +15 -15
  19. package/templates/default/app/(rootLayout)/dashboard/page.tsx +70 -70
  20. package/templates/default/app/(rootLayout)/edit/[section]/[itemId]/page.tsx +20 -20
  21. package/templates/default/app/(rootLayout)/layout.tsx +81 -81
  22. package/templates/default/app/(rootLayout)/loading.tsx +10 -10
  23. package/templates/default/app/(rootLayout)/log/page.tsx +7 -7
  24. package/templates/default/app/(rootLayout)/new/[section]/page.tsx +15 -15
  25. package/templates/default/app/(rootLayout)/section/[section]/page.tsx +19 -19
  26. package/templates/default/app/(rootLayout)/settings/page.tsx +13 -13
  27. package/templates/default/app/api/auth/csrf/route.ts +25 -25
  28. package/templates/default/app/api/auth/refresh/route.ts +10 -10
  29. package/templates/default/app/api/auth/route.ts +49 -49
  30. package/templates/default/app/api/auth/session/route.ts +20 -20
  31. package/templates/default/app/api/document/route.ts +165 -165
  32. package/templates/default/app/api/editor/photo/route.ts +49 -49
  33. package/templates/default/app/api/photo/route.ts +27 -27
  34. package/templates/default/app/api/submit/section/item/[slug]/route.ts +95 -95
  35. package/templates/default/app/api/submit/section/item/route.ts +56 -56
  36. package/templates/default/app/api/submit/section/simple/route.ts +86 -86
  37. package/templates/default/app/api/video/route.ts +174 -174
  38. package/templates/default/app/globals.css +236 -236
  39. package/templates/default/cms.config.ts +56 -56
  40. package/templates/default/components/admin/admin-card.tsx +165 -165
  41. package/templates/default/components/admin/admin-edit-page.tsx +124 -124
  42. package/templates/default/components/admin/admin-privilege-card.tsx +184 -184
  43. package/templates/default/components/admin/new-admin-form.tsx +172 -172
  44. package/templates/default/components/container-box.tsx +24 -24
  45. package/templates/default/components/dnd-kit/draggable.tsx +21 -21
  46. package/templates/default/components/dnd-kit/droppable.tsx +20 -20
  47. package/templates/default/components/dnd-kit/sortable-item.tsx +18 -18
  48. package/templates/default/components/feedback/error-component.tsx +16 -16
  49. package/templates/default/components/feedback/info-card.tsx +93 -93
  50. package/templates/default/components/feedback/loading-spinners.tsx +67 -67
  51. package/templates/default/components/feedback/modal.tsx +166 -166
  52. package/templates/default/components/feedback/progress-bar.tsx +48 -48
  53. package/templates/default/components/feedback/tooltip-component.tsx +27 -27
  54. package/templates/default/components/form/form-input-element.tsx +70 -70
  55. package/templates/default/components/form/helpers/_section-hot-reload.js +1 -1
  56. package/templates/default/components/form/helpers/util.ts +17 -17
  57. package/templates/default/components/form/inputs/checkbox-form-input.tsx +46 -46
  58. package/templates/default/components/form/inputs/color-form-input.tsx +44 -44
  59. package/templates/default/components/form/inputs/date-form-input.tsx +93 -93
  60. package/templates/default/components/form/inputs/map-form-input.tsx +141 -141
  61. package/templates/default/components/form/inputs/multiple-select-form-input.tsx +85 -85
  62. package/templates/default/components/form/inputs/number-form-input.tsx +43 -43
  63. package/templates/default/components/form/inputs/password-form-input.tsx +47 -47
  64. package/templates/default/components/form/inputs/photo-form-input.tsx +279 -279
  65. package/templates/default/components/form/inputs/rich-text-form-input.tsx +148 -148
  66. package/templates/default/components/form/inputs/select-form-input.tsx +159 -159
  67. package/templates/default/components/form/inputs/slug-form-input.tsx +131 -131
  68. package/templates/default/components/form/inputs/tags-form-input.tsx +255 -255
  69. package/templates/default/components/form/inputs/text-form-input.tsx +61 -61
  70. package/templates/default/components/form/inputs/textarea-form-input.tsx +61 -61
  71. package/templates/default/components/layout/default-nav-items.tsx +3 -3
  72. package/templates/default/components/layout/layout.tsx +84 -84
  73. package/templates/default/components/layout/navbar.tsx +258 -258
  74. package/templates/default/components/layout/sidebar-dropdown-item.tsx +83 -83
  75. package/templates/default/components/layout/sidebar-item.tsx +24 -24
  76. package/templates/default/components/layout/sidebar.tsx +229 -229
  77. package/templates/default/components/layout/theme-provider.tsx +8 -8
  78. package/templates/default/components/layout/theme-toggle.tsx +39 -39
  79. package/templates/default/components/locale/locale-switcher.tsx +98 -98
  80. package/templates/default/components/media/dropzone.tsx +154 -154
  81. package/templates/default/components/media/protected-document.tsx +44 -44
  82. package/templates/default/components/media/protected-image.tsx +143 -143
  83. package/templates/default/components/media/protected-video.tsx +76 -76
  84. package/templates/default/components/multi-select.tsx +1150 -1150
  85. package/templates/default/components/pages/admins-page.tsx +43 -43
  86. package/templates/default/components/pages/browse-page.tsx +106 -106
  87. package/templates/default/components/pages/categorized-section-page.tsx +31 -31
  88. package/templates/default/components/pages/dashboard-page-alt.tsx +45 -45
  89. package/templates/default/components/pages/item-edit-page.tsx +267 -267
  90. package/templates/default/components/pages/log-page.tsx +107 -107
  91. package/templates/default/components/pages/new-page.tsx +183 -183
  92. package/templates/default/components/pages/section-page.tsx +203 -203
  93. package/templates/default/components/pages/settings-page.tsx +232 -232
  94. package/templates/default/components/pagination/pagination-buttons.tsx +147 -147
  95. package/templates/default/components/pagination/pagination.tsx +36 -36
  96. package/templates/default/components/sections/category-delete-confirm-page.tsx +130 -130
  97. package/templates/default/components/sections/category-section-select-input.tsx +139 -139
  98. package/templates/default/components/sections/conditional-fields.tsx +49 -49
  99. package/templates/default/components/sections/section-icon.tsx +8 -8
  100. package/templates/default/components/sections/section-item-card.tsx +143 -143
  101. package/templates/default/components/sections/section-item-status-badge.tsx +17 -17
  102. package/templates/default/components/sections/select-input-buttons.tsx +125 -125
  103. package/templates/default/components/select-box.tsx +98 -98
  104. package/templates/default/components/ui/accordion.tsx +53 -53
  105. package/templates/default/components/ui/alert-dialog.tsx +113 -113
  106. package/templates/default/components/ui/alert.tsx +47 -47
  107. package/templates/default/components/ui/badge.tsx +38 -38
  108. package/templates/default/components/ui/card.tsx +43 -43
  109. package/templates/default/components/ui/command.tsx +137 -137
  110. package/templates/default/components/ui/custom-alert-dialog.tsx +113 -113
  111. package/templates/default/components/ui/custom-dialog.tsx +123 -123
  112. package/templates/default/components/ui/dialog.tsx +123 -123
  113. package/templates/default/components/ui/direction.tsx +22 -22
  114. package/templates/default/components/ui/dropdown-menu.tsx +182 -182
  115. package/templates/default/components/ui/input-group.tsx +54 -54
  116. package/templates/default/components/ui/input.tsx +22 -22
  117. package/templates/default/components/ui/label.tsx +19 -19
  118. package/templates/default/components/ui/popover.tsx +42 -42
  119. package/templates/default/components/ui/progress.tsx +31 -31
  120. package/templates/default/components/ui/scroll-area.tsx +42 -42
  121. package/templates/default/components/ui/select.tsx +165 -165
  122. package/templates/default/components/ui/separator.tsx +28 -28
  123. package/templates/default/components/ui/sheet.tsx +103 -103
  124. package/templates/default/components/ui/spinner.tsx +16 -16
  125. package/templates/default/components/ui/switch.tsx +29 -29
  126. package/templates/default/components/ui/table.tsx +83 -83
  127. package/templates/default/components/ui/tabs.tsx +55 -55
  128. package/templates/default/components/ui/toast.tsx +113 -113
  129. package/templates/default/components/ui/toaster.tsx +35 -35
  130. package/templates/default/components/ui/tooltip.tsx +30 -30
  131. package/templates/default/components/ui/use-toast.ts +187 -187
  132. package/templates/default/drizzle.config.ts +4 -4
  133. package/templates/default/dynamic-schemas/schema.ts +225 -75
  134. package/templates/default/env/env.ts +46 -46
  135. package/templates/default/envConfig.ts +4 -4
  136. package/templates/default/lib/postinstall.js +14 -14
  137. package/templates/default/lib/utils.ts +6 -6
  138. package/templates/default/next-env.d.ts +6 -6
  139. package/templates/default/next.config.ts +24 -24
  140. package/templates/default/package.json +1 -1
  141. package/templates/default/postcss.config.mjs +6 -6
  142. package/templates/default/proxy.ts +32 -32
  143. package/templates/default/tsconfig.json +48 -48
@@ -1,166 +1,166 @@
1
- import { useI18n } from 'nextjs-cms/translations/client'
2
- import React from 'react'
3
- import { useModal } from '@/components/feedback/modal-context'
4
- import ProtectedImage from '@/components/media/protected-image'
5
- import AdminEditPage from '@/components/admin/admin-edit-page'
6
- import { useToast } from '@/components/ui/use-toast'
7
- import { ScrollArea } from '@/components/ui/scroll-area'
8
- import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
9
- import { Badge } from '@/components/ui/badge'
10
- import { Button } from '@/components/ui/button'
11
- import { trpc } from '@/app/_trpc/client'
12
- import Image from 'next/image'
1
+ import { useI18n } from 'nextjs-cms/translations/client'
2
+ import React from 'react'
3
+ import { useModal } from '@/components/feedback/modal-context'
4
+ import ProtectedImage from '@/components/media/protected-image'
5
+ import AdminEditPage from '@/components/admin/admin-edit-page'
6
+ import { useToast } from '@/components/ui/use-toast'
7
+ import { ScrollArea } from '@/components/ui/scroll-area'
8
+ import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
9
+ import { Badge } from '@/components/ui/badge'
10
+ import { Button } from '@/components/ui/button'
11
+ import { trpc } from '@/app/_trpc/client'
12
+ import Image from 'next/image'
13
13
  import type { RouterOutputs } from '@/app/_trpc/types'
14
- export default function AdminCard({
15
- admin,
16
- action,
17
- }: {
18
- admin: NonNullable<RouterOutputs['admins']['list']['admins']>[number]
19
- action: () => void
20
- }) {
21
- const t = useI18n()
22
- const { setModal, modal, modalResponse, setModalResponse } = useModal()
23
- const { toast } = useToast()
24
-
25
- const deleteMutation = trpc.admins.remove.useMutation({
26
- onSuccess: () => {
27
- action()
28
- setModal(null)
29
- setModalResponse(null)
30
- toast({
31
- variant: 'success',
32
- title: t('deleteAdmin'),
33
- description: t('adminDeletedSuccessfully'),
34
- })
35
- },
36
- onError: (error) => {
37
- toast({
38
- variant: 'destructive',
39
- title: t('deleteAdmin'),
40
- description: error.message,
41
- })
42
- },
43
- })
44
-
45
- const handleDeleteAdmin = async () => {
46
- if ([1, '1'].includes(admin.id)) {
47
- toast({
48
- variant: 'destructive',
49
- title: t('deleteAdmin'),
50
- description: t('masterAdminCannotBeDeleted'),
51
- })
52
- return
53
- }
54
- deleteMutation.mutate({
55
- id: admin.id,
56
- })
57
- }
58
-
59
- return (
60
- <Card className='flex flex-col justify-between border border-gray-400 shadow-xs'>
61
- <CardHeader>
62
- <CardTitle>
63
- <div className='flex flex-row items-center gap-2'>
64
- <div>
65
- {admin.avatar ? (
66
- <ProtectedImage
67
- section={'admins'}
68
- photo={admin.avatar}
69
- isThumb={true}
70
- alt={'admin'}
71
- width={50}
72
- height={50}
73
- />
74
- ) : (
75
- <Image
76
- src='/blank_avatar.png'
77
- height={50}
78
- width={50}
79
- alt={admin.username}
80
- className='rounded-full ring-2 ring-amber-300 ring-inset'
81
- />
82
- )}
83
- </div>
84
- <div className='flex flex-col break-all'>
85
- <div className='text-foreground text-sm font-medium'>
86
- <div className='font-bold'>{admin.username}</div>
87
- </div>
88
- </div>
89
- </div>
90
- </CardTitle>
91
- </CardHeader>
92
- <CardContent className='p-1'>
93
- <ScrollArea className='bg-accent/50 h-[100px] p-2' type='always'>
94
- <div className='flex flex-wrap gap-2'>
95
- {admin.roles &&
96
- admin.roles.length > 0 &&
97
- admin.roles.map((role, index: number) => (
98
- <Badge key={index} className='border-foreground/30' variant='outline'>
99
- {role.sectionName} ({role.operations})
100
- </Badge>
101
- ))}
102
- </div>
103
- </ScrollArea>
104
- </CardContent>
105
- <CardFooter className='flex flex-wrap gap-4'>
106
- <Button
107
- variant='default'
108
- onClick={() => {
109
- setModal({
110
- title: t('editAdmin'),
111
- body: [1, '1'].includes(admin.id) ? (
112
- <div className='p-4'>{t('masterAdminCannotBeModified')}</div>
113
- ) : (
114
- <div className='p-4'>{<AdminEditPage id={admin.id} action={() => action()} />}</div>
115
- ),
116
- headerColor: 'bg-sky-700',
117
- titleColor: 'text-white',
118
- lang: 'en',
119
- size: 'xl',
120
- })
121
- }}
122
- >
123
- {t('edit')}
124
- </Button>
125
- <Button
126
- variant='destructive'
127
- onClick={() => {
128
- setModal({
129
- title: t('deleteAdmin'),
130
- body: [1, '1'].includes(admin.id) ? (
131
- <div className='p-4'>{t('masterAdminCannotBeDeleted')}</div>
132
- ) : (
133
- <div className='p-4'>
134
- <div className='flex flex-col gap-4'>
135
- <div>{t('deleteAdminText')}</div>
136
- <div className='flex gap-2'>
137
- <button
138
- className='rounded bg-green-600 px-2 py-1 text-white'
139
- onClick={handleDeleteAdmin}
140
- >
141
- {t('yes')}
142
- </button>
143
- <button
144
- className='rounded bg-red-800 px-2 py-1 text-white'
145
- onClick={() => {
146
- setModal(null)
147
- }}
148
- >
149
- {t('no')}
150
- </button>
151
- </div>
152
- </div>
153
- </div>
154
- ),
155
- headerColor: 'bg-red-700',
156
- titleColor: 'text-white',
157
- lang: 'en',
158
- })
159
- }}
160
- >
161
- {t('delete')}
162
- </Button>
163
- </CardFooter>
164
- </Card>
165
- )
166
- }
14
+ export default function AdminCard({
15
+ admin,
16
+ action,
17
+ }: {
18
+ admin: NonNullable<RouterOutputs['admins']['list']['admins']>[number]
19
+ action: () => void
20
+ }) {
21
+ const t = useI18n()
22
+ const { setModal, modal, modalResponse, setModalResponse } = useModal()
23
+ const { toast } = useToast()
24
+
25
+ const deleteMutation = trpc.admins.remove.useMutation({
26
+ onSuccess: () => {
27
+ action()
28
+ setModal(null)
29
+ setModalResponse(null)
30
+ toast({
31
+ variant: 'success',
32
+ title: t('deleteAdmin'),
33
+ description: t('adminDeletedSuccessfully'),
34
+ })
35
+ },
36
+ onError: (error) => {
37
+ toast({
38
+ variant: 'destructive',
39
+ title: t('deleteAdmin'),
40
+ description: error.message,
41
+ })
42
+ },
43
+ })
44
+
45
+ const handleDeleteAdmin = async () => {
46
+ if ([1, '1'].includes(admin.id)) {
47
+ toast({
48
+ variant: 'destructive',
49
+ title: t('deleteAdmin'),
50
+ description: t('masterAdminCannotBeDeleted'),
51
+ })
52
+ return
53
+ }
54
+ deleteMutation.mutate({
55
+ id: admin.id,
56
+ })
57
+ }
58
+
59
+ return (
60
+ <Card className='flex flex-col justify-between border border-gray-400 shadow-xs'>
61
+ <CardHeader>
62
+ <CardTitle>
63
+ <div className='flex flex-row items-center gap-2'>
64
+ <div>
65
+ {admin.avatar ? (
66
+ <ProtectedImage
67
+ section={'admins'}
68
+ photo={admin.avatar}
69
+ isThumb={true}
70
+ alt={'admin'}
71
+ width={50}
72
+ height={50}
73
+ />
74
+ ) : (
75
+ <Image
76
+ src='/blank_avatar.png'
77
+ height={50}
78
+ width={50}
79
+ alt={admin.username}
80
+ className='rounded-full ring-2 ring-amber-300 ring-inset'
81
+ />
82
+ )}
83
+ </div>
84
+ <div className='flex flex-col break-all'>
85
+ <div className='text-foreground text-sm font-medium'>
86
+ <div className='font-bold'>{admin.username}</div>
87
+ </div>
88
+ </div>
89
+ </div>
90
+ </CardTitle>
91
+ </CardHeader>
92
+ <CardContent className='p-1'>
93
+ <ScrollArea className='bg-accent/50 h-[100px] p-2' type='always'>
94
+ <div className='flex flex-wrap gap-2'>
95
+ {admin.roles &&
96
+ admin.roles.length > 0 &&
97
+ admin.roles.map((role, index: number) => (
98
+ <Badge key={index} className='border-foreground/30' variant='outline'>
99
+ {role.sectionName} ({role.operations})
100
+ </Badge>
101
+ ))}
102
+ </div>
103
+ </ScrollArea>
104
+ </CardContent>
105
+ <CardFooter className='flex flex-wrap gap-4'>
106
+ <Button
107
+ variant='default'
108
+ onClick={() => {
109
+ setModal({
110
+ title: t('editAdmin'),
111
+ body: [1, '1'].includes(admin.id) ? (
112
+ <div className='p-4'>{t('masterAdminCannotBeModified')}</div>
113
+ ) : (
114
+ <div className='p-4'>{<AdminEditPage id={admin.id} action={() => action()} />}</div>
115
+ ),
116
+ headerColor: 'bg-sky-700',
117
+ titleColor: 'text-white',
118
+ lang: 'en',
119
+ size: 'xl',
120
+ })
121
+ }}
122
+ >
123
+ {t('edit')}
124
+ </Button>
125
+ <Button
126
+ variant='destructive'
127
+ onClick={() => {
128
+ setModal({
129
+ title: t('deleteAdmin'),
130
+ body: [1, '1'].includes(admin.id) ? (
131
+ <div className='p-4'>{t('masterAdminCannotBeDeleted')}</div>
132
+ ) : (
133
+ <div className='p-4'>
134
+ <div className='flex flex-col gap-4'>
135
+ <div>{t('deleteAdminText')}</div>
136
+ <div className='flex gap-2'>
137
+ <button
138
+ className='rounded bg-green-600 px-2 py-1 text-white'
139
+ onClick={handleDeleteAdmin}
140
+ >
141
+ {t('yes')}
142
+ </button>
143
+ <button
144
+ className='rounded bg-red-800 px-2 py-1 text-white'
145
+ onClick={() => {
146
+ setModal(null)
147
+ }}
148
+ >
149
+ {t('no')}
150
+ </button>
151
+ </div>
152
+ </div>
153
+ </div>
154
+ ),
155
+ headerColor: 'bg-red-700',
156
+ titleColor: 'text-white',
157
+ lang: 'en',
158
+ })
159
+ }}
160
+ >
161
+ {t('delete')}
162
+ </Button>
163
+ </CardFooter>
164
+ </Card>
165
+ )
166
+ }
@@ -1,124 +1,124 @@
1
- import { useI18n } from 'nextjs-cms/translations/client'
2
- import React from 'react'
3
- import { useModal } from '@/components/feedback/modal-context'
4
- import { trpc } from '@/app/_trpc/client'
5
- import AdminRoleCard from '@/components/admin/admin-privilege-card'
6
- import LoadingSpinners from '@/components/feedback/loading-spinners'
7
- import { Button } from '@/components/ui/button'
8
-
9
- export default function AdminEditPage({ id, action }: { id: string; action: any }) {
10
- const t = useI18n()
11
- const { setModal } = useModal()
12
- const [allChecked, setAllChecked] = React.useState<boolean | 'edited' | null>(null)
13
- const utils = trpc.useUtils()
14
-
15
- const submitMutation = trpc.admins.update.useMutation({
16
- onError: (error) => {
17
- setModal({
18
- title: t('editAdmin'),
19
- body: (
20
- <div className='rounded border-2 border-dashed border-red-500 bg-red-100 p-2 text-red-800'>
21
- {error.message}
22
- </div>
23
- ),
24
- headerColor: 'bg-red-700',
25
- titleColor: 'text-white',
26
- lang: 'en',
27
- })
28
- },
29
-
30
- onSuccess: (data) => {
31
- action()
32
- utils.admins.get.invalidate(id)
33
- setModal({
34
- title: t('editAdmin'),
35
- body: (
36
- <div className='rounded border-2 border-dashed border-green-500 bg-green-100 p-2 text-green-800'>
37
- {data.status}
38
- </div>
39
- ),
40
- headerColor: 'bg-green-700',
41
- titleColor: 'text-white',
42
- lang: 'en',
43
- })
44
- },
45
- })
46
-
47
- const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
48
- e.preventDefault()
49
-
50
- const data = new FormData(e.currentTarget)
51
- const values = Object.fromEntries(data.entries())
52
-
53
- /**
54
- * Convert the privileges to an array of objects
55
- */
56
- const privileges = Object.entries(values).filter(([key]) => key.includes('privileges'))
57
- const privilegesArray = privileges.map(([key, value]) => JSON.parse(value as string))
58
-
59
- submitMutation.mutate({
60
- id: id,
61
- privileges: privilegesArray,
62
- })
63
- }
64
-
65
- const { data, error, isLoading, isError } = trpc.admins.get.useQuery(id)
66
- if (isError) {
67
- return <div>{error?.message}</div>
68
- }
69
- return (
70
- <div>
71
- <h2>
72
- {t('editAdminText')}: {data?.admin.user}
73
- </h2>
74
- <div>
75
- {isLoading ? <LoadingSpinners /> : null}
76
- {data && data.allRoles && data.allRoles.length > 0 ? (
77
- <form onSubmit={handleSubmit}>
78
- <div className='mt-5 w-full font-semibold'>{t('adminPrivileges')}</div>
79
- <div className='flex'>
80
- <Button
81
- className='border border-foreground'
82
- type='button'
83
- variant={allChecked === true ? 'default' : 'secondary'}
84
- onClick={(event) => {
85
- event.preventDefault()
86
- event.stopPropagation()
87
- if (allChecked === true) {
88
- setAllChecked(false)
89
- } else {
90
- setAllChecked(true)
91
- }
92
- }}
93
- >
94
- {allChecked === true ? t('removeAll') : t('checkAll')}
95
- </Button>
96
- </div>
97
- <div className='mt-2 grid w-full grid-cols-1 gap-2 md-sidebar:grid-cols-2 lg-sidebar:grid-cols-3 xl-sidebar:grid-cols-4 2xl-sidebar:grid-cols-5'>
98
- {data.allRoles.map((role, index: number) => {
99
- const defaultValue = data.adminRoles.find((r) => r.sectionName === role.sectionName)
100
- return (
101
- <AdminRoleCard
102
- key={index}
103
- privilege={role}
104
- allChecked={allChecked}
105
- setAllChecked={setAllChecked}
106
- defaultValue={{
107
- operations: defaultValue?.operations.toLowerCase() || 'none',
108
- publisher: defaultValue?.publisher || false,
109
- }}
110
- />
111
- )
112
- })}
113
- </div>
114
- <div className='mt-4'>
115
- <button type='submit' className='rounded bg-blue-700 px-2 py-1 font-bold text-white'>
116
- {t('save')}
117
- </button>
118
- </div>
119
- </form>
120
- ) : null}
121
- </div>
122
- </div>
123
- )
124
- }
1
+ import { useI18n } from 'nextjs-cms/translations/client'
2
+ import React from 'react'
3
+ import { useModal } from '@/components/feedback/modal-context'
4
+ import { trpc } from '@/app/_trpc/client'
5
+ import AdminRoleCard from '@/components/admin/admin-privilege-card'
6
+ import LoadingSpinners from '@/components/feedback/loading-spinners'
7
+ import { Button } from '@/components/ui/button'
8
+
9
+ export default function AdminEditPage({ id, action }: { id: string; action: any }) {
10
+ const t = useI18n()
11
+ const { setModal } = useModal()
12
+ const [allChecked, setAllChecked] = React.useState<boolean | 'edited' | null>(null)
13
+ const utils = trpc.useUtils()
14
+
15
+ const submitMutation = trpc.admins.update.useMutation({
16
+ onError: (error) => {
17
+ setModal({
18
+ title: t('editAdmin'),
19
+ body: (
20
+ <div className='rounded border-2 border-dashed border-red-500 bg-red-100 p-2 text-red-800'>
21
+ {error.message}
22
+ </div>
23
+ ),
24
+ headerColor: 'bg-red-700',
25
+ titleColor: 'text-white',
26
+ lang: 'en',
27
+ })
28
+ },
29
+
30
+ onSuccess: (data) => {
31
+ action()
32
+ utils.admins.get.invalidate(id)
33
+ setModal({
34
+ title: t('editAdmin'),
35
+ body: (
36
+ <div className='rounded border-2 border-dashed border-green-500 bg-green-100 p-2 text-green-800'>
37
+ {data.status}
38
+ </div>
39
+ ),
40
+ headerColor: 'bg-green-700',
41
+ titleColor: 'text-white',
42
+ lang: 'en',
43
+ })
44
+ },
45
+ })
46
+
47
+ const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
48
+ e.preventDefault()
49
+
50
+ const data = new FormData(e.currentTarget)
51
+ const values = Object.fromEntries(data.entries())
52
+
53
+ /**
54
+ * Convert the privileges to an array of objects
55
+ */
56
+ const privileges = Object.entries(values).filter(([key]) => key.includes('privileges'))
57
+ const privilegesArray = privileges.map(([key, value]) => JSON.parse(value as string))
58
+
59
+ submitMutation.mutate({
60
+ id: id,
61
+ privileges: privilegesArray,
62
+ })
63
+ }
64
+
65
+ const { data, error, isLoading, isError } = trpc.admins.get.useQuery(id)
66
+ if (isError) {
67
+ return <div>{error?.message}</div>
68
+ }
69
+ return (
70
+ <div>
71
+ <h2>
72
+ {t('editAdminText')}: {data?.admin.user}
73
+ </h2>
74
+ <div>
75
+ {isLoading ? <LoadingSpinners /> : null}
76
+ {data && data.allRoles && data.allRoles.length > 0 ? (
77
+ <form onSubmit={handleSubmit}>
78
+ <div className='mt-5 w-full font-semibold'>{t('adminPrivileges')}</div>
79
+ <div className='flex'>
80
+ <Button
81
+ className='border border-foreground'
82
+ type='button'
83
+ variant={allChecked === true ? 'default' : 'secondary'}
84
+ onClick={(event) => {
85
+ event.preventDefault()
86
+ event.stopPropagation()
87
+ if (allChecked === true) {
88
+ setAllChecked(false)
89
+ } else {
90
+ setAllChecked(true)
91
+ }
92
+ }}
93
+ >
94
+ {allChecked === true ? t('removeAll') : t('checkAll')}
95
+ </Button>
96
+ </div>
97
+ <div className='mt-2 grid w-full grid-cols-1 gap-2 md-sidebar:grid-cols-2 lg-sidebar:grid-cols-3 xl-sidebar:grid-cols-4 2xl-sidebar:grid-cols-5'>
98
+ {data.allRoles.map((role, index: number) => {
99
+ const defaultValue = data.adminRoles.find((r) => r.sectionName === role.sectionName)
100
+ return (
101
+ <AdminRoleCard
102
+ key={index}
103
+ privilege={role}
104
+ allChecked={allChecked}
105
+ setAllChecked={setAllChecked}
106
+ defaultValue={{
107
+ operations: defaultValue?.operations.toLowerCase() || 'none',
108
+ publisher: defaultValue?.publisher || false,
109
+ }}
110
+ />
111
+ )
112
+ })}
113
+ </div>
114
+ <div className='mt-4'>
115
+ <button type='submit' className='rounded bg-blue-700 px-2 py-1 font-bold text-white'>
116
+ {t('save')}
117
+ </button>
118
+ </div>
119
+ </form>
120
+ ) : null}
121
+ </div>
122
+ </div>
123
+ )
124
+ }