create-nextjs-cms 0.9.4 → 0.9.6

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 -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 +3 -3
  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)/layout.tsx +81 -81
  14. package/templates/default/app/(rootLayout)/(plugins)/[...slug]/page.tsx +40 -40
  15. package/templates/default/app/(rootLayout)/(plugins)/[...slug]/plugin-server-registry.ts +16 -16
  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 +63 -63
  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/_trpc/client.ts +3 -3
  28. package/templates/default/app/api/auth/csrf/route.ts +25 -25
  29. package/templates/default/app/api/auth/refresh/route.ts +10 -10
  30. package/templates/default/app/api/auth/route.ts +49 -49
  31. package/templates/default/app/api/auth/session/route.ts +20 -20
  32. package/templates/default/app/api/document/route.ts +165 -165
  33. package/templates/default/app/api/editor/photo/route.ts +49 -49
  34. package/templates/default/app/api/photo/route.ts +27 -27
  35. package/templates/default/app/api/submit/section/item/[slug]/route.ts +95 -95
  36. package/templates/default/app/api/submit/section/item/route.ts +56 -56
  37. package/templates/default/app/api/submit/section/simple/route.ts +86 -86
  38. package/templates/default/app/api/trpc/[trpc]/route.ts +33 -33
  39. package/templates/default/app/api/video/route.ts +174 -174
  40. package/templates/default/app/globals.css +228 -228
  41. package/templates/default/app/providers.tsx +152 -152
  42. package/templates/default/cms.config.ts +58 -57
  43. package/templates/default/components/AdminCard.tsx +166 -166
  44. package/templates/default/components/AdminEditPage.tsx +124 -124
  45. package/templates/default/components/AdminPrivilegeCard.tsx +185 -185
  46. package/templates/default/components/AdminsPage.tsx +43 -43
  47. package/templates/default/components/AnalyticsPage.tsx +144 -128
  48. package/templates/default/components/BarChartBox.tsx +42 -42
  49. package/templates/default/components/BrowsePage.tsx +106 -106
  50. package/templates/default/components/CategorizedSectionPage.tsx +31 -31
  51. package/templates/default/components/CategoryDeleteConfirmPage.tsx +130 -130
  52. package/templates/default/components/CategorySectionSelectInput.tsx +140 -140
  53. package/templates/default/components/ConditionalFields.tsx +49 -49
  54. package/templates/default/components/ContainerBox.tsx +24 -24
  55. package/templates/default/components/DashboardPageAlt.tsx +45 -45
  56. package/templates/default/components/DefaultNavItems.tsx +3 -3
  57. package/templates/default/components/Dropzone.tsx +154 -154
  58. package/templates/default/components/ErrorComponent.tsx +16 -16
  59. package/templates/default/components/GalleryPhoto.tsx +93 -93
  60. package/templates/default/components/InfoCard.tsx +93 -93
  61. package/templates/default/components/ItemEditPage.tsx +294 -294
  62. package/templates/default/components/Layout.tsx +84 -84
  63. package/templates/default/components/LoadingSpinners.tsx +67 -67
  64. package/templates/default/components/LocaleSwitcher.tsx +89 -89
  65. package/templates/default/components/LogPage.tsx +107 -107
  66. package/templates/default/components/Modal.tsx +166 -166
  67. package/templates/default/components/Navbar.tsx +258 -258
  68. package/templates/default/components/NewAdminForm.tsx +173 -173
  69. package/templates/default/components/NewPage.tsx +206 -206
  70. package/templates/default/components/NewVariantComponent.tsx +229 -229
  71. package/templates/default/components/PhotoGallery.tsx +35 -35
  72. package/templates/default/components/PieChartBox.tsx +101 -101
  73. package/templates/default/components/ProgressBar.tsx +48 -48
  74. package/templates/default/components/ProtectedDocument.tsx +44 -44
  75. package/templates/default/components/ProtectedImage.tsx +143 -143
  76. package/templates/default/components/ProtectedVideo.tsx +76 -76
  77. package/templates/default/components/SectionIcon.tsx +8 -8
  78. package/templates/default/components/SectionItemCard.tsx +144 -144
  79. package/templates/default/components/SectionItemStatusBadge.tsx +17 -17
  80. package/templates/default/components/SectionPage.tsx +205 -205
  81. package/templates/default/components/SelectBox.tsx +98 -98
  82. package/templates/default/components/SelectInputButtons.tsx +125 -125
  83. package/templates/default/components/SettingsPage.tsx +232 -232
  84. package/templates/default/components/Sidebar.tsx +204 -204
  85. package/templates/default/components/SidebarDropdownItem.tsx +83 -83
  86. package/templates/default/components/SidebarItem.tsx +24 -24
  87. package/templates/default/components/ThemeProvider.tsx +8 -8
  88. package/templates/default/components/TooltipComponent.tsx +27 -27
  89. package/templates/default/components/VariantCard.tsx +124 -124
  90. package/templates/default/components/VariantEditPage.tsx +230 -230
  91. package/templates/default/components/analytics/BounceRate.tsx +70 -70
  92. package/templates/default/components/analytics/LivePageViews.tsx +55 -55
  93. package/templates/default/components/analytics/LiveUsersCount.tsx +33 -33
  94. package/templates/default/components/analytics/MonthlyPageViews.tsx +42 -42
  95. package/templates/default/components/analytics/TopCountries.tsx +52 -52
  96. package/templates/default/components/analytics/TopDevices.tsx +46 -46
  97. package/templates/default/components/analytics/TopMediums.tsx +58 -58
  98. package/templates/default/components/analytics/TopSources.tsx +45 -45
  99. package/templates/default/components/analytics/TotalPageViews.tsx +41 -41
  100. package/templates/default/components/analytics/TotalSessions.tsx +41 -41
  101. package/templates/default/components/analytics/TotalUniqueUsers.tsx +41 -41
  102. package/templates/default/components/custom/RightHomeRoomVariantCard.tsx +138 -138
  103. package/templates/default/components/dndKit/Draggable.tsx +21 -21
  104. package/templates/default/components/dndKit/Droppable.tsx +20 -20
  105. package/templates/default/components/dndKit/SortableItem.tsx +18 -18
  106. package/templates/default/components/form/Form.tsx +370 -360
  107. package/templates/default/components/form/FormInputElement.tsx +70 -70
  108. package/templates/default/components/form/FormInputs.tsx +120 -111
  109. package/templates/default/components/form/helpers/_section-hot-reload.js +1 -1
  110. package/templates/default/components/form/helpers/util.ts +17 -17
  111. package/templates/default/components/form/inputs/CheckboxFormInput.tsx +46 -46
  112. package/templates/default/components/form/inputs/ColorFormInput.tsx +44 -44
  113. package/templates/default/components/form/inputs/DateFormInput.tsx +156 -156
  114. package/templates/default/components/form/inputs/DateRangeFormInput.tsx +153 -0
  115. package/templates/default/components/form/inputs/DocumentFormInput.tsx +222 -222
  116. package/templates/default/components/form/inputs/MapFormInput.tsx +140 -140
  117. package/templates/default/components/form/inputs/MultipleSelectFormInput.tsx +85 -85
  118. package/templates/default/components/form/inputs/NumberFormInput.tsx +43 -43
  119. package/templates/default/components/form/inputs/PasswordFormInput.tsx +47 -47
  120. package/templates/default/components/form/inputs/PhotoFormInput.tsx +275 -275
  121. package/templates/default/components/form/inputs/RichTextFormInput.tsx +138 -138
  122. package/templates/default/components/form/inputs/SelectFormInput.tsx +175 -175
  123. package/templates/default/components/form/inputs/SlugFormInput.tsx +131 -131
  124. package/templates/default/components/form/inputs/TagsFormInput.tsx +261 -260
  125. package/templates/default/components/form/inputs/TextFormInput.tsx +51 -51
  126. package/templates/default/components/form/inputs/TextareaFormInput.tsx +50 -50
  127. package/templates/default/components/form/inputs/VideoFormInput.tsx +118 -118
  128. package/templates/default/components/multi-select.tsx +1146 -1146
  129. package/templates/default/components/pagination/Pagination.tsx +36 -36
  130. package/templates/default/components/pagination/PaginationButtons.tsx +147 -147
  131. package/templates/default/components/theme-toggle.tsx +39 -39
  132. package/templates/default/components/ui/accordion.tsx +53 -53
  133. package/templates/default/components/ui/alert-dialog.tsx +157 -157
  134. package/templates/default/components/ui/alert.tsx +47 -47
  135. package/templates/default/components/ui/badge.tsx +38 -38
  136. package/templates/default/components/ui/button.tsx +62 -62
  137. package/templates/default/components/ui/calendar.tsx +166 -166
  138. package/templates/default/components/ui/card.tsx +43 -43
  139. package/templates/default/components/ui/checkbox.tsx +29 -29
  140. package/templates/default/components/ui/command.tsx +137 -137
  141. package/templates/default/components/ui/custom-alert-dialog.tsx +113 -113
  142. package/templates/default/components/ui/custom-dialog.tsx +123 -123
  143. package/templates/default/components/ui/dialog.tsx +123 -123
  144. package/templates/default/components/ui/direction.tsx +22 -22
  145. package/templates/default/components/ui/dropdown-menu.tsx +182 -182
  146. package/templates/default/components/ui/input-group.tsx +54 -54
  147. package/templates/default/components/ui/input.tsx +22 -22
  148. package/templates/default/components/ui/label.tsx +19 -19
  149. package/templates/default/components/ui/popover.tsx +42 -42
  150. package/templates/default/components/ui/progress.tsx +31 -31
  151. package/templates/default/components/ui/scroll-area.tsx +42 -42
  152. package/templates/default/components/ui/select.tsx +165 -165
  153. package/templates/default/components/ui/separator.tsx +28 -28
  154. package/templates/default/components/ui/sheet.tsx +103 -103
  155. package/templates/default/components/ui/spinner.tsx +16 -16
  156. package/templates/default/components/ui/switch.tsx +29 -29
  157. package/templates/default/components/ui/table.tsx +83 -83
  158. package/templates/default/components/ui/tabs.tsx +55 -55
  159. package/templates/default/components/ui/toast.tsx +113 -113
  160. package/templates/default/components/ui/toaster.tsx +35 -35
  161. package/templates/default/components/ui/tooltip.tsx +30 -30
  162. package/templates/default/components/ui/use-toast.ts +188 -188
  163. package/templates/default/components.json +21 -21
  164. package/templates/default/context/ModalProvider.tsx +53 -53
  165. package/templates/default/drizzle.config.ts +4 -4
  166. package/templates/default/env/env.js +130 -130
  167. package/templates/default/envConfig.ts +4 -4
  168. package/templates/default/hooks/useModal.ts +8 -8
  169. package/templates/default/lib/apiHelpers.ts +92 -92
  170. package/templates/default/lib/postinstall.js +14 -14
  171. package/templates/default/lib/utils.ts +6 -6
  172. package/templates/default/next-env.d.ts +6 -6
  173. package/templates/default/next.config.ts +23 -23
  174. package/templates/default/package.json +3 -3
  175. package/templates/default/postcss.config.mjs +6 -6
  176. package/templates/default/proxy.ts +32 -32
  177. package/templates/default/tsconfig.json +48 -48
  178. package/templates/default/app/(rootLayout)/dashboard-new/page.tsx +0 -7
  179. package/templates/default/components/DashboardNewPage.tsx +0 -253
  180. package/templates/default/components/DashboardPage.tsx +0 -188
  181. package/templates/default/components/EmailCard.tsx +0 -138
  182. package/templates/default/components/EmailPasswordForm.tsx +0 -85
  183. package/templates/default/components/EmailQuotaForm.tsx +0 -73
  184. package/templates/default/components/EmailsPage.tsx +0 -49
  185. package/templates/default/components/NewEmailForm.tsx +0 -132
  186. package/templates/default/components/form/DateRangeFormInput.tsx +0 -57
  187. package/templates/default/dynamic-schemas/schema.ts +0 -475
