create-nextjs-cms 0.5.8

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 (187) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +71 -0
  3. package/dist/index.d.ts +3 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +395 -0
  6. package/dist/lib/utils.d.ts +11 -0
  7. package/dist/lib/utils.d.ts.map +1 -0
  8. package/dist/lib/utils.js +48 -0
  9. package/package.json +44 -0
  10. package/templates/default/.env +24 -0
  11. package/templates/default/.env.development +8 -0
  12. package/templates/default/.eslintrc.json +5 -0
  13. package/templates/default/.prettierignore +7 -0
  14. package/templates/default/.prettierrc.json +19 -0
  15. package/templates/default/CHANGELOG.md +77 -0
  16. package/templates/default/README.md +45 -0
  17. package/templates/default/app/(auth)/auth/login/LoginPage.tsx +175 -0
  18. package/templates/default/app/(auth)/auth/login/page.tsx +12 -0
  19. package/templates/default/app/(rootLayout)/admins/page.tsx +5 -0
  20. package/templates/default/app/(rootLayout)/advanced/page.tsx +5 -0
  21. package/templates/default/app/(rootLayout)/analytics/page.tsx +7 -0
  22. package/templates/default/app/(rootLayout)/browse/[section]/[page]/page.tsx +7 -0
  23. package/templates/default/app/(rootLayout)/categorized/[section]/page.tsx +7 -0
  24. package/templates/default/app/(rootLayout)/dashboard/page.tsx +7 -0
  25. package/templates/default/app/(rootLayout)/edit/[section]/[itemId]/page.tsx +7 -0
  26. package/templates/default/app/(rootLayout)/emails/page.tsx +6 -0
  27. package/templates/default/app/(rootLayout)/layout.tsx +5 -0
  28. package/templates/default/app/(rootLayout)/loading.tsx +10 -0
  29. package/templates/default/app/(rootLayout)/log/page.tsx +7 -0
  30. package/templates/default/app/(rootLayout)/new/[section]/page.tsx +7 -0
  31. package/templates/default/app/(rootLayout)/page.tsx +9 -0
  32. package/templates/default/app/(rootLayout)/section/[section]/page.tsx +7 -0
  33. package/templates/default/app/(rootLayout)/settings/page.tsx +7 -0
  34. package/templates/default/app/_trpc/client.ts +4 -0
  35. package/templates/default/app/api/auth/csrf/route.ts +25 -0
  36. package/templates/default/app/api/auth/refresh/route.ts +10 -0
  37. package/templates/default/app/api/auth/route.ts +23 -0
  38. package/templates/default/app/api/auth/session/route.ts +20 -0
  39. package/templates/default/app/api/editor/photo/route.ts +42 -0
  40. package/templates/default/app/api/photo/route.ts +27 -0
  41. package/templates/default/app/api/placeholder/route.ts +7 -0
  42. package/templates/default/app/api/submit/section/item/[slug]/route.ts +63 -0
  43. package/templates/default/app/api/submit/section/item/route.ts +53 -0
  44. package/templates/default/app/api/submit/section/simple/route.ts +54 -0
  45. package/templates/default/app/api/trpc/[trpc]/route.ts +33 -0
  46. package/templates/default/app/api/video/route.ts +174 -0
  47. package/templates/default/app/dictionaries.ts +14 -0
  48. package/templates/default/app/layout.tsx +28 -0
  49. package/templates/default/app/providers.tsx +151 -0
  50. package/templates/default/cli.ts +4 -0
  51. package/templates/default/components/AdminCard.tsx +163 -0
  52. package/templates/default/components/AdminEditPage.tsx +123 -0
  53. package/templates/default/components/AdminPrivilegeCard.tsx +184 -0
  54. package/templates/default/components/AdminsPage.tsx +43 -0
  55. package/templates/default/components/AdvancedSettingsPage.tsx +167 -0
  56. package/templates/default/components/AnalyticsPage.tsx +127 -0
  57. package/templates/default/components/BarChartBox.tsx +43 -0
  58. package/templates/default/components/BrowsePage.tsx +119 -0
  59. package/templates/default/components/CategorizedSectionPage.tsx +36 -0
  60. package/templates/default/components/CategoryDeleteConfirmPage.tsx +129 -0
  61. package/templates/default/components/CategorySectionSelectInput.tsx +139 -0
  62. package/templates/default/components/ConditionalFields.tsx +49 -0
  63. package/templates/default/components/ContainerBox.tsx +24 -0
  64. package/templates/default/components/DashboardPage.tsx +187 -0
  65. package/templates/default/components/DashboardPageAlt.tsx +43 -0
  66. package/templates/default/components/DefaultNavItems.tsx +3 -0
  67. package/templates/default/components/Dropzone.tsx +153 -0
  68. package/templates/default/components/EmailCard.tsx +137 -0
  69. package/templates/default/components/EmailPasswordForm.tsx +84 -0
  70. package/templates/default/components/EmailQuotaForm.tsx +72 -0
  71. package/templates/default/components/EmailsPage.tsx +48 -0
  72. package/templates/default/components/GalleryPhoto.tsx +93 -0
  73. package/templates/default/components/InfoCard.tsx +94 -0
  74. package/templates/default/components/ItemEditPage.tsx +217 -0
  75. package/templates/default/components/Layout.tsx +70 -0
  76. package/templates/default/components/LoadingSpinners.tsx +67 -0
  77. package/templates/default/components/LogPage.tsx +17 -0
  78. package/templates/default/components/Modal.tsx +99 -0
  79. package/templates/default/components/Navbar.tsx +29 -0
  80. package/templates/default/components/NavbarAlt.tsx +182 -0
  81. package/templates/default/components/NewAdminForm.tsx +172 -0
  82. package/templates/default/components/NewEmailForm.tsx +131 -0
  83. package/templates/default/components/NewPage.tsx +206 -0
  84. package/templates/default/components/NewVariantComponent.tsx +228 -0
  85. package/templates/default/components/PhotoGallery.tsx +35 -0
  86. package/templates/default/components/PieChartBox.tsx +101 -0
  87. package/templates/default/components/ProgressBar.tsx +24 -0
  88. package/templates/default/components/ProtectedDocument.tsx +78 -0
  89. package/templates/default/components/ProtectedImage.tsx +143 -0
  90. package/templates/default/components/ProtectedVideo.tsx +76 -0
  91. package/templates/default/components/SectionItemCard.tsx +143 -0
  92. package/templates/default/components/SectionItemStatusBadge.tsx +16 -0
  93. package/templates/default/components/SectionPage.tsx +124 -0
  94. package/templates/default/components/SelectBox.tsx +99 -0
  95. package/templates/default/components/SelectInputButtons.tsx +124 -0
  96. package/templates/default/components/SettingsPage.tsx +238 -0
  97. package/templates/default/components/Sidebar.tsx +209 -0
  98. package/templates/default/components/SidebarDropdownItem.tsx +74 -0
  99. package/templates/default/components/SidebarItem.tsx +19 -0
  100. package/templates/default/components/TempPage.tsx +12 -0
  101. package/templates/default/components/ThemeProvider.tsx +8 -0
  102. package/templates/default/components/TooltipComponent.tsx +27 -0
  103. package/templates/default/components/VariantCard.tsx +123 -0
  104. package/templates/default/components/VariantEditPage.tsx +229 -0
  105. package/templates/default/components/analytics/BounceRate.tsx +69 -0
  106. package/templates/default/components/analytics/LivePageViews.tsx +54 -0
  107. package/templates/default/components/analytics/LiveUsersCount.tsx +32 -0
  108. package/templates/default/components/analytics/MonthlyPageViews.tsx +41 -0
  109. package/templates/default/components/analytics/TopCountries.tsx +51 -0
  110. package/templates/default/components/analytics/TopDevices.tsx +45 -0
  111. package/templates/default/components/analytics/TopMediums.tsx +57 -0
  112. package/templates/default/components/analytics/TopSources.tsx +44 -0
  113. package/templates/default/components/analytics/TotalPageViews.tsx +40 -0
  114. package/templates/default/components/analytics/TotalSessions.tsx +40 -0
  115. package/templates/default/components/analytics/TotalUniqueUsers.tsx +40 -0
  116. package/templates/default/components/custom/RightHomeRoomVariantCard.tsx +137 -0
  117. package/templates/default/components/dndKit/Draggable.tsx +21 -0
  118. package/templates/default/components/dndKit/Droppable.tsx +20 -0
  119. package/templates/default/components/dndKit/SortableItem.tsx +18 -0
  120. package/templates/default/components/form/DateRangeFormInput.tsx +55 -0
  121. package/templates/default/components/form/Form.tsx +298 -0
  122. package/templates/default/components/form/FormInputElement.tsx +68 -0
  123. package/templates/default/components/form/FormInputs.tsx +108 -0
  124. package/templates/default/components/form/helpers/util.ts +20 -0
  125. package/templates/default/components/form/inputs/CheckboxFormInput.tsx +33 -0
  126. package/templates/default/components/form/inputs/ColorFormInput.tsx +44 -0
  127. package/templates/default/components/form/inputs/DateFormInput.tsx +107 -0
  128. package/templates/default/components/form/inputs/DocumentFormInput.tsx +124 -0
  129. package/templates/default/components/form/inputs/MapFormInput.tsx +139 -0
  130. package/templates/default/components/form/inputs/MultipleSelectFormInput.tsx +150 -0
  131. package/templates/default/components/form/inputs/NumberFormInput.tsx +42 -0
  132. package/templates/default/components/form/inputs/PasswordFormInput.tsx +47 -0
  133. package/templates/default/components/form/inputs/PhotoFormInput.tsx +218 -0
  134. package/templates/default/components/form/inputs/RichTextFormInput.tsx +133 -0
  135. package/templates/default/components/form/inputs/SelectFormInput.tsx +164 -0
  136. package/templates/default/components/form/inputs/TagsFormInput.tsx +63 -0
  137. package/templates/default/components/form/inputs/TextFormInput.tsx +48 -0
  138. package/templates/default/components/form/inputs/TextareaFormInput.tsx +47 -0
  139. package/templates/default/components/form/inputs/VideoFormInput.tsx +117 -0
  140. package/templates/default/components/pagination/Pagination.tsx +36 -0
  141. package/templates/default/components/pagination/PaginationButtons.tsx +145 -0
  142. package/templates/default/components/ui/accordion.tsx +57 -0
  143. package/templates/default/components/ui/alert.tsx +46 -0
  144. package/templates/default/components/ui/badge.tsx +33 -0
  145. package/templates/default/components/ui/button.tsx +57 -0
  146. package/templates/default/components/ui/calendar.tsx +68 -0
  147. package/templates/default/components/ui/card.tsx +76 -0
  148. package/templates/default/components/ui/checkbox.tsx +29 -0
  149. package/templates/default/components/ui/dropdown-menu.tsx +205 -0
  150. package/templates/default/components/ui/input.tsx +25 -0
  151. package/templates/default/components/ui/label.tsx +26 -0
  152. package/templates/default/components/ui/popover.tsx +31 -0
  153. package/templates/default/components/ui/scroll-area.tsx +42 -0
  154. package/templates/default/components/ui/select.tsx +164 -0
  155. package/templates/default/components/ui/sheet.tsx +107 -0
  156. package/templates/default/components/ui/switch.tsx +29 -0
  157. package/templates/default/components/ui/table.tsx +120 -0
  158. package/templates/default/components/ui/tabs.tsx +55 -0
  159. package/templates/default/components/ui/toast.tsx +113 -0
  160. package/templates/default/components/ui/toaster.tsx +35 -0
  161. package/templates/default/components/ui/tooltip.tsx +30 -0
  162. package/templates/default/components/ui/use-toast.ts +188 -0
  163. package/templates/default/components.json +16 -0
  164. package/templates/default/context/ModalProvider.tsx +53 -0
  165. package/templates/default/drizzle.config.ts +4 -0
  166. package/templates/default/dynamic-schemas/schema.ts +373 -0
  167. package/templates/default/env/env.js +130 -0
  168. package/templates/default/envConfig.ts +4 -0
  169. package/templates/default/hooks/useModal.ts +8 -0
  170. package/templates/default/lib/apiHelpers.ts +106 -0
  171. package/templates/default/lz.config.ts +40 -0
  172. package/templates/default/middleware.ts +33 -0
  173. package/templates/default/next.config.ts +46 -0
  174. package/templates/default/package.json +134 -0
  175. package/templates/default/postcss.config.js +6 -0
  176. package/templates/default/postinstall.js +14 -0
  177. package/templates/default/public/blank_avatar.png +0 -0
  178. package/templates/default/public/favicon.ico +0 -0
  179. package/templates/default/public/img/placeholder.svg +1 -0
  180. package/templates/default/public/lazemni_logo.png +0 -0
  181. package/templates/default/public/next.svg +1 -0
  182. package/templates/default/public/vercel.svg +1 -0
  183. package/templates/default/section-tests.ts +92 -0
  184. package/templates/default/styles/globals.css +88 -0
  185. package/templates/default/tailwind.config.js +95 -0
  186. package/templates/default/test.ts +77 -0
  187. package/templates/default/tsconfig.json +44 -0
