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,117 @@
1
+ import React, { useRef, useState } from 'react'
2
+ import getString from 'nextjs-cms/translations'
3
+ import FormInputElement from '@/components/form/FormInputElement'
4
+ import ProtectedVideo from '@/components/ProtectedVideo'
5
+ import { VideoFieldClientConfig } from 'nextjs-cms/core/fields'
6
+ import { ChevronRight, X } from 'lucide-react'
7
+ import { useController, useFormContext } from 'react-hook-form'
8
+
9
+ export default function VideoFormInput({ input, sectionName }: { input: VideoFieldClientConfig; sectionName: string }) {
10
+ const { control } = useFormContext()
11
+ const {
12
+ field,
13
+ fieldState: { invalid, isTouched, isDirty, error },
14
+ } = useController({
15
+ name: input.name,
16
+ control,
17
+ defaultValue: input.value ?? '',
18
+ })
19
+
20
+ const [fileName, setFileName] = useState(getString('no_file_selected'))
21
+ const fileInputContainerRef = useRef<HTMLInputElement>(null)
22
+ const [image, setImage] = useState<string | null>(null)
23
+
24
+ const onImageChange = (event: React.ChangeEvent<HTMLInputElement>) => {
25
+ if (event.target.files && event.target.files[0]) {
26
+ setFileName(event.target.files?.[0].name || getString('no_file_selected'))
27
+ setImage(URL.createObjectURL(event.target.files[0]))
28
+ } else {
29
+ setFileName(getString('no_file_selected'))
30
+ setImage(null)
31
+ }
32
+ }
33
+
34
+ const protectedVideo = (
35
+ <ProtectedVideo
36
+ section={sectionName}
37
+ fieldName={input.name}
38
+ file={input.value}
39
+ width={400}
40
+ height={280}
41
+ className='mb-4 rounded p-1 ring ring-gray-400'
42
+ />
43
+ )
44
+
45
+ return (
46
+ <FormInputElement
47
+ validationError={error}
48
+ value={protectedVideo}
49
+ readonly={input.readonly}
50
+ label={input.label}
51
+ required={input.required}
52
+ >
53
+ <div className='flex flex-row gap-2'>
54
+ {input.value ? protectedVideo : null}
55
+ {image && (
56
+ <div className='relative flex items-center'>
57
+ <ChevronRight fontSize='large' />
58
+ <div className='relative'>
59
+ <X
60
+ className='absolute -right-3 -top-3 cursor-pointer rounded-full border-2 border-gray-500 bg-white p-1'
61
+ onClick={() => {
62
+ setImage(null)
63
+ setFileName(getString('no_file_selected'))
64
+ field.onChange(undefined)
65
+ /**
66
+ * Clear the child input value
67
+ */
68
+ if (fileInputContainerRef.current?.firstChild) {
69
+ ;(fileInputContainerRef.current.firstChild as HTMLInputElement).value = ''
70
+ }
71
+ }}
72
+ />
73
+ <embed
74
+ src={image}
75
+ className='mb-4 max-w-full rounded p-1 ring ring-green-600'
76
+ width={300}
77
+ height={190}
78
+ />
79
+ </div>
80
+ </div>
81
+ )}
82
+ </div>
83
+ <div ref={fileInputContainerRef}>
84
+ <input
85
+ type='file'
86
+ className='hidden'
87
+ name={field.name}
88
+ ref={field.ref}
89
+ onChange={(e) => {
90
+ onImageChange(e)
91
+ if (!input.value) {
92
+ field.onChange(e.target?.files?.length ? e.target.files[0] : undefined)
93
+ }
94
+ }}
95
+ />
96
+ </div>
97
+ <div className='flex flex-col items-center gap-2 md:flex-row'>
98
+ <div className='w-full flex-1 md:flex-[0.5]'>
99
+ <button
100
+ type='button'
101
+ className='w-full rounded border bg-gradient-to-r from-blue-700 to-sky-500 p-2 text-center text-sm font-bold uppercase text-white drop-shadow'
102
+ onClick={() => {
103
+ if (fileInputContainerRef.current?.firstChild) {
104
+ ;(fileInputContainerRef.current.firstChild as HTMLInputElement).click()
105
+ }
106
+ }}
107
+ >
108
+ Upload
109
+ </button>
110
+ </div>
111
+ <div className='w-full flex-1'>
112
+ <span>{fileName}</span>
113
+ </div>
114
+ </div>
115
+ </FormInputElement>
116
+ )
117
+ }
@@ -0,0 +1,36 @@
1
+ import React from 'react'
2
+ import PaginationButtons from './PaginationButtons'
3
+
4
+ export default function Pagination({
5
+ page,
6
+ totalCount,
7
+ limit,
8
+ route,
9
+ lang = 'ar',
10
+ paginationLinksToShow,
11
+ searchParams = null,
12
+ }: {
13
+ page: number
14
+ totalCount: number
15
+ limit: number
16
+ route: string
17
+ lang?: string
18
+ paginationLinksToShow?: number
19
+ searchParams?: string | null
20
+ }) {
21
+ return (
22
+ <>
23
+ <div className='my-3 flex w-full items-center justify-center gap-1 p-3 text-center font-bold'>
24
+ <PaginationButtons
25
+ paginationLinksToShow={paginationLinksToShow}
26
+ page={page}
27
+ totalCount={totalCount}
28
+ limit={limit}
29
+ route={route}
30
+ lang={lang}
31
+ searchParams={searchParams}
32
+ />
33
+ </div>
34
+ </>
35
+ )
36
+ }
@@ -0,0 +1,145 @@
1
+ import React from 'react'
2
+ import Link from 'next/link'
3
+
4
+ export default function PaginationButtons({
5
+ page,
6
+ totalCount,
7
+ limit,
8
+ route,
9
+ lang = 'ar',
10
+ paginationLinksToShow = 5,
11
+ searchParams = null,
12
+ useLangInRoute = false,
13
+ }: {
14
+ page: number
15
+ totalCount: number
16
+ limit: number
17
+ route: string
18
+ lang?: string
19
+ paginationLinksToShow?: number
20
+ searchParams?: string | null
21
+ useLangInRoute?: boolean
22
+ }) {
23
+ // Remove first and trailing slashes from route
24
+ route = route.replace(/^\/|\/$/g, '')
25
+ const paginationLinksCount = Math.ceil(totalCount / limit)
26
+ let paginationLinksArray = []
27
+ switch (page) {
28
+ case 1: // If first page
29
+ for (let i = 1; i <= paginationLinksToShow; i++) {
30
+ if (i > paginationLinksCount) {
31
+ continue
32
+ }
33
+
34
+ paginationLinksArray[i] = {
35
+ route: route + '/' + i,
36
+ html: i,
37
+ }
38
+ }
39
+
40
+ paginationLinksArray[0] = {
41
+ route: route + '/' + 1,
42
+ html: lang === 'ar' ? 'الأولى' : 'First',
43
+ disabled: true,
44
+ }
45
+
46
+ let lastDisabled = true
47
+ if (paginationLinksCount > 1) {
48
+ lastDisabled = false
49
+ }
50
+ paginationLinksArray[paginationLinksCount + 1] = {
51
+ route: route + '/' + paginationLinksCount,
52
+ html: lang === 'ar' ? 'الأخيرة' : 'Last',
53
+ disabled: lastDisabled,
54
+ }
55
+ break
56
+ case paginationLinksCount: // If last page
57
+ let j = paginationLinksCount
58
+ for (j; j > paginationLinksCount - paginationLinksToShow; j--) {
59
+ if (j < 1) {
60
+ continue
61
+ }
62
+ paginationLinksArray[j] = {
63
+ route: route + '/' + j,
64
+ html: j,
65
+ }
66
+ }
67
+
68
+ paginationLinksArray[0] = {
69
+ route: route + '/' + 1,
70
+ html: lang === 'ar' ? 'الأولى' : 'First',
71
+ }
72
+
73
+ paginationLinksArray[paginationLinksCount + 1] = {
74
+ route: route + '/' + paginationLinksCount,
75
+ html: lang === 'ar' ? 'الأخيرة' : 'Last',
76
+ disabled: true,
77
+ }
78
+ break
79
+ default: // If in between
80
+ let start = Math.max(1, page - Math.floor(paginationLinksToShow / 2))
81
+ let end = Math.min(paginationLinksCount, start + paginationLinksToShow - 1)
82
+ start = Math.max(1, end - paginationLinksToShow + 1) // Adjust start if end is less than paginationLinksToShow
83
+
84
+ for (let i = start; i <= end; i++) {
85
+ paginationLinksArray[i] = {
86
+ route: route + '/' + i,
87
+ html: i,
88
+ }
89
+ }
90
+
91
+ paginationLinksArray[0] = {
92
+ route: route + '/' + 1,
93
+ html: lang === 'ar' ? 'الأولى' : 'First',
94
+ }
95
+
96
+ paginationLinksArray[paginationLinksCount + 1] = {
97
+ route: route + '/' + paginationLinksCount,
98
+ html: lang === 'ar' ? 'الأخيرة' : 'Last',
99
+ }
100
+ break
101
+ }
102
+
103
+ if (paginationLinksCount === 0) {
104
+ paginationLinksArray = []
105
+ }
106
+
107
+ return (
108
+ <>
109
+ {paginationLinksArray.map((item, i) => {
110
+ const href = useLangInRoute ? `${lang}/${item.route}` : item.route
111
+ if (i === page) {
112
+ return (
113
+ <div
114
+ className='active rounded border border-neutral-400 bg-blue-950 px-3 py-1 text-amber-50 dark:border-neutral-100'
115
+ key={i.toString()}
116
+ >
117
+ <span className='page-link'>{item.html}</span>
118
+ </div>
119
+ )
120
+ } else {
121
+ if (item.disabled) {
122
+ return (
123
+ <div
124
+ className='active rounded border border-neutral-400 bg-gray-300 px-3 py-1 text-black/40 opacity-70 dark:border-neutral-100 dark:bg-black dark:text-gray-500/60'
125
+ key={i.toString()}
126
+ >
127
+ {item.html}
128
+ </div>
129
+ )
130
+ } else {
131
+ return (
132
+ <Link
133
+ href={searchParams ? `/${href}?${searchParams}` : `/${href}`}
134
+ className='active rounded border border-neutral-400 bg-white px-3 py-1 text-blue-950 hover:bg-emerald-100 dark:border-neutral-100 dark:bg-neutral-800 dark:text-gray-50 dark:hover:bg-neutral-600'
135
+ key={i.toString()}
136
+ >
137
+ {item.html}
138
+ </Link>
139
+ )
140
+ }
141
+ }
142
+ })}
143
+ </>
144
+ )
145
+ }
@@ -0,0 +1,57 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as AccordionPrimitive from "@radix-ui/react-accordion"
5
+ import { ChevronDownIcon } from "@radix-ui/react-icons"
6
+
7
+ import { cn } from "nextjs-cms/utils"
8
+
9
+ const Accordion = AccordionPrimitive.Root
10
+
11
+ const AccordionItem = React.forwardRef<
12
+ React.ElementRef<typeof AccordionPrimitive.Item>,
13
+ React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
14
+ >(({ className, ...props }, ref) => (
15
+ <AccordionPrimitive.Item
16
+ ref={ref}
17
+ className={cn("border-b", className)}
18
+ {...props}
19
+ />
20
+ ))
21
+ AccordionItem.displayName = "AccordionItem"
22
+
23
+ const AccordionTrigger = React.forwardRef<
24
+ React.ElementRef<typeof AccordionPrimitive.Trigger>,
25
+ React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
26
+ >(({ className, children, ...props }, ref) => (
27
+ <AccordionPrimitive.Header className="flex">
28
+ <AccordionPrimitive.Trigger
29
+ ref={ref}
30
+ className={cn(
31
+ "flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
32
+ className
33
+ )}
34
+ {...props}
35
+ >
36
+ {children}
37
+ <ChevronDownIcon className="h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200" />
38
+ </AccordionPrimitive.Trigger>
39
+ </AccordionPrimitive.Header>
40
+ ))
41
+ AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
42
+
43
+ const AccordionContent = React.forwardRef<
44
+ React.ElementRef<typeof AccordionPrimitive.Content>,
45
+ React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
46
+ >(({ className, children, ...props }, ref) => (
47
+ <AccordionPrimitive.Content
48
+ ref={ref}
49
+ className="overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
50
+ {...props}
51
+ >
52
+ <div className={cn("pb-4 pt-0", className)}>{children}</div>
53
+ </AccordionPrimitive.Content>
54
+ ))
55
+ AccordionContent.displayName = AccordionPrimitive.Content.displayName
56
+
57
+ export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
@@ -0,0 +1,46 @@
1
+ import * as React from 'react'
2
+ import { cva, type VariantProps } from 'class-variance-authority'
3
+
4
+ import { cn } from 'nextjs-cms/utils'
5
+
6
+ const alertVariants = cva(
7
+ 'relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7',
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: 'bg-background text-foreground',
12
+ destructive:
13
+ 'border-destructive/90 bg-destructive/10 text-destructive dark:border-destructive [&>svg]:text-destructive',
14
+ light: 'bg-gray-100 text-gray-900 dark:bg-gray-950 dark:text-gray-100',
15
+ info: 'bg-info text-info dark:bg-info dark:text-info',
16
+ },
17
+ },
18
+ defaultVariants: {
19
+ variant: 'default',
20
+ },
21
+ },
22
+ )
23
+
24
+ const Alert = React.forwardRef<
25
+ HTMLDivElement,
26
+ React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
27
+ >(({ className, variant, ...props }, ref) => (
28
+ <div ref={ref} role='alert' className={cn(alertVariants({ variant }), className)} {...props} />
29
+ ))
30
+ Alert.displayName = 'Alert'
31
+
32
+ const AlertTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(
33
+ ({ className, ...props }, ref) => (
34
+ <h5 ref={ref} className={cn('mb-1 font-medium leading-none tracking-tight', className)} {...props} />
35
+ ),
36
+ )
37
+ AlertTitle.displayName = 'AlertTitle'
38
+
39
+ const AlertDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
40
+ ({ className, ...props }, ref) => (
41
+ <div ref={ref} className={cn('text-sm [&_p]:leading-relaxed', className)} {...props} />
42
+ ),
43
+ )
44
+ AlertDescription.displayName = 'AlertDescription'
45
+
46
+ export { Alert, AlertTitle, AlertDescription }
@@ -0,0 +1,33 @@
1
+ import * as React from 'react'
2
+ import { cva, type VariantProps } from 'class-variance-authority'
3
+
4
+ import { cn } from 'nextjs-cms/utils'
5
+
6
+ const badgeVariants = cva(
7
+ 'inline-flex items-center rounded-md border px-1.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2',
8
+ {
9
+ variants: {
10
+ variant: {
11
+ default: 'border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80',
12
+ secondary: 'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80',
13
+ destructive:
14
+ 'border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80',
15
+ outline: 'text-foreground',
16
+ primary: 'border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80',
17
+ success: 'border-transparent bg-success text-success-foreground shadow hover:bg-success/80',
18
+ warning: 'border-transparent bg-warning text-warning-foreground shadow hover:bg-warning/80',
19
+ },
20
+ },
21
+ defaultVariants: {
22
+ variant: 'default',
23
+ },
24
+ },
25
+ )
26
+
27
+ export interface BadgeProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof badgeVariants> {}
28
+
29
+ function Badge({ className, variant, ...props }: BadgeProps) {
30
+ return <div className={cn(badgeVariants({ variant }), className)} {...props} />
31
+ }
32
+
33
+ export { Badge, badgeVariants }
@@ -0,0 +1,57 @@
1
+ import * as React from "react"
2
+ import { Slot } from "@radix-ui/react-slot"
3
+ import { cva, type VariantProps } from "class-variance-authority"
4
+
5
+ import { cn } from 'nextjs-cms/utils'
6
+
7
+ const buttonVariants = cva(
8
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default:
13
+ "bg-primary text-primary-foreground shadow hover:bg-primary/90",
14
+ destructive:
15
+ "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
16
+ outline:
17
+ "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
18
+ secondary:
19
+ "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
20
+ ghost: "hover:bg-accent hover:text-accent-foreground",
21
+ link: "text-primary underline-offset-4 hover:underline",
22
+ },
23
+ size: {
24
+ default: "h-9 px-4 py-2",
25
+ sm: "h-8 rounded-md px-3 text-xs",
26
+ lg: "h-10 rounded-md px-8",
27
+ icon: "h-9 w-9",
28
+ },
29
+ },
30
+ defaultVariants: {
31
+ variant: "default",
32
+ size: "default",
33
+ },
34
+ }
35
+ )
36
+
37
+ export interface ButtonProps
38
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
39
+ VariantProps<typeof buttonVariants> {
40
+ asChild?: boolean
41
+ }
42
+
43
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
44
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
45
+ const Comp = asChild ? Slot : "button"
46
+ return (
47
+ <Comp
48
+ className={cn(buttonVariants({ variant, size, className }))}
49
+ ref={ref}
50
+ {...props}
51
+ />
52
+ )
53
+ }
54
+ )
55
+ Button.displayName = "Button"
56
+
57
+ export { Button, buttonVariants }
@@ -0,0 +1,68 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import { DayPicker } from 'react-day-picker'
5
+
6
+ import { cn } from 'nextjs-cms/utils'
7
+ import { buttonVariants } from '@/components/ui/button'
8
+ import { ChevronLeftIcon, ChevronRightIcon } from '@radix-ui/react-icons'
9
+
10
+ export type CalendarProps = React.ComponentProps<typeof DayPicker>
11
+
12
+ function Calendar({ className, classNames, showOutsideDays = true, ...props }: CalendarProps) {
13
+ return (
14
+ <DayPicker
15
+ showOutsideDays={showOutsideDays}
16
+ className={cn('p-3', className)}
17
+ classNames={{
18
+ months: 'flex flex-col sm:flex-row space-y-4 sm:space-x-4 sm:space-y-0',
19
+ month: 'space-y-4',
20
+ caption: 'flex justify-center pt-1 relative items-center',
21
+ caption_label: 'text-sm font-medium',
22
+ nav: 'space-x-1 flex items-center',
23
+ nav_button: cn(
24
+ buttonVariants({ variant: 'outline' }),
25
+ 'h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100',
26
+ ),
27
+ nav_button_previous: 'absolute left-1',
28
+ nav_button_next: 'absolute right-1',
29
+ table: 'w-full border-collapse space-y-1',
30
+ head_row: 'flex',
31
+ head_cell: 'text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]',
32
+ row: 'flex w-full mt-2',
33
+ cell: cn(
34
+ 'relative p-0 text-center text-sm focus-within:relative focus-within:z-20 [&:has([aria-selected])]:bg-accent [&:has([aria-selected].day-outside)]:bg-accent/50 [&:has([aria-selected].day-range-end)]:rounded-r-md',
35
+ props.mode === 'range'
36
+ ? '[&:has(>.day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md'
37
+ : '[&:has([aria-selected])]:rounded-md',
38
+ ),
39
+ day: cn(buttonVariants({ variant: 'ghost' }), 'h-8 w-8 p-0 font-normal aria-selected:opacity-100'),
40
+ day_range_start: 'day-range-start',
41
+ day_range_end: 'day-range-end',
42
+ day_selected:
43
+ 'bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground',
44
+ day_today: 'bg-accent text-accent-foreground',
45
+ day_outside:
46
+ 'day-outside text-muted-foreground aria-selected:bg-accent/50 aria-selected:text-muted-foreground',
47
+ day_disabled: 'text-muted-foreground opacity-50',
48
+ day_range_middle: 'aria-selected:bg-accent aria-selected:text-accent-foreground',
49
+ day_hidden: 'invisible',
50
+ ...classNames,
51
+ }}
52
+ components={{
53
+ IconLeft: ({ className, ...props }) => (
54
+ // @ts-ignore
55
+ <ChevronLeftIcon className={cn('h-4 w-4', className)} {...props} />
56
+ ),
57
+ IconRight: ({ className, ...props }) => (
58
+ // @ts-ignore
59
+ <ChevronRightIcon className={cn('h-4 w-4', className)} {...props} />
60
+ ),
61
+ }}
62
+ {...props}
63
+ />
64
+ )
65
+ }
66
+ Calendar.displayName = 'Calendar'
67
+
68
+ export { Calendar }
@@ -0,0 +1,76 @@
1
+ import * as React from "react"
2
+
3
+ import { cn } from 'nextjs-cms/utils'
4
+
5
+ const Card = React.forwardRef<
6
+ HTMLDivElement,
7
+ React.HTMLAttributes<HTMLDivElement>
8
+ >(({ className, ...props }, ref) => (
9
+ <div
10
+ ref={ref}
11
+ className={cn(
12
+ "rounded-xl border bg-card text-card-foreground shadow",
13
+ className
14
+ )}
15
+ {...props}
16
+ />
17
+ ))
18
+ Card.displayName = "Card"
19
+
20
+ const CardHeader = React.forwardRef<
21
+ HTMLDivElement,
22
+ React.HTMLAttributes<HTMLDivElement>
23
+ >(({ className, ...props }, ref) => (
24
+ <div
25
+ ref={ref}
26
+ className={cn("flex flex-col space-y-1.5 p-6", className)}
27
+ {...props}
28
+ />
29
+ ))
30
+ CardHeader.displayName = "CardHeader"
31
+
32
+ const CardTitle = React.forwardRef<
33
+ HTMLParagraphElement,
34
+ React.HTMLAttributes<HTMLHeadingElement>
35
+ >(({ className, ...props }, ref) => (
36
+ <h3
37
+ ref={ref}
38
+ className={cn("font-semibold leading-none tracking-tight", className)}
39
+ {...props}
40
+ />
41
+ ))
42
+ CardTitle.displayName = "CardTitle"
43
+
44
+ const CardDescription = React.forwardRef<
45
+ HTMLParagraphElement,
46
+ React.HTMLAttributes<HTMLParagraphElement>
47
+ >(({ className, ...props }, ref) => (
48
+ <p
49
+ ref={ref}
50
+ className={cn("text-sm text-muted-foreground", className)}
51
+ {...props}
52
+ />
53
+ ))
54
+ CardDescription.displayName = "CardDescription"
55
+
56
+ const CardContent = React.forwardRef<
57
+ HTMLDivElement,
58
+ React.HTMLAttributes<HTMLDivElement>
59
+ >(({ className, ...props }, ref) => (
60
+ <div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
61
+ ))
62
+ CardContent.displayName = "CardContent"
63
+
64
+ const CardFooter = React.forwardRef<
65
+ HTMLDivElement,
66
+ React.HTMLAttributes<HTMLDivElement>
67
+ >(({ className, ...props }, ref) => (
68
+ <div
69
+ ref={ref}
70
+ className={cn("flex items-center p-6 pt-0", className)}
71
+ {...props}
72
+ />
73
+ ))
74
+ CardFooter.displayName = "CardFooter"
75
+
76
+ export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
@@ -0,0 +1,29 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import * as CheckboxPrimitive from '@radix-ui/react-checkbox'
5
+ import { CheckIcon, DividerHorizontalIcon } from '@radix-ui/react-icons'
6
+
7
+ import { cn } from 'nextjs-cms/utils'
8
+
9
+ const Checkbox = React.forwardRef<
10
+ React.ElementRef<typeof CheckboxPrimitive.Root>,
11
+ React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
12
+ >(({ className, ...props }, ref) => (
13
+ <CheckboxPrimitive.Root
14
+ ref={ref}
15
+ className={cn(
16
+ 'peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state="indeterminate"]:bg-gray-300 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state="indeterminate"]:bg-gray-500',
17
+ className,
18
+ )}
19
+ {...props}
20
+ >
21
+ <CheckboxPrimitive.Indicator className={cn('flex items-center justify-center text-current')}>
22
+ {props.checked === 'indeterminate' && <DividerHorizontalIcon />}
23
+ {props.checked === true && <CheckIcon />}
24
+ </CheckboxPrimitive.Indicator>
25
+ </CheckboxPrimitive.Root>
26
+ ))
27
+ Checkbox.displayName = CheckboxPrimitive.Root.displayName
28
+
29
+ export { Checkbox }