@@ -1,76 +1,76 @@
1
- import React, { useEffect } from 'react'
2
- import { useRefreshToken } from 'nextjs-cms/auth/hooks'
3
- import { useSession } from 'nextjs-cms/auth/react'
4
-
5
- const ProtectedVideo = ({
6
- section,
7
- fieldName,
8
- file,
9
- width,
10
- height,
11
- className = 'rounded-3xl object-cover',
12
- }: {
13
- section: string
14
- fieldName: string
15
- file: string
16
- width: number
17
- height: number
18
- className?: string
19
- }) => {
20
- const refresh = useRefreshToken()
21
- const ref = React.useRef<HTMLVideoElement>(null)
22
- const [currentTime, setCurrentTime] = React.useState(0)
23
- const [isSeeking, setIsSeeking] = React.useState(false)
24
-
25
- const session = useSession()
26
- useEffect(() => {
27
- if (ref.current && currentTime) {
28
- ref.current.currentTime = currentTime
29
- ref.current.play().then(() => {})
30
- }
31
- }, [session])
32
-
33
- return (
34
- <div className={className}>
35
- {/**
36
- * The video will stream as long as the access token is valid
37
- * If the access token expires, the video will pause
38
- * A refresh token request will be sent to `/api/auth/refresh`
39
- * If the refresh token is valid, the video will resume playing
40
- * If the refresh token is invalid, the user will be logged out
41
- */}
42
- <video
43
- ref={ref}
44
- src={`/api/video?sectionName=${section}&fieldName=${fieldName}&name=${file}`}
45
- className='max-w-full'
46
- width={width}
47
- controls={true}
48
- height={height}
49
- onErrorCapture={async (e) => {
50
- if (isSeeking) return
51
- setIsSeeking(true)
52
- // console.log('seeked')
53
- /**
54
- * Pause the video
55
- */
56
- e.currentTarget.pause()
57
- /**
58
- * Get the current time of the video and save it in the state
59
- */
60
- setCurrentTime(e.currentTarget.currentTime)
61
- /**
62
- * Refresh the access token
63
- * This will trigger the `useEffect` hook
64
- * and set the current time of the video
65
- * to the time it was paused at
66
- * and play the video
67
- */
68
- await refresh()
69
- setIsSeeking(false)
70
- }}
71
- />
72
- </div>
73
- )
74
- }
75
-
76
- export default ProtectedVideo
1
+ import React, { useEffect } from 'react'
2
+ import { useRefreshToken } from 'nextjs-cms/auth/hooks'
3
+ import { useSession } from 'nextjs-cms/auth/react'
4
+
5
+ const ProtectedVideo = ({
6
+ section,
7
+ fieldName,
8
+ file,
9
+ width,
10
+ height,
11
+ className = 'rounded-3xl object-cover',
12
+ }: {
13
+ section: string
14
+ fieldName: string
15
+ file: string
16
+ width: number
17
+ height: number
18
+ className?: string
19
+ }) => {
20
+ const refresh = useRefreshToken()
21
+ const ref = React.useRef<HTMLVideoElement>(null)
22
+ const [currentTime, setCurrentTime] = React.useState(0)
23
+ const [isSeeking, setIsSeeking] = React.useState(false)
24
+
25
+ const session = useSession()
26
+ useEffect(() => {
27
+ if (ref.current && currentTime) {
28
+ ref.current.currentTime = currentTime
29
+ ref.current.play().then(() => {})
30
+ }
31
+ }, [session])
32
+
33
+ return (
34
+ <div className={className}>
35
+ {/**
36
+ * The video will stream as long as the access token is valid
37
+ * If the access token expires, the video will pause
38
+ * A refresh token request will be sent to `/api/auth/refresh`
39
+ * If the refresh token is valid, the video will resume playing
40
+ * If the refresh token is invalid, the user will be logged out
41
+ */}
42
+ <video
43
+ ref={ref}
44
+ src={`/api/video?sectionName=${section}&fieldName=${fieldName}&name=${file}`}
45
+ className='max-w-full'
46
+ width={width}
47
+ controls={true}
48
+ height={height}
49
+ onErrorCapture={async (e) => {
50
+ if (isSeeking) return
51
+ setIsSeeking(true)
52
+ // console.log('seeked')
53
+ /**
54
+ * Pause the video
55
+ */
56
+ e.currentTarget.pause()
57
+ /**
58
+ * Get the current time of the video and save it in the state
59
+ */
60
+ setCurrentTime(e.currentTarget.currentTime)
61
+ /**
62
+ * Refresh the access token
63
+ * This will trigger the `useEffect` hook
64
+ * and set the current time of the video
65
+ * to the time it was paused at
66
+ * and play the video
67
+ */
68
+ await refresh()
69
+ setIsSeeking(false)
70
+ }}
71
+ />
72
+ </div>
73
+ )
74
+ }
75
+
76
+ export default ProtectedVideo
@@ -1,8 +1,8 @@
1
- 'use client'
2
-
3
- import {DynamicIcon} from 'lucide-react/dynamic'
4
- import type { IconName } from 'lucide-react/dynamic'
5
-
6
- export default function SectionIcon({ name, className }: { name: string; className?: string }) {
7
- return <DynamicIcon name={name as IconName} className={className} />
8
- }
1
+ 'use client'
2
+
3
+ import {DynamicIcon} from 'lucide-react/dynamic'
4
+ import type { IconName } from 'lucide-react/dynamic'
5
+
6
+ export default function SectionIcon({ name, className }: { name: string; className?: string }) {
7
+ return <DynamicIcon name={name as IconName} className={className} />
8
+ }
@@ -1,144 +1,144 @@
1
- import React from 'react'
2
- import { useI18n } from 'nextjs-cms/translations/client'
3
- import useModal from '@/hooks/useModal'
4
- import ProtectedImage from '@/components/ProtectedImage'
5
- import SectionItemStatusBadge from '@/components/SectionItemStatusBadge'
6
- import { displayDateFromString } from 'nextjs-cms/utils'
7
- import Link from 'next/link'
8
- import { CardTitle, CardHeader, CardFooter, Card, CardContent } from '@/components/ui/card'
9
- import { Button } from '@/components/ui/button'
10
- import { PersonIcon } from '@radix-ui/react-icons'
11
- import { trpc } from '@/app/_trpc/client'
12
- import type { RouterOutputs } from 'nextjs-cms/api'
13
-
14
- // Used to get elements of array types as types
15
- type ArrElement<ArrType> = ArrType extends readonly (infer ElementType)[] ? ElementType : never
16
-
17
- export default function SectionItemCard({
18
- sectionName,
19
- item,
20
- action,
21
- }: {
22
- sectionName: string
23
- item: ArrElement<RouterOutputs['hasItemsSections']['listItems']['items']>
24
- action: any
25
- }) {
26
- const t = useI18n()
27
- const { setModal } = useModal()
28
- const utils = trpc.useUtils()
29
- const deleteMutation = trpc.hasItemsSections.deleteItem.useMutation({
30
- onError: (error) => {
31
- setModal({
32
- title: t('delete'),
33
- body: (
34
- <div className='rounded border-2 border-dashed border-red-500 bg-red-100 p-2 text-red-800'>
35
- {error.message}
36
- </div>
37
- ),
38
- headerColor: 'bg-red-700',
39
- titleColor: 'text-white',
40
- lang: 'en',
41
- })
42
- },
43
-
44
- onSuccess: async (data) => {
45
- await utils.hasItemsSections.listItems.invalidate({
46
- sectionName: sectionName,
47
- })
48
- action()
49
- setModal({
50
- title: t('delete'),
51
- body: (
52
- <div className='rounded border-2 border-dashed border-green-500 bg-green-100 p-2 text-green-800'>
53
- {t('itemDeletedSuccessfully')}
54
- </div>
55
- ),
56
- headerColor: 'bg-green-700',
57
- titleColor: 'text-white',
58
- lang: 'en',
59
- })
60
- },
61
- })
62
- const handleDelete = async () => {
63
- deleteMutation.mutate({
64
- sectionName: sectionName,
65
- sectionItemId: item.id,
66
- })
67
- }
68
-
69
- return (
70
- <>
71
- <Card className='relative mx-auto w-full max-w-md overflow-hidden rounded-lg border-secondary-foreground pb-[85px] shadow-sm'>
72
- {item.coverPhoto ? (
73
- <div className='relative h-[200px]'>
74
- {[0, 1].includes(item.permission) ? <SectionItemStatusBadge status={item.permission} /> : null}
75
- <ProtectedImage
76
- className='h-64 w-full object-cover'
77
- photo={item.coverPhoto}
78
- section={sectionName}
79
- isThumb={true}
80
- alt={'item'}
81
- width={100}
82
- height={50}
83
- fill={true}
84
- />
85
- </div>
86
- ) : null}
87
- <CardHeader className='p-6'>
88
- <CardTitle className='text-xl font-bold'>{item.headingTitle}</CardTitle>
89
- </CardHeader>
90
- <CardContent className='mt-2'>
91
- <div className='flex flex-row flex-wrap gap-1'>
92
- <PersonIcon fontSize='small' /> {item.createdBy},{' '}
93
- {displayDateFromString(item.createdAt, 'en', false, ',')}
94
- </div>
95
- </CardContent>
96
- <CardFooter className='absolute bottom-0 start-0 flex h-[85px] w-full justify-between p-6'>
97
- <Link
98
- href={`/edit/${sectionName}/${item.id}`}
99
- className='rounded bg-primary px-3 py-1 font-semibold text-primary-foreground'
100
- >
101
- {t('edit')}
102
- </Link>
103
- <Button
104
- onClick={() => {
105
- setModal({
106
- title: t('delete'),
107
- body: (
108
- <div className='p-4'>
109
- <div className='flex flex-col gap-4'>
110
- <div>{t('deleteItemText')}</div>
111
- <div className='flex gap-2'>
112
- <button
113
- className='rounded bg-success px-2 py-1 text-success-foreground'
114
- onClick={handleDelete}
115
- >
116
- Yes
117
- </button>
118
- <button
119
- className='rounded bg-destructive px-2 py-1 text-destructive-foreground'
120
- onClick={() => {
121
- setModal(null)
122
- }}
123
- >
124
- No
125
- </button>
126
- </div>
127
- </div>
128
- </div>
129
- ),
130
- headerColor: 'bg-red-700',
131
- titleColor: 'text-white',
132
- lang: 'en',
133
- })
134
- }}
135
- variant='destructive'
136
- className='rounded px-3 py-1 font-semibold'
137
- >
138
- {t('delete')}
139
- </Button>
140
- </CardFooter>
141
- </Card>
142
- </>
143
- )
144
- }
1
+ import React from 'react'
2
+ import { useI18n } from 'nextjs-cms/translations/client'
3
+ import useModal from '@/hooks/useModal'
4
+ import ProtectedImage from '@/components/ProtectedImage'
5
+ import SectionItemStatusBadge from '@/components/SectionItemStatusBadge'
6
+ import { displayDateFromString } from 'nextjs-cms/utils'
7
+ import Link from 'next/link'
8
+ import { CardTitle, CardHeader, CardFooter, Card, CardContent } from '@/components/ui/card'
9
+ import { Button } from '@/components/ui/button'
10
+ import { PersonIcon } from '@radix-ui/react-icons'
11
+ import { trpc } from '@/app/_trpc/client'
12
+ import type { RouterOutputs } from 'nextjs-cms/api'
13
+
14
+ // Used to get elements of array types as types
15
+ type ArrElement<ArrType> = ArrType extends readonly (infer ElementType)[] ? ElementType : never
16
+
17
+ export default function SectionItemCard({
18
+ sectionName,
19
+ item,
20
+ action,
21
+ }: {
22
+ sectionName: string
23
+ item: ArrElement<RouterOutputs['hasItemsSections']['listItems']['items']>
24
+ action: any
25
+ }) {
26
+ const t = useI18n()
27
+ const { setModal } = useModal()
28
+ const utils = trpc.useUtils()
29
+ const deleteMutation = trpc.hasItemsSections.deleteItem.useMutation({
30
+ onError: (error) => {
31
+ setModal({
32
+ title: t('delete'),
33
+ body: (
34
+ <div className='rounded border-2 border-dashed border-red-500 bg-red-100 p-2 text-red-800'>
35
+ {error.message}
36
+ </div>
37
+ ),
38
+ headerColor: 'bg-red-700',
39
+ titleColor: 'text-white',
40
+ lang: 'en',
41
+ })
42
+ },
43
+
44
+ onSuccess: async (data) => {
45
+ await utils.hasItemsSections.listItems.invalidate({
46
+ sectionName: sectionName,
47
+ })
48
+ action()
49
+ setModal({
50
+ title: t('delete'),
51
+ body: (
52
+ <div className='rounded border-2 border-dashed border-green-500 bg-green-100 p-2 text-green-800'>
53
+ {t('itemDeletedSuccessfully')}
54
+ </div>
55
+ ),
56
+ headerColor: 'bg-green-700',
57
+ titleColor: 'text-white',
58
+ lang: 'en',
59
+ })
60
+ },
61
+ })
62
+ const handleDelete = async () => {
63
+ deleteMutation.mutate({
64
+ sectionName: sectionName,
65
+ sectionItemId: item.id,
66
+ })
67
+ }
68
+
69
+ return (
70
+ <>
71
+ <Card className='relative mx-auto w-full max-w-md overflow-hidden rounded-lg border-secondary-foreground pb-[85px] shadow-sm'>
72
+ {item.coverPhoto ? (
73
+ <div className='relative h-[200px]'>
74
+ {[0, 1].includes(item.permission) ? <SectionItemStatusBadge status={item.permission} /> : null}
75
+ <ProtectedImage
76
+ className='h-64 w-full object-cover'
77
+ photo={item.coverPhoto}
78
+ section={sectionName}
79
+ isThumb={true}
80
+ alt={'item'}
81
+ width={100}
82
+ height={50}
83
+ fill={true}
84
+ />
85
+ </div>
86
+ ) : null}
87
+ <CardHeader className='p-6'>
88
+ <CardTitle className='text-xl font-bold'>{item.headingTitle}</CardTitle>
89
+ </CardHeader>
90
+ <CardContent className='mt-2'>
91
+ <div className='flex flex-row flex-wrap gap-1'>
92
+ <PersonIcon fontSize='small' /> {item.createdBy},{' '}
93
+ {displayDateFromString(item.createdAt, 'en', false, ',')}
94
+ </div>
95
+ </CardContent>
96
+ <CardFooter className='absolute bottom-0 start-0 flex h-[85px] w-full justify-between p-6'>
97
+ <Link
98
+ href={`/edit/${sectionName}/${item.id}`}
99
+ className='rounded bg-primary px-3 py-1 font-semibold text-primary-foreground'
100
+ >
101
+ {t('edit')}
102
+ </Link>
103
+ <Button
104
+ onClick={() => {
105
+ setModal({
106
+ title: t('delete'),
107
+ body: (
108
+ <div className='p-4'>
109
+ <div className='flex flex-col gap-4'>
110
+ <div>{t('deleteItemText')}</div>
111
+ <div className='flex gap-2'>
112
+ <button
113
+ className='rounded bg-success px-2 py-1 text-success-foreground'
114
+ onClick={handleDelete}
115
+ >
116
+ Yes
117
+ </button>
118
+ <button
119
+ className='rounded bg-destructive px-2 py-1 text-destructive-foreground'
120
+ onClick={() => {
121
+ setModal(null)
122
+ }}
123
+ >
124
+ No
125
+ </button>
126
+ </div>
127
+ </div>
128
+ </div>
129
+ ),
130
+ headerColor: 'bg-red-700',
131
+ titleColor: 'text-white',
132
+ lang: 'en',
133
+ })
134
+ }}
135
+ variant='destructive'
136
+ className='rounded px-3 py-1 font-semibold'
137
+ >
138
+ {t('delete')}
139
+ </Button>
140
+ </CardFooter>
141
+ </Card>
142
+ </>
143
+ )
144
+ }
@@ -1,17 +1,17 @@
1
- import React from 'react'
2
- import { useI18n } from 'nextjs-cms/translations/client'
3
- import { Badge } from '@/components/ui/badge'
4
-
5
- const SectionItemStatusBadge = ({ status }: { status: number }) => {
6
- const t = useI18n()
7
- return (
8
- <Badge
9
- variant={status === 1 ? 'success' : 'destructive'}
10
- className='absolute top-3 shadow-sm left-3 z-30 font-bold'
11
- >
12
- {status === 1 ? t('approved') : t('pendingApproval')}
13
- </Badge>
14
- )
15
- }
16
-
17
- export default SectionItemStatusBadge
1
+ import React from 'react'
2
+ import { useI18n } from 'nextjs-cms/translations/client'
3
+ import { Badge } from '@/components/ui/badge'
4
+
5
+ const SectionItemStatusBadge = ({ status }: { status: number }) => {
6
+ const t = useI18n()
7
+ return (
8
+ <Badge
9
+ variant={status === 1 ? 'success' : 'destructive'}
10
+ className='absolute top-3 shadow-sm left-3 z-30 font-bold'
11
+ >
12
+ {status === 1 ? t('approved') : t('pendingApproval')}
13
+ </Badge>
14
+ )
15
+ }
16
+
17
+ export default SectionItemStatusBadge