create-nextjs-cms 0.9.22 → 0.9.24
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.
- package/package.json +3 -3
- package/templates/default/app/(auth)/auth/login/LoginPage.tsx +2 -2
- package/templates/default/app/(auth)/layout.tsx +1 -1
- package/templates/default/app/(rootLayout)/(plugins)/[...slug]/page.tsx +47 -40
- package/templates/default/app/(rootLayout)/(plugins)/[...slug]/plugin-server-registry.ts +16 -16
- package/templates/default/app/(rootLayout)/admins/page.tsx +2 -2
- package/templates/default/app/(rootLayout)/browse/[section]/[page]/page.tsx +2 -2
- package/templates/default/app/(rootLayout)/categorized/[section]/page.tsx +2 -2
- package/templates/default/app/(rootLayout)/dashboard/page.tsx +10 -3
- package/templates/default/app/(rootLayout)/edit/[section]/[itemId]/page.tsx +2 -2
- package/templates/default/app/(rootLayout)/layout.tsx +3 -3
- package/templates/default/app/(rootLayout)/loading.tsx +1 -1
- package/templates/default/app/(rootLayout)/log/page.tsx +1 -1
- package/templates/default/app/(rootLayout)/new/[section]/page.tsx +2 -2
- package/templates/default/app/(rootLayout)/section/[section]/page.tsx +2 -2
- package/templates/default/app/(rootLayout)/settings/page.tsx +2 -2
- package/templates/default/app/_trpc/client.tsx +6 -0
- package/templates/default/app/_trpc/server.ts +9 -0
- package/templates/default/app/_trpc/types.ts +6 -0
- package/templates/default/app/api/document/route.ts +1 -1
- package/templates/default/app/api/photo/route.ts +1 -1
- package/templates/default/app/api/trpc/[trpc]/route.ts +3 -33
- package/templates/default/app/api/video/route.ts +1 -1
- package/templates/default/app/providers.tsx +20 -152
- package/templates/default/cms.config.ts +4 -2
- package/templates/default/components/{AdminCard.tsx → admin/admin-card.tsx} +4 -4
- package/templates/default/components/{AdminEditPage.tsx → admin/admin-edit-page.tsx} +3 -3
- package/templates/default/components/{AdminPrivilegeCard.tsx → admin/admin-privilege-card.tsx} +1 -1
- package/templates/default/components/{NewAdminForm.tsx → admin/new-admin-form.tsx} +4 -4
- package/templates/default/components/{ContainerBox.tsx → container-box.tsx} +1 -1
- package/templates/default/components/{ErrorComponent.tsx → feedback/error-component.tsx} +1 -1
- package/templates/default/{context/ModalProvider.tsx → components/feedback/modal-context.tsx} +56 -53
- package/templates/default/components/{Modal.tsx → feedback/modal.tsx} +1 -1
- package/templates/default/components/form/{FormInputs.tsx → form-inputs.tsx} +17 -17
- package/templates/default/components/form/{Form.tsx → form.tsx} +17 -11
- package/templates/default/components/form/helpers/_section-hot-reload.js +1 -1
- package/templates/default/components/form/inputs/{CheckboxFormInput.tsx → checkbox-form-input.tsx} +1 -1
- package/templates/default/components/form/inputs/{ColorFormInput.tsx → color-form-input.tsx} +1 -1
- package/templates/default/components/form/inputs/{DateFormInput.tsx → date-form-input.tsx} +1 -1
- package/templates/default/components/form/inputs/{DateRangeFormInput.tsx → date-range-form-input.tsx} +1 -1
- package/templates/default/components/form/inputs/{DocumentFormInput.tsx → document-form-input.tsx} +3 -3
- package/templates/default/components/form/inputs/{MapFormInput.tsx → map-form-input.tsx} +5 -4
- package/templates/default/components/form/inputs/{MultipleSelectFormInput.tsx → multiple-select-form-input.tsx} +1 -1
- package/templates/default/components/form/inputs/{NumberFormInput.tsx → number-form-input.tsx} +1 -1
- package/templates/default/components/form/inputs/{PasswordFormInput.tsx → password-form-input.tsx} +1 -1
- package/templates/default/components/form/inputs/{PhotoFormInput.tsx → photo-form-input.tsx} +3 -3
- package/templates/default/components/form/inputs/{RichTextFormInput.tsx → rich-text-form-input.tsx} +3 -3
- package/templates/default/components/form/inputs/{SelectFormInput.tsx → select-form-input.tsx} +4 -4
- package/templates/default/components/form/inputs/{SlugFormInput.tsx → slug-form-input.tsx} +1 -1
- package/templates/default/components/form/inputs/{TagsFormInput.tsx → tags-form-input.tsx} +1 -1
- package/templates/default/components/form/inputs/{TextFormInput.tsx → text-form-input.tsx} +3 -3
- package/templates/default/components/form/inputs/{TextareaFormInput.tsx → textarea-form-input.tsx} +2 -2
- package/templates/default/components/form/inputs/{VideoFormInput.tsx → video-form-input.tsx} +3 -3
- package/templates/default/components/{Layout.tsx → layout/layout.tsx} +4 -4
- package/templates/default/components/{Navbar.tsx → layout/navbar.tsx} +2 -2
- package/templates/default/components/{SidebarDropdownItem.tsx → layout/sidebar-dropdown-item.tsx} +1 -1
- package/templates/default/components/{SidebarItem.tsx → layout/sidebar-item.tsx} +1 -1
- package/templates/default/components/layout/sidebar-plugin-group.tsx +63 -0
- package/templates/default/components/{Sidebar.tsx → layout/sidebar.tsx} +28 -3
- package/templates/default/components/{LocaleSwitcher.tsx → locale/locale-switcher.tsx} +2 -2
- package/templates/default/components/{Dropzone.tsx → media/dropzone.tsx} +1 -1
- package/templates/default/components/{GalleryPhoto.tsx → media/gallery-photo.tsx} +2 -2
- package/templates/default/components/{PhotoGallery.tsx → media/photo-gallery.tsx} +2 -2
- package/templates/default/components/{ProtectedImage.tsx → media/protected-image.tsx} +1 -1
- package/templates/default/components/multi-select.tsx +8 -4
- package/templates/default/components/{AdminsPage.tsx → pages/admins-page.tsx} +4 -4
- package/templates/default/components/{BrowsePage.tsx → pages/browse-page.tsx} +7 -7
- package/templates/default/components/{CategorizedSectionPage.tsx → pages/categorized-section-page.tsx} +2 -2
- package/templates/default/components/{ItemEditPage.tsx → pages/item-edit-page.tsx} +8 -34
- package/templates/default/components/{LogPage.tsx → pages/log-page.tsx} +1 -1
- package/templates/default/components/{NewPage.tsx → pages/new-page.tsx} +28 -51
- package/templates/default/components/{SectionPage.tsx → pages/section-page.tsx} +7 -7
- package/templates/default/components/{SettingsPage.tsx → pages/settings-page.tsx} +4 -4
- package/templates/default/components/pagination/{Pagination.tsx → pagination.tsx} +1 -1
- package/templates/default/components/{CategoryDeleteConfirmPage.tsx → sections/category-delete-confirm-page.tsx} +4 -4
- package/templates/default/components/{CategorySectionSelectInput.tsx → sections/category-section-select-input.tsx} +5 -5
- package/templates/default/components/{ConditionalFields.tsx → sections/conditional-fields.tsx} +1 -1
- package/templates/default/components/{SectionItemCard.tsx → sections/section-item-card.tsx} +4 -4
- package/templates/default/components/{SelectInputButtons.tsx → sections/select-input-buttons.tsx} +4 -4
- package/templates/default/dynamic-schemas/schema.ts +44 -2
- package/templates/default/env/env.ts +42 -0
- package/templates/default/next.config.ts +1 -0
- package/templates/default/package.json +2 -1
- package/templates/default/app/_trpc/client.ts +0 -3
- package/templates/default/components/AnalyticsPage.tsx +0 -144
- package/templates/default/components/BarChartBox.tsx +0 -42
- package/templates/default/components/NewVariantComponent.tsx +0 -229
- package/templates/default/components/PieChartBox.tsx +0 -101
- package/templates/default/components/VariantCard.tsx +0 -124
- package/templates/default/components/VariantEditPage.tsx +0 -230
- package/templates/default/components/analytics/BounceRate.tsx +0 -70
- package/templates/default/components/analytics/LivePageViews.tsx +0 -55
- package/templates/default/components/analytics/LiveUsersCount.tsx +0 -33
- package/templates/default/components/analytics/MonthlyPageViews.tsx +0 -42
- package/templates/default/components/analytics/TopCountries.tsx +0 -52
- package/templates/default/components/analytics/TopDevices.tsx +0 -46
- package/templates/default/components/analytics/TopMediums.tsx +0 -58
- package/templates/default/components/analytics/TopSources.tsx +0 -45
- package/templates/default/components/analytics/TotalPageViews.tsx +0 -41
- package/templates/default/components/analytics/TotalSessions.tsx +0 -41
- package/templates/default/components/analytics/TotalUniqueUsers.tsx +0 -41
- package/templates/default/components/custom/RightHomeRoomVariantCard.tsx +0 -138
- package/templates/default/env/env.js +0 -130
- package/templates/default/hooks/useModal.ts +0 -8
- package/templates/default/lib/apiHelpers.ts +0 -92
- /package/templates/default/components/{dndKit/Draggable.tsx → dnd-kit/draggable.tsx} +0 -0
- /package/templates/default/components/{dndKit/Droppable.tsx → dnd-kit/droppable.tsx} +0 -0
- /package/templates/default/components/{dndKit/SortableItem.tsx → dnd-kit/sortable-item.tsx} +0 -0
- /package/templates/default/components/{InfoCard.tsx → feedback/info-card.tsx} +0 -0
- /package/templates/default/components/{LoadingSpinners.tsx → feedback/loading-spinners.tsx} +0 -0
- /package/templates/default/components/{ProgressBar.tsx → feedback/progress-bar.tsx} +0 -0
- /package/templates/default/components/{TooltipComponent.tsx → feedback/tooltip-component.tsx} +0 -0
- /package/templates/default/components/form/{ContentLocaleContext.tsx → content-locale-context.tsx} +0 -0
- /package/templates/default/components/form/{FormInputElement.tsx → form-input-element.tsx} +0 -0
- /package/templates/default/components/{language-dropdown.tsx → i18n/language-dropdown.tsx} +0 -0
- /package/templates/default/components/{language-picker.tsx → i18n/language-picker.tsx} +0 -0
- /package/templates/default/components/{login-language-dropdown.tsx → i18n/login-language-dropdown.tsx} +0 -0
- /package/templates/default/components/{DefaultNavItems.tsx → layout/default-nav-items.tsx} +0 -0
- /package/templates/default/components/{ThemeProvider.tsx → layout/theme-provider.tsx} +0 -0
- /package/templates/default/components/{theme-toggle.tsx → layout/theme-toggle.tsx} +0 -0
- /package/templates/default/components/{ProtectedDocument.tsx → media/protected-document.tsx} +0 -0
- /package/templates/default/components/{ProtectedVideo.tsx → media/protected-video.tsx} +0 -0
- /package/templates/default/components/{DashboardPageAlt.tsx → pages/dashboard-page-alt.tsx} +0 -0
- /package/templates/default/components/pagination/{PaginationButtons.tsx → pagination-buttons.tsx} +0 -0
- /package/templates/default/components/{SectionIcon.tsx → sections/section-icon.tsx} +0 -0
- /package/templates/default/components/{SectionItemStatusBadge.tsx → sections/section-item-status-badge.tsx} +0 -0
- /package/templates/default/components/{SelectBox.tsx → select-box.tsx} +0 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { createEnv } from '@t3-oss/env-nextjs'
|
|
2
|
+
import * as z from 'zod'
|
|
3
|
+
|
|
4
|
+
export const env = createEnv({
|
|
5
|
+
/**
|
|
6
|
+
* Specify your server-side environment variables schema here. This way you can ensure the app
|
|
7
|
+
* isn't built with invalid env vars.
|
|
8
|
+
*/
|
|
9
|
+
server: {
|
|
10
|
+
ACCESS_TOKEN_SECRET: z.string(),
|
|
11
|
+
REFRESH_TOKEN_SECRET: z.string(),
|
|
12
|
+
CSRF_TOKEN_SECRET: z.string(),
|
|
13
|
+
ACCESS_TOKEN_EXPIRATION: z.string(),
|
|
14
|
+
REFRESH_TOKEN_EXPIRATION: z.string(),
|
|
15
|
+
NODE_ENV: z.enum(['development', 'test', 'production']).default('development'),
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Specify your client-side environment variables schema here. This way you can ensure the app
|
|
20
|
+
* isn't built with invalid env vars. To expose them to the client, prefix them with
|
|
21
|
+
* `NEXT_PUBLIC_`.
|
|
22
|
+
*/
|
|
23
|
+
client: {
|
|
24
|
+
NEXT_PUBLIC_RICHTEXT_INLINE_PUBLIC_URL: z.string().optional(),
|
|
25
|
+
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY: z.string().optional(),
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
experimental__runtimeEnv: {
|
|
29
|
+
NEXT_PUBLIC_RICHTEXT_INLINE_PUBLIC_URL: process.env.NEXT_PUBLIC_RICHTEXT_INLINE_PUBLIC_URL,
|
|
30
|
+
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY: process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY,
|
|
31
|
+
},
|
|
32
|
+
/**
|
|
33
|
+
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially
|
|
34
|
+
* useful for Docker builds.
|
|
35
|
+
*/
|
|
36
|
+
skipValidation: !!process.env.SKIP_ENV_VALIDATION,
|
|
37
|
+
/**
|
|
38
|
+
* Makes it so that empty strings are treated as undefined. `SOME_VAR: z.string()` and
|
|
39
|
+
* `SOME_VAR=''` will throw an error.
|
|
40
|
+
*/
|
|
41
|
+
emptyStringAsUndefined: true,
|
|
42
|
+
})
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
"@radix-ui/react-tabs": "^1.1.3",
|
|
43
43
|
"@radix-ui/react-toast": "^1.2.6",
|
|
44
44
|
"@radix-ui/react-tooltip": "^1.1.8",
|
|
45
|
+
"@t3-oss/env-nextjs": "^0.13.11",
|
|
45
46
|
"@tanstack/react-query": "^5.66.0",
|
|
46
47
|
"@tanstack/react-query-devtools": "^5.66.0",
|
|
47
48
|
"@tinymce/tinymce-react": "^6.3.0",
|
|
@@ -70,7 +71,7 @@
|
|
|
70
71
|
"nanoid": "^5.1.2",
|
|
71
72
|
"next": "16.1.1",
|
|
72
73
|
"next-themes": "^0.4.6",
|
|
73
|
-
"nextjs-cms": "0.9.
|
|
74
|
+
"nextjs-cms": "0.9.23",
|
|
74
75
|
"plaiceholder": "^3.0.0",
|
|
75
76
|
"prettier-plugin-tailwindcss": "^0.7.2",
|
|
76
77
|
"qrcode": "^1.5.4",
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
|
3
|
-
import React, { useEffect, useRef } from 'react'
|
|
4
|
-
import { useI18n } from 'nextjs-cms/translations/client'
|
|
5
|
-
import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js'
|
|
6
|
-
import DateRangeFormInput from '@/components/form/inputs/DateRangeFormInput'
|
|
7
|
-
import { MonthlyPageViews } from '@/components/analytics/MonthlyPageViews'
|
|
8
|
-
import { TopSources } from '@/components/analytics/TopSources'
|
|
9
|
-
import { TopMediums } from '@/components/analytics/TopMediums'
|
|
10
|
-
import { LiveUsersCount } from '@/components/analytics/LiveUsersCount'
|
|
11
|
-
import { TotalPageViews } from '@/components/analytics/TotalPageViews'
|
|
12
|
-
import { TotalSessions } from '@/components/analytics/TotalSessions'
|
|
13
|
-
import { TotalUniqueUsers } from '@/components/analytics/TotalUniqueUsers'
|
|
14
|
-
import { BounceRate } from '@/components/analytics/BounceRate'
|
|
15
|
-
import { LivePageViews } from '@/components/analytics/LivePageViews'
|
|
16
|
-
import { TopCountries } from '@/components/analytics/TopCountries'
|
|
17
|
-
import { TopDevices } from '@/components/analytics/TopDevices'
|
|
18
|
-
// import { trpc } from '@/app/_trpc/client'
|
|
19
|
-
ChartJS.register(ArcElement, Tooltip, Legend)
|
|
20
|
-
|
|
21
|
-
// TODO: MOVE THIS TO THE PLUGIN
|
|
22
|
-
|
|
23
|
-
const AnalyticsPage = () => {
|
|
24
|
-
const t = useI18n()
|
|
25
|
-
const controller = new AbortController()
|
|
26
|
-
const datePickerRangeRef = useRef<HTMLDivElement>(null)
|
|
27
|
-
const [fromDate, setFromDate] = React.useState<string>('30daysAgo')
|
|
28
|
-
const [toDate, setToDate] = React.useState<string>('today')
|
|
29
|
-
|
|
30
|
-
// const { data, isError } = trpc.googleAnalytics.test.useQuery()
|
|
31
|
-
|
|
32
|
-
useEffect(() => {
|
|
33
|
-
return () => {
|
|
34
|
-
controller.abort()
|
|
35
|
-
}
|
|
36
|
-
}, [])
|
|
37
|
-
|
|
38
|
-
return (
|
|
39
|
-
<div className='w-full'>
|
|
40
|
-
<div className='bg-linear-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'>
|
|
41
|
-
<h1 className='text-3xl'>{t('analytics')}</h1>
|
|
42
|
-
</div>
|
|
43
|
-
<div className='p-4'>
|
|
44
|
-
<div className='flex gap-2'>
|
|
45
|
-
<Tabs
|
|
46
|
-
defaultValue='account'
|
|
47
|
-
onValueChange={(value) => {
|
|
48
|
-
if (value === 'custom') {
|
|
49
|
-
// Remove hidden class from date picker range
|
|
50
|
-
datePickerRangeRef.current?.classList.remove('hidden')
|
|
51
|
-
} else {
|
|
52
|
-
// Add hidden class to date picker range
|
|
53
|
-
datePickerRangeRef.current?.classList.add('hidden')
|
|
54
|
-
}
|
|
55
|
-
}}
|
|
56
|
-
>
|
|
57
|
-
<Tabs
|
|
58
|
-
defaultValue='lastMonth'
|
|
59
|
-
onValueChange={(value) => {
|
|
60
|
-
if (value === 'custom') {
|
|
61
|
-
// Remove hidden class from date picker range
|
|
62
|
-
datePickerRangeRef.current?.classList.remove('hidden')
|
|
63
|
-
} else {
|
|
64
|
-
switch (value) {
|
|
65
|
-
case 'today':
|
|
66
|
-
setFromDate('yesterday')
|
|
67
|
-
setToDate('today')
|
|
68
|
-
break
|
|
69
|
-
case 'lastWeek':
|
|
70
|
-
setFromDate('10daysAgo')
|
|
71
|
-
setToDate('today')
|
|
72
|
-
break
|
|
73
|
-
case 'lastMonth':
|
|
74
|
-
setFromDate('30daysAgo')
|
|
75
|
-
setToDate('today')
|
|
76
|
-
break
|
|
77
|
-
case 'lastYear':
|
|
78
|
-
setFromDate('365daysAgo')
|
|
79
|
-
setToDate('today')
|
|
80
|
-
break
|
|
81
|
-
}
|
|
82
|
-
// Add hidden class to date picker range
|
|
83
|
-
datePickerRangeRef.current?.classList.add('hidden')
|
|
84
|
-
}
|
|
85
|
-
}}
|
|
86
|
-
>
|
|
87
|
-
<TabsList>
|
|
88
|
-
<TabsTrigger value='today'>{t('today')}</TabsTrigger>
|
|
89
|
-
<TabsTrigger value='lastWeek'>{t('last7Days')}</TabsTrigger>
|
|
90
|
-
<TabsTrigger value='lastMonth'>{t('last30Days')}</TabsTrigger>
|
|
91
|
-
<TabsTrigger value='lastYear'>{t('last365Days')}</TabsTrigger>
|
|
92
|
-
<TabsTrigger value='custom'>{t('custom')}</TabsTrigger>
|
|
93
|
-
</TabsList>
|
|
94
|
-
</Tabs>
|
|
95
|
-
</Tabs>
|
|
96
|
-
<div ref={datePickerRangeRef} className='hidden'>
|
|
97
|
-
<DateRangeFormInput input={{
|
|
98
|
-
name: 'dateRange',
|
|
99
|
-
label: 'Date Range',
|
|
100
|
-
required: true,
|
|
101
|
-
conditionalFields: [],
|
|
102
|
-
readonly: false,
|
|
103
|
-
defaultValue: undefined,
|
|
104
|
-
value: undefined,
|
|
105
|
-
startName: 'fromDate',
|
|
106
|
-
endName: 'toDate',
|
|
107
|
-
format: 'date',
|
|
108
|
-
startValue: fromDate,
|
|
109
|
-
endValue: toDate,
|
|
110
|
-
type: 'date_range',
|
|
111
|
-
}} />
|
|
112
|
-
</div>
|
|
113
|
-
</div>
|
|
114
|
-
<div className='my-2 grid grid-cols-2 gap-4 sm-sidebar:grid-cols-2 md-sidebar:grid-cols-2 lg-sidebar:grid-cols-4'>
|
|
115
|
-
<LiveUsersCount />
|
|
116
|
-
<TotalPageViews fromDate={fromDate} toDate={toDate} />
|
|
117
|
-
<TotalSessions fromDate={fromDate} toDate={toDate} />
|
|
118
|
-
<TotalUniqueUsers fromDate={fromDate} toDate={toDate} />
|
|
119
|
-
</div>
|
|
120
|
-
<div className='my-4 grid grid-cols-1 gap-4 sm-sidebar:grid-cols-1 md-sidebar:grid-cols-2 lg-sidebar:grid-cols-4'>
|
|
121
|
-
<div className='col-span-2'>
|
|
122
|
-
<MonthlyPageViews fromDate={fromDate} toDate={toDate} />
|
|
123
|
-
</div>
|
|
124
|
-
<div className='col-span-2 grid grid-cols-1 gap-4 sm-sidebar:grid-cols-1 md-sidebar:grid-cols-2'>
|
|
125
|
-
<TopCountries fromDate={fromDate} toDate={toDate} />
|
|
126
|
-
<BounceRate fromDate={fromDate} toDate={toDate} />
|
|
127
|
-
</div>
|
|
128
|
-
</div>
|
|
129
|
-
<div className='my-4 grid grid-cols-1 gap-4 sm-sidebar:grid-cols-1 md-sidebar:grid-cols-2 lg-sidebar:grid-cols-4'>
|
|
130
|
-
<div className='col-span-2'>
|
|
131
|
-
<TopSources fromDate={fromDate} toDate={toDate} />
|
|
132
|
-
</div>
|
|
133
|
-
<TopMediums fromDate={fromDate} toDate={toDate} />
|
|
134
|
-
<TopDevices fromDate={fromDate} toDate={toDate} />
|
|
135
|
-
</div>
|
|
136
|
-
<div className='my-2'>
|
|
137
|
-
<LivePageViews />
|
|
138
|
-
</div>
|
|
139
|
-
</div>
|
|
140
|
-
</div>
|
|
141
|
-
)
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
export default AnalyticsPage
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import ContainerBox from '@/components/ContainerBox'
|
|
2
|
-
import { BarChart, Bar, XAxis, YAxis, ResponsiveContainer } from 'recharts'
|
|
3
|
-
import { BarChartDataItem } from 'nextjs-cms/core/types'
|
|
4
|
-
import LoadingSpinners from '@/components/LoadingSpinners'
|
|
5
|
-
|
|
6
|
-
export default function BarChartBox({
|
|
7
|
-
chartData,
|
|
8
|
-
title,
|
|
9
|
-
fill = '#adfa1d',
|
|
10
|
-
isLoading,
|
|
11
|
-
}: {
|
|
12
|
-
chartData: BarChartDataItem[]
|
|
13
|
-
title: string
|
|
14
|
-
fill?: string
|
|
15
|
-
isLoading?: boolean
|
|
16
|
-
}) {
|
|
17
|
-
return (
|
|
18
|
-
<ContainerBox title={title}>
|
|
19
|
-
<div>
|
|
20
|
-
<div style={{ width: '100%', height: 300 }}>
|
|
21
|
-
{isLoading ? (
|
|
22
|
-
<LoadingSpinners single={true} />
|
|
23
|
-
) : (
|
|
24
|
-
<ResponsiveContainer width='100%' height='100%'>
|
|
25
|
-
<BarChart data={chartData}>
|
|
26
|
-
<XAxis
|
|
27
|
-
dataKey='name'
|
|
28
|
-
stroke='#888888'
|
|
29
|
-
fontSize={12}
|
|
30
|
-
tickLine={false}
|
|
31
|
-
axisLine={false}
|
|
32
|
-
/>
|
|
33
|
-
<YAxis stroke='#888888' fontSize={12} tickLine={false} axisLine={true} />
|
|
34
|
-
<Bar dataKey='value' fill={fill} radius={[4, 4, 0, 0]} />
|
|
35
|
-
</BarChart>
|
|
36
|
-
</ResponsiveContainer>
|
|
37
|
-
)}
|
|
38
|
-
</div>
|
|
39
|
-
</div>
|
|
40
|
-
</ContainerBox>
|
|
41
|
-
)
|
|
42
|
-
}
|
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger } from '@/components/ui/sheet'
|
|
2
|
-
import React, { forwardRef, Ref, useEffect, useImperativeHandle, useState } from 'react'
|
|
3
|
-
import ContainerBox from '@/components/ContainerBox'
|
|
4
|
-
import { useI18n } from 'nextjs-cms/translations/client'
|
|
5
|
-
import { Button } from '@/components/ui/button'
|
|
6
|
-
import { useQuery } from '@tanstack/react-query'
|
|
7
|
-
import { getNewVariantPage } from '@/lib/apiHelpers'
|
|
8
|
-
import { useAxiosPrivate } from 'nextjs-cms/auth/hooks'
|
|
9
|
-
import LoadingSpinners from '@/components/LoadingSpinners'
|
|
10
|
-
import { InputGroup, Variant, VariantItem } from 'nextjs-cms/core/types'
|
|
11
|
-
import FormInputs from '@/components/form/FormInputs'
|
|
12
|
-
import classNames from 'classnames'
|
|
13
|
-
import ProgressBar from '@/components/ProgressBar'
|
|
14
|
-
import { AxiosError } from 'axios'
|
|
15
|
-
import InfoCard from '@/components/InfoCard'
|
|
16
|
-
import { useToast } from '@/components/ui/use-toast'
|
|
17
|
-
import { nanoid } from 'nanoid'
|
|
18
|
-
import { MinusIcon } from '@radix-ui/react-icons'
|
|
19
|
-
|
|
20
|
-
export interface VariantHandles {
|
|
21
|
-
getVariants: () => VariantItem[]
|
|
22
|
-
removeVariants: () => void
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function NewVariantComponent(
|
|
26
|
-
{
|
|
27
|
-
section,
|
|
28
|
-
variantInfo,
|
|
29
|
-
xsrfToken,
|
|
30
|
-
}: {
|
|
31
|
-
section: string
|
|
32
|
-
variantInfo: Variant
|
|
33
|
-
xsrfToken: string
|
|
34
|
-
},
|
|
35
|
-
ref: Ref<VariantHandles>,
|
|
36
|
-
) {
|
|
37
|
-
const t = useI18n()
|
|
38
|
-
const [open, setOpen] = React.useState(false)
|
|
39
|
-
const axiosPrivate = useAxiosPrivate()
|
|
40
|
-
const controller = new AbortController()
|
|
41
|
-
const { isLoading, isError, data, error } = useQuery({
|
|
42
|
-
queryKey: ['newVariant', variantInfo.variant_name],
|
|
43
|
-
queryFn: () => getNewVariantPage(section, variantInfo.variant_name, axiosPrivate, controller),
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
const [variants, setVariants] = useState<VariantItem[]>([])
|
|
47
|
-
const [progress, setProgress] = useState(0)
|
|
48
|
-
const [progressVariant, setProgressVariant] = useState<'determinate' | 'query'>('determinate')
|
|
49
|
-
const [isSubmitting, setIsSubmitting] = useState(false)
|
|
50
|
-
const [response, setResponse] = useState<any>(null)
|
|
51
|
-
const { toast } = useToast()
|
|
52
|
-
|
|
53
|
-
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
|
54
|
-
setIsSubmitting(true)
|
|
55
|
-
e.preventDefault()
|
|
56
|
-
e.stopPropagation()
|
|
57
|
-
setResponse(null)
|
|
58
|
-
const formData = new FormData(e.currentTarget)
|
|
59
|
-
|
|
60
|
-
formData.append('section', section)
|
|
61
|
-
formData.append('variant', variantInfo.variant_name)
|
|
62
|
-
formData.append('preSubmit', 'true')
|
|
63
|
-
formData.append('form_type', 'new')
|
|
64
|
-
|
|
65
|
-
const body = Object.fromEntries(formData.entries())
|
|
66
|
-
try {
|
|
67
|
-
const res = await axiosPrivate.post(`/api-submit`, body, {
|
|
68
|
-
signal: controller.signal,
|
|
69
|
-
onUploadProgress: (progressEvent) => {
|
|
70
|
-
if (!progressEvent.total) return
|
|
71
|
-
const progress = Math.round((progressEvent.loaded / progressEvent.total) * 100)
|
|
72
|
-
// Update progress bar value here
|
|
73
|
-
setProgress(progress)
|
|
74
|
-
|
|
75
|
-
if (progress === 100) {
|
|
76
|
-
setProgressVariant('query')
|
|
77
|
-
}
|
|
78
|
-
},
|
|
79
|
-
headers: {
|
|
80
|
-
'Content-Type': 'multipart/form-data',
|
|
81
|
-
},
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
if (res) {
|
|
85
|
-
setIsSubmitting(false)
|
|
86
|
-
setProgress(0)
|
|
87
|
-
setProgressVariant('determinate')
|
|
88
|
-
if (res.data.code === 200) {
|
|
89
|
-
setOpen(false)
|
|
90
|
-
// Handle closure
|
|
91
|
-
setResponse(null)
|
|
92
|
-
|
|
93
|
-
// Add the new variant to the list
|
|
94
|
-
// But first, let's remove the 'preSubmit' key
|
|
95
|
-
delete body.preSubmit
|
|
96
|
-
setVariants([
|
|
97
|
-
...variants,
|
|
98
|
-
{
|
|
99
|
-
id: nanoid(),
|
|
100
|
-
// @ts-ignore
|
|
101
|
-
title: formData.get(data?.variant.heading_input_name),
|
|
102
|
-
data: body,
|
|
103
|
-
} as VariantItem,
|
|
104
|
-
])
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
} catch (error: AxiosError | any) {
|
|
108
|
-
setIsSubmitting(false)
|
|
109
|
-
setProgress(0)
|
|
110
|
-
setProgressVariant('determinate')
|
|
111
|
-
if (error?.response?.data) {
|
|
112
|
-
setResponse(
|
|
113
|
-
<InfoCard result={{ key: 'danger', title: error.response.data.error.message, status: false }} />,
|
|
114
|
-
)
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
function cancelSubmit() {
|
|
120
|
-
controller.abort()
|
|
121
|
-
setIsSubmitting(false)
|
|
122
|
-
setProgress(0)
|
|
123
|
-
setProgressVariant('determinate')
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
useEffect(() => {
|
|
127
|
-
// console.log('variants', variants)
|
|
128
|
-
return () => {
|
|
129
|
-
cancelSubmit()
|
|
130
|
-
}
|
|
131
|
-
}, [variants])
|
|
132
|
-
|
|
133
|
-
useImperativeHandle(ref, () => ({
|
|
134
|
-
getVariants: () => variants,
|
|
135
|
-
removeVariants: () => {
|
|
136
|
-
setVariants([])
|
|
137
|
-
},
|
|
138
|
-
}))
|
|
139
|
-
|
|
140
|
-
return (
|
|
141
|
-
<ContainerBox title={`${t('add')} ${variantInfo.variant_html_name_en}`}>
|
|
142
|
-
<div className='mb-4 grid grid-cols-1 gap-4 rounded-2xl border-4 border-dashed border-gray-400 bg-accent p-2 md:grid-cols-2 lg:grid-cols-4'>
|
|
143
|
-
{variants.map((v) => (
|
|
144
|
-
<ContainerBox title={v.title} key={v.id}>
|
|
145
|
-
{/* Delete Button */}
|
|
146
|
-
<button
|
|
147
|
-
type='button'
|
|
148
|
-
className='absolute end-1 top-1 rounded-lg bg-red-500 p-1'
|
|
149
|
-
onClick={() => {
|
|
150
|
-
setVariants((prev) =>
|
|
151
|
-
prev.filter((prevVariant) => {
|
|
152
|
-
if (prevVariant.id !== v.id) {
|
|
153
|
-
return prevVariant
|
|
154
|
-
}
|
|
155
|
-
}),
|
|
156
|
-
)
|
|
157
|
-
}}
|
|
158
|
-
>
|
|
159
|
-
<MinusIcon className='text-white' />
|
|
160
|
-
</button>
|
|
161
|
-
<div className='truncate'>{JSON.stringify(v.data)}</div>
|
|
162
|
-
</ContainerBox>
|
|
163
|
-
))}
|
|
164
|
-
</div>
|
|
165
|
-
<Sheet open={open} onOpenChange={setOpen}>
|
|
166
|
-
<SheetTrigger>
|
|
167
|
-
<Button type='button'>{t('add')} +</Button>
|
|
168
|
-
</SheetTrigger>
|
|
169
|
-
<SheetContent className='w-1/2 max-w-[2/3] sm:w-[940px] sm:max-w-full'>
|
|
170
|
-
<SheetHeader>
|
|
171
|
-
<SheetTitle>{t('newVariant')}</SheetTitle>
|
|
172
|
-
<SheetDescription>
|
|
173
|
-
<form onSubmit={handleSubmit}>
|
|
174
|
-
<div className=''>
|
|
175
|
-
{isLoading && (
|
|
176
|
-
<div>
|
|
177
|
-
<LoadingSpinners />
|
|
178
|
-
</div>
|
|
179
|
-
)}
|
|
180
|
-
|
|
181
|
-
{data && data.inputGroups && (
|
|
182
|
-
<div className='flex flex-col gap-4'>
|
|
183
|
-
{data?.inputGroups?.length > 0 &&
|
|
184
|
-
data?.inputGroups?.map((inputGroup: InputGroup, index: number) => {
|
|
185
|
-
return (
|
|
186
|
-
<ContainerBox title={inputGroup.groupTitle} key={index}>
|
|
187
|
-
<FormInputs
|
|
188
|
-
inputs={[]}
|
|
189
|
-
sectionName={variantInfo.variant_name}
|
|
190
|
-
/>
|
|
191
|
-
</ContainerBox>
|
|
192
|
-
)
|
|
193
|
-
})}
|
|
194
|
-
</div>
|
|
195
|
-
)}
|
|
196
|
-
<div className='my-4 flex flex-col gap-3 p-4'>
|
|
197
|
-
<div className='mt-5'>
|
|
198
|
-
<button
|
|
199
|
-
className={classNames({
|
|
200
|
-
'w-full rounded bg-linear-to-r p-2 font-bold text-white drop-shadow-sm':
|
|
201
|
-
true,
|
|
202
|
-
'from-emerald-700 via-green-700 to-green-500 dark:from-blue-800 dark:via-sky-800 dark:to-slate-500':
|
|
203
|
-
!isSubmitting,
|
|
204
|
-
'from-gray-600 via-gray-500 to-gray-400': isSubmitting,
|
|
205
|
-
})}
|
|
206
|
-
type='submit'
|
|
207
|
-
disabled={isSubmitting}
|
|
208
|
-
>
|
|
209
|
-
{isSubmitting ? t('loading') : t('create')}
|
|
210
|
-
</button>
|
|
211
|
-
{isSubmitting && (
|
|
212
|
-
<div className='mt-0.5'>
|
|
213
|
-
<ProgressBar variant={progressVariant} value={progress} />
|
|
214
|
-
</div>
|
|
215
|
-
)}
|
|
216
|
-
</div>
|
|
217
|
-
{response && <div className='w-full'>{response}</div>}
|
|
218
|
-
</div>
|
|
219
|
-
</div>
|
|
220
|
-
</form>
|
|
221
|
-
</SheetDescription>
|
|
222
|
-
</SheetHeader>
|
|
223
|
-
</SheetContent>
|
|
224
|
-
</Sheet>
|
|
225
|
-
</ContainerBox>
|
|
226
|
-
)
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
export default forwardRef(NewVariantComponent)
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import { PieChartDataItem, PieChartTitles } from 'nextjs-cms/core/types'
|
|
3
|
-
import ContainerBox from '@/components/ContainerBox'
|
|
4
|
-
import { Badge } from '@/components/ui/badge'
|
|
5
|
-
import { ResponsiveContainer, PieChart, Pie, Legend } from 'recharts'
|
|
6
|
-
import LoadingSpinners from '@/components/LoadingSpinners'
|
|
7
|
-
|
|
8
|
-
const RADIAN = Math.PI / 180
|
|
9
|
-
const renderCustomizedLabel = ({
|
|
10
|
-
cx,
|
|
11
|
-
cy,
|
|
12
|
-
midAngle,
|
|
13
|
-
innerRadius,
|
|
14
|
-
outerRadius,
|
|
15
|
-
percent,
|
|
16
|
-
index,
|
|
17
|
-
name,
|
|
18
|
-
}: {
|
|
19
|
-
cx: number
|
|
20
|
-
cy: number
|
|
21
|
-
midAngle: number
|
|
22
|
-
innerRadius: number
|
|
23
|
-
outerRadius: number
|
|
24
|
-
percent: number
|
|
25
|
-
index: number
|
|
26
|
-
name: string
|
|
27
|
-
}) => {
|
|
28
|
-
const radius = innerRadius + (outerRadius - innerRadius) * 0.5
|
|
29
|
-
const x = cx + radius * Math.cos(-midAngle * RADIAN)
|
|
30
|
-
const y = cy + radius * Math.sin(-midAngle * RADIAN)
|
|
31
|
-
|
|
32
|
-
return (
|
|
33
|
-
<text
|
|
34
|
-
x={x}
|
|
35
|
-
y={y}
|
|
36
|
-
fill='white'
|
|
37
|
-
className='font-semibold'
|
|
38
|
-
textAnchor={x > cx ? 'start' : 'end'}
|
|
39
|
-
dominantBaseline='central'
|
|
40
|
-
>
|
|
41
|
-
{`${(percent * 100).toFixed(0)}%`}
|
|
42
|
-
</text>
|
|
43
|
-
)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export default function PieChartBox({
|
|
47
|
-
chartData,
|
|
48
|
-
chartBoxTitles,
|
|
49
|
-
height = 300,
|
|
50
|
-
legend = true,
|
|
51
|
-
isLoading,
|
|
52
|
-
}: {
|
|
53
|
-
chartData: PieChartDataItem[]
|
|
54
|
-
chartBoxTitles: PieChartTitles
|
|
55
|
-
height?: number
|
|
56
|
-
legend?: boolean
|
|
57
|
-
isLoading?: boolean
|
|
58
|
-
}) {
|
|
59
|
-
return (
|
|
60
|
-
<ContainerBox title={chartBoxTitles.mainTitle}>
|
|
61
|
-
{isLoading ? (
|
|
62
|
-
<LoadingSpinners single={true} />
|
|
63
|
-
) : (
|
|
64
|
-
<>
|
|
65
|
-
<div className='text-muted-foreground'>
|
|
66
|
-
{chartBoxTitles.totalUnitSubtitle && (
|
|
67
|
-
<div>
|
|
68
|
-
{`${chartBoxTitles.totalUnitSubtitle.key}: `}{' '}
|
|
69
|
-
<Badge variant='secondary'>{chartBoxTitles.totalUnitSubtitle.value}</Badge>
|
|
70
|
-
</div>
|
|
71
|
-
)}
|
|
72
|
-
|
|
73
|
-
{chartBoxTitles.usedUnitSubtitle && (
|
|
74
|
-
<div>
|
|
75
|
-
{`${chartBoxTitles.usedUnitSubtitle.key}: `}
|
|
76
|
-
<Badge variant='secondary'>{chartBoxTitles.usedUnitSubtitle.value}</Badge>
|
|
77
|
-
</div>
|
|
78
|
-
)}
|
|
79
|
-
</div>
|
|
80
|
-
<div>
|
|
81
|
-
<div style={{ width: '100%', height: height }}>
|
|
82
|
-
<ResponsiveContainer>
|
|
83
|
-
<PieChart>
|
|
84
|
-
{legend && <Legend />}
|
|
85
|
-
<Pie
|
|
86
|
-
dataKey='value'
|
|
87
|
-
data={chartData}
|
|
88
|
-
fill='#8884d8'
|
|
89
|
-
label={renderCustomizedLabel}
|
|
90
|
-
labelLine={false}
|
|
91
|
-
startAngle={-180}
|
|
92
|
-
/>
|
|
93
|
-
</PieChart>
|
|
94
|
-
</ResponsiveContainer>
|
|
95
|
-
</div>
|
|
96
|
-
</div>
|
|
97
|
-
</>
|
|
98
|
-
)}
|
|
99
|
-
</ContainerBox>
|
|
100
|
-
)
|
|
101
|
-
}
|