@@ -0,0 +1,163 @@
1
+ import getString from 'nextjs-cms/translations'
2
+ import React from 'react'
3
+ import useModal from '@/hooks/useModal'
4
+ import ProtectedImage from '@/components/ProtectedImage'
5
+ import AdminEditPage from '@/components/AdminEditPage'
6
+ import { useToast } from '@/components/ui/use-toast'
7
+ import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
8
+ import { Badge } from '@/components/ui/badge'
9
+ import { Button } from '@/components/ui/button'
10
+ import { trpc } from '@/app/_trpc/client'
11
+ import Image from 'next/image'
12
+ import { RouterOutputs } from 'nextjs-cms/api'
13
+
14
+ export default function AdminCard({
15
+ admin,
16
+ action,
17
+ }: {
18
+ admin: RouterOutputs['admins']['list']['admins'][0]
19
+ action: any
20
+ }) {
21
+ const { setModal, modal, modalResponse, setModalResponse } = useModal()
22
+ const { toast } = useToast()
23
+
24
+ const deleteMutation = trpc.admins.remove.useMutation({
25
+ onSuccess: () => {
26
+ action()
27
+ setModal(null)
28
+ setModalResponse(null)
29
+ toast({
30
+ variant: 'success',
31
+ title: getString('delete_admin'),
32
+ description: getString('adminDeletedSuccessfully'),
33
+ })
34
+ },
35
+ onError: (error) => {
36
+ toast({
37
+ variant: 'destructive',
38
+ title: getString('delete_admin'),
39
+ description: error.message,
40
+ })
41
+ },
42
+ })
43
+
44
+ const handleDeleteAdmin = async () => {
45
+ if ([1, '1'].includes(admin.id)) {
46
+ toast({
47
+ variant: 'destructive',
48
+ title: getString('delete_admin'),
49
+ description: getString('masterAdminCannotBeDeleted'),
50
+ })
51
+ return
52
+ }
53
+ deleteMutation.mutate({
54
+ id: admin.id,
55
+ })
56
+ }
57
+
58
+ return (
59
+ <Card className='flex flex-col justify-between border border-gray-400 shadow-sm'>
60
+ <CardHeader>
61
+ <CardTitle>
62
+ <div className='flex flex-row items-center gap-2'>
63
+ <div>
64
+ {admin.avatar ? (
65
+ <ProtectedImage
66
+ section={'admins'}
67
+ photo={admin.avatar}
68
+ isThumb={true}
69
+ alt={'admin'}
70
+ width={50}
71
+ height={50}
72
+ />
73
+ ) : (
74
+ <Image
75
+ src='/blank_avatar.png'
76
+ height={50}
77
+ width={50}
78
+ alt={admin.username}
79
+ className='rounded-full ring-2 ring-inset ring-amber-300'
80
+ />
81
+ )}
82
+ </div>
83
+ <div className='flex flex-col break-all'>
84
+ <div className='text-sm font-medium text-foreground'>
85
+ <div className='font-bold'>{admin.username}</div>
86
+ </div>
87
+ </div>
88
+ </div>
89
+ </CardTitle>
90
+ </CardHeader>
91
+ <CardContent>
92
+ <div className='flex flex-wrap gap-2'>
93
+ {admin.roles &&
94
+ admin.roles.length > 0 &&
95
+ admin.roles.map((role, index: number) => (
96
+ <Badge key={index} className='border-foreground' variant='secondary'>
97
+ {role.sectionName} ({role.operations})
98
+ </Badge>
99
+ ))}
100
+ </div>
101
+ </CardContent>
102
+ <CardFooter className='flex flex-wrap gap-4'>
103
+ <Button
104
+ variant='default'
105
+ onClick={() => {
106
+ setModal({
107
+ title: getString('edit_admin'),
108
+ body: [1, '1'].includes(admin.id) ? (
109
+ <div className='p-4'>{getString('masterAdminCannotBeModified')}</div>
110
+ ) : (
111
+ <div className='p-4'>{<AdminEditPage id={admin.id} action={() => action()} />}</div>
112
+ ),
113
+ headerColor: 'bg-sky-700',
114
+ titleColor: 'text-white',
115
+ lang: 'en',
116
+ size: 'xl',
117
+ })
118
+ }}
119
+ >
120
+ {getString('edit')}
121
+ </Button>
122
+ <Button
123
+ variant='destructive'
124
+ onClick={() => {
125
+ setModal({
126
+ title: getString('delete_admin'),
127
+ body: [1, '1'].includes(admin.id) ? (
128
+ <div className='p-4'>{getString('masterAdminCannotBeDeleted')}</div>
129
+ ) : (
130
+ <div className='p-4'>
131
+ <div className='flex flex-col gap-4'>
132
+ <div>{getString('delete_admin_text')}</div>
133
+ <div className='flex gap-2'>
134
+ <button
135
+ className='rounded bg-green-600 px-2 py-1 text-white'
136
+ onClick={handleDeleteAdmin}
137
+ >
138
+ Yes
139
+ </button>
140
+ <button
141
+ className='rounded bg-red-800 px-2 py-1 text-white'
142
+ onClick={() => {
143
+ setModal(null)
144
+ }}
145
+ >
146
+ No
147
+ </button>
148
+ </div>
149
+ </div>
150
+ </div>
151
+ ),
152
+ headerColor: 'bg-red-700',
153
+ titleColor: 'text-white',
154
+ lang: 'en',
155
+ })
156
+ }}
157
+ >
158
+ {getString('delete')}
159
+ </Button>
160
+ </CardFooter>
161
+ </Card>
162
+ )
163
+ }
@@ -0,0 +1,123 @@
1
+ import getString from 'nextjs-cms/translations'
2
+ import React from 'react'
3
+ import useModal from '@/hooks/useModal'
4
+ import { trpc } from '@/app/_trpc/client'
5
+ import AdminRoleCard from './AdminPrivilegeCard'
6
+ import LoadingSpinners from './LoadingSpinners'
7
+ import { Button } from '@/components/ui/button'
8
+
9
+ export default function AdminEditPage({ id, action }: { id: string; action: any }) {
10
+ const { setModal } = useModal()
11
+ const [allChecked, setAllChecked] = React.useState<boolean | 'edited' | null>(null)
12
+ const utils = trpc.useUtils()
13
+
14
+ const submitMutation = trpc.admins.update.useMutation({
15
+ onError: (error) => {
16
+ setModal({
17
+ title: getString('edit_admin'),
18
+ body: (
19
+ <div className='rounded border-2 border-dashed border-red-500 bg-red-100 p-2 text-red-800'>
20
+ {error.message}
21
+ </div>
22
+ ),
23
+ headerColor: 'bg-red-700',
24
+ titleColor: 'text-white',
25
+ lang: 'en',
26
+ })
27
+ },
28
+
29
+ onSuccess: (data) => {
30
+ action()
31
+ utils.admins.get.invalidate(id)
32
+ setModal({
33
+ title: getString('edit_admin'),
34
+ body: (
35
+ <div className='rounded border-2 border-dashed border-green-500 bg-green-100 p-2 text-green-800'>
36
+ {data.status}
37
+ </div>
38
+ ),
39
+ headerColor: 'bg-green-700',
40
+ titleColor: 'text-white',
41
+ lang: 'en',
42
+ })
43
+ },
44
+ })
45
+
46
+ const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
47
+ e.preventDefault()
48
+
49
+ const data = new FormData(e.currentTarget)
50
+ const values = Object.fromEntries(data.entries())
51
+
52
+ /**
53
+ * Convert the privileges to an array of objects
54
+ */
55
+ const privileges = Object.entries(values).filter(([key]) => key.includes('privileges'))
56
+ const privilegesArray = privileges.map(([key, value]) => JSON.parse(value as string))
57
+
58
+ submitMutation.mutate({
59
+ id: id,
60
+ privileges: privilegesArray,
61
+ })
62
+ }
63
+
64
+ const { data, error, isLoading, isError } = trpc.admins.get.useQuery(id)
65
+ if (isError) {
66
+ return <div>{error?.message}</div>
67
+ }
68
+ return (
69
+ <div>
70
+ <h2>
71
+ {getString('edit_admin_text')}: {data?.admin.user}
72
+ </h2>
73
+ <div>
74
+ {isLoading ? <LoadingSpinners /> : null}
75
+ {data && data.allRoles && data.allRoles.length > 0 ? (
76
+ <form onSubmit={handleSubmit}>
77
+ <div className='mt-5 w-full font-semibold'>{getString('admin_privileges')}</div>
78
+ <div className='flex'>
79
+ <Button
80
+ className='border border-foreground'
81
+ type='button'
82
+ variant={allChecked === true ? 'default' : 'secondary'}
83
+ onClick={(event) => {
84
+ event.preventDefault()
85
+ event.stopPropagation()
86
+ if (allChecked === true) {
87
+ setAllChecked(false)
88
+ } else {
89
+ setAllChecked(true)
90
+ }
91
+ }}
92
+ >
93
+ {allChecked === true ? getString('removeAll') : getString('checkAll')}
94
+ </Button>
95
+ </div>
96
+ <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'>
97
+ {data.allRoles.map((role, index: number) => {
98
+ const defaultValue = data.adminRoles.find((r) => r.sectionName === role.sectionName)
99
+ return (
100
+ <AdminRoleCard
101
+ key={index}
102
+ privilege={role}
103
+ allChecked={allChecked}
104
+ setAllChecked={setAllChecked}
105
+ defaultValue={{
106
+ operations: defaultValue?.operations.toLowerCase() || 'none',
107
+ publisher: defaultValue?.publisher || false,
108
+ }}
109
+ />
110
+ )
111
+ })}
112
+ </div>
113
+ <div className='mt-4'>
114
+ <button type='submit' className='rounded bg-blue-700 px-2 py-1 font-bold text-white'>
115
+ {getString('save')}
116
+ </button>
117
+ </div>
118
+ </form>
119
+ ) : null}
120
+ </div>
121
+ </div>
122
+ )
123
+ }
@@ -0,0 +1,184 @@
1
+ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
2
+ import { Checkbox } from '@/components/ui/checkbox'
3
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
4
+ import React, { useEffect } from 'react'
5
+ import getString from 'nextjs-cms/translations'
6
+ import { Badge } from '@/components/ui/badge'
7
+ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
8
+ import { RouterOutputs } from 'nextjs-cms/api'
9
+
10
+ export default function AdminRoleCard({
11
+ privilege,
12
+ allChecked,
13
+ setAllChecked,
14
+ defaultValue = {
15
+ operations: 'none',
16
+ publisher: false,
17
+ }
18
+ }: {
19
+ privilege: RouterOutputs['admins']['list']['privileges'][0]
20
+ allChecked: boolean | 'edited' | null
21
+ setAllChecked: React.Dispatch<React.SetStateAction<boolean | 'edited' | null>>
22
+ defaultValue?: {
23
+ operations: string
24
+ publisher: boolean
25
+ }
26
+ }) {
27
+ const [checked, setChecked] = React.useState<boolean | 'indeterminate'>(defaultValue.operations === 'cud')
28
+ const [publisherChecked, setPublisherChecked] = React.useState<boolean>(defaultValue.publisher)
29
+ const [value, setValue] = React.useState<string>(defaultValue.operations)
30
+
31
+ useEffect(() => {
32
+ if(allChecked === null) return
33
+ if (allChecked === true) {
34
+ setChecked(true)
35
+ setValue('cud')
36
+ setPublisherChecked(true)
37
+ } else if (allChecked === false) {
38
+ setChecked(false)
39
+ setValue('none')
40
+ setPublisherChecked(false)
41
+ }
42
+ }, [allChecked])
43
+
44
+ return (
45
+ <Card className='flex flex-col'>
46
+ <CardHeader>
47
+ <CardTitle>{privilege.title}</CardTitle>
48
+ </CardHeader>
49
+ <CardContent>
50
+ <input
51
+ type='hidden'
52
+ name={`privileges[${privilege.sectionName}]`}
53
+ value={JSON.stringify({
54
+ operations: value,
55
+ publisher: privilege.sectionType === 'has_items' ? publisherChecked : null,
56
+ sectionName: privilege.sectionName,
57
+ })}
58
+ />
59
+ <div className='flex flex-row'>
60
+ <Checkbox
61
+ onClick={() => {
62
+ setChecked(() => {
63
+ switch (checked) {
64
+ case 'indeterminate':
65
+ setValue('none')
66
+ setPublisherChecked(false)
67
+ setAllChecked('edited')
68
+ return false
69
+ case false:
70
+ setValue('cud')
71
+ return true
72
+ case true:
73
+ setValue('none')
74
+ setPublisherChecked(false)
75
+ setAllChecked('edited')
76
+ return false
77
+ }
78
+ })
79
+ }}
80
+ checked={checked}
81
+ // name={privilege.title}
82
+ id={privilege.title + '_new'}
83
+ />
84
+ <label
85
+ htmlFor={privilege.title + '_new'}
86
+ className='ms-2 block text-sm font-medium text-foreground'
87
+ >
88
+ Full Access
89
+ </label>
90
+ </div>
91
+
92
+ {privilege.sectionType === 'has_items' ? (
93
+ <div className='flex flex-row'>
94
+ <Checkbox
95
+ disabled={value === 'none'}
96
+ onClick={() => {
97
+ if (publisherChecked) {
98
+ setAllChecked('edited')
99
+ }
100
+ setPublisherChecked(!publisherChecked)
101
+ }}
102
+ checked={value === 'none' ? false : publisherChecked}
103
+ // name={`${privilege.title}_publisher`}
104
+ id={`${privilege.title}_publisher`}
105
+ />
106
+ <label
107
+ htmlFor={`${privilege.title}_publisher`}
108
+ className={`ms-2 block text-sm font-medium text-foreground`}
109
+ >
110
+ <span className={`${value === 'none' ? 'opacity-40' : ''}`}>Publisher</span>{' '}
111
+ <TooltipProvider delayDuration={0}>
112
+ <Tooltip>
113
+ <TooltipTrigger asChild>
114
+ <span>
115
+ <Badge variant='outline'>?</Badge>
116
+ </span>
117
+ </TooltipTrigger>
118
+ <TooltipContent>
119
+ <p>Only publishers can publish/unpublish items</p>
120
+ </TooltipContent>
121
+ </Tooltip>
122
+ </TooltipProvider>
123
+ </label>
124
+ </div>
125
+ ) : null}
126
+
127
+ <div className='mt-2 flex flex-col border-t p-2'>
128
+ <div className='text-sm'>Custom:</div>
129
+ <Select
130
+ defaultValue={'none'}
131
+ value={value}
132
+ onValueChange={(value: string) => {
133
+ setValue(value)
134
+ switch (value) {
135
+ case 'cud':
136
+ setChecked(true)
137
+ break
138
+ case 'none':
139
+ setChecked(false)
140
+ setPublisherChecked(false)
141
+ setAllChecked('edited')
142
+ break
143
+ default:
144
+ setChecked('indeterminate')
145
+ setAllChecked('edited')
146
+ break
147
+ }
148
+ }}
149
+ >
150
+ <SelectTrigger className='w-[180px]'>
151
+ <SelectValue placeholder={getString('select')} />
152
+ </SelectTrigger>
153
+ <SelectContent>
154
+ <SelectItem value='none'>
155
+ <Badge variant='outline'>No Access</Badge>
156
+ </SelectItem>
157
+ <SelectItem value='c'>
158
+ <Badge variant='secondary'>C</Badge> <span className='italic'>Create</span>
159
+ </SelectItem>
160
+ <SelectItem value='u'>
161
+ <Badge variant='secondary'>U</Badge> <span className='italic'>Update</span>
162
+ </SelectItem>
163
+ <SelectItem value='d'>
164
+ <Badge variant='secondary'>D</Badge> <span className='italic'>Delete</span>
165
+ </SelectItem>
166
+ <SelectItem value='cu'>
167
+ <Badge variant='secondary'>C + U</Badge>
168
+ </SelectItem>
169
+ <SelectItem value='cd'>
170
+ <Badge variant='secondary'>C + D</Badge>
171
+ </SelectItem>
172
+ <SelectItem value='ud'>
173
+ <Badge variant='secondary'>U + D</Badge>
174
+ </SelectItem>
175
+ <SelectItem value='cud'>
176
+ <Badge variant='success'>C + U + D</Badge> (All)
177
+ </SelectItem>
178
+ </SelectContent>
179
+ </Select>
180
+ </div>
181
+ </CardContent>
182
+ </Card>
183
+ )
184
+ }
@@ -0,0 +1,43 @@
1
+ 'use client'
2
+
3
+ import AdminCard from '@/components/AdminCard'
4
+ import getString from 'nextjs-cms/translations'
5
+ import NewAdminForm from '@/components/NewAdminForm'
6
+ import ContainerBox from '@/components/ContainerBox'
7
+ import LoadingSpinners from '@/components/LoadingSpinners'
8
+ import { trpc } from '@/app/_trpc/client'
9
+
10
+ const AdminsPage = () => {
11
+ const { isLoading, isError, data, error, refetch } = trpc.admins.list.useQuery()
12
+
13
+ return (
14
+ <div className='w-full'>
15
+ <div className='bg-gradient-to-r from-sky-200 via-emerald-300 to-blue-600 p-8 font-extrabold text-foreground dark:from-blue-800 dark:via-amber-700 dark:to-rose-900'>
16
+ <h1 className='text-3xl'>{getString('admins')}</h1>
17
+ </div>
18
+
19
+ <div className='flex flex-col gap-2 p-4'>
20
+ <ContainerBox title={getString('create_new_admin')}>
21
+ {data && data.privileges && <NewAdminForm action={() => refetch()} privileges={data.privileges} />}
22
+ </ContainerBox>
23
+
24
+ {isLoading && (
25
+ <div>
26
+ <LoadingSpinners />
27
+ </div>
28
+ )}
29
+ {data && data.admins && data.admins.length > 0 && (
30
+ <ContainerBox title={getString('admins_list')}>
31
+ <div className='mt-2 grid w-full grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3'>
32
+ {data.admins.map((admin) => (
33
+ <AdminCard key={admin.id} action={() => refetch()} admin={admin} />
34
+ ))}
35
+ </div>
36
+ </ContainerBox>
37
+ )}
38
+ </div>
39
+ </div>
40
+ )
41
+ }
42
+
43
+ export default AdminsPage