create-nextjs-cms 0.5.92 → 0.5.94
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 +13 -12
- package/templates/default/app/(auth)/layout.tsx +56 -0
- package/templates/default/app/(rootLayout)/layout.tsx +58 -8
- package/templates/default/app/globals.css +1 -1
- package/templates/default/components/AdminCard.tsx +16 -15
- package/templates/default/components/AdminEditPage.tsx +8 -7
- package/templates/default/components/AdminPrivilegeCard.tsx +12 -11
- package/templates/default/components/AdminsPage.tsx +5 -4
- package/templates/default/components/AnalyticsPage.tsx +8 -7
- package/templates/default/components/BrowsePage.tsx +7 -6
- package/templates/default/components/CategoryDeleteConfirmPage.tsx +12 -11
- package/templates/default/components/CategorySectionSelectInput.tsx +3 -2
- package/templates/default/components/DashboardNewPage.tsx +40 -39
- package/templates/default/components/DashboardPage.tsx +34 -33
- package/templates/default/components/DashboardPageAlt.tsx +3 -1
- package/templates/default/components/Dropzone.tsx +8 -7
- package/templates/default/components/EmailCard.tsx +12 -11
- package/templates/default/components/EmailPasswordForm.tsx +7 -6
- package/templates/default/components/EmailQuotaForm.tsx +7 -6
- package/templates/default/components/EmailsPage.tsx +6 -5
- package/templates/default/components/GalleryPhoto.tsx +6 -5
- package/templates/default/components/ItemEditPage.tsx +5 -4
- package/templates/default/components/Layout.tsx +1 -1
- package/templates/default/components/LoadingSpinners.tsx +1 -1
- package/templates/default/components/LogPage.tsx +24 -9
- package/templates/default/components/Navbar.tsx +13 -12
- package/templates/default/components/NewAdminForm.tsx +12 -11
- package/templates/default/components/NewEmailForm.tsx +11 -10
- package/templates/default/components/NewPage.tsx +6 -5
- package/templates/default/components/NewVariantComponent.tsx +6 -5
- package/templates/default/components/PhotoGallery.tsx +4 -3
- package/templates/default/components/SectionItemCard.tsx +8 -8
- package/templates/default/components/SectionItemStatusBadge.tsx +3 -2
- package/templates/default/components/SectionPage.tsx +6 -5
- package/templates/default/components/SelectInputButtons.tsx +8 -7
- package/templates/default/components/SettingsPage.tsx +18 -17
- package/templates/default/components/Sidebar.tsx +23 -15
- package/templates/default/components/SidebarDropdownItem.tsx +11 -5
- package/templates/default/components/SidebarItem.tsx +1 -0
- package/templates/default/components/VariantCard.tsx +8 -8
- package/templates/default/components/VariantEditPage.tsx +5 -4
- package/templates/default/components/analytics/BounceRate.tsx +6 -5
- package/templates/default/components/analytics/LivePageViews.tsx +8 -7
- package/templates/default/components/analytics/LiveUsersCount.tsx +3 -2
- package/templates/default/components/analytics/MonthlyPageViews.tsx +3 -2
- package/templates/default/components/analytics/TopCountries.tsx +4 -3
- package/templates/default/components/analytics/TopDevices.tsx +4 -3
- package/templates/default/components/analytics/TopMediums.tsx +4 -3
- package/templates/default/components/analytics/TopSources.tsx +4 -3
- package/templates/default/components/analytics/TotalPageViews.tsx +3 -2
- package/templates/default/components/analytics/TotalSessions.tsx +3 -2
- package/templates/default/components/analytics/TotalUniqueUsers.tsx +3 -2
- package/templates/default/components/custom/RightHomeRoomVariantCard.tsx +9 -8
- package/templates/default/components/form/Form.tsx +5 -4
- package/templates/default/components/form/FormInputElement.tsx +3 -1
- package/templates/default/components/form/helpers/_section-hot-reload.js +1 -1
- package/templates/default/components/form/inputs/DateFormInput.tsx +3 -2
- package/templates/default/components/form/inputs/DocumentFormInput.tsx +16 -15
- package/templates/default/components/form/inputs/MapFormInput.tsx +5 -4
- package/templates/default/components/form/inputs/PhotoFormInput.tsx +16 -15
- package/templates/default/components/form/inputs/SelectFormInput.tsx +3 -2
- package/templates/default/components/form/inputs/TagsFormInput.tsx +3 -2
- package/templates/default/components/form/inputs/VideoFormInput.tsx +7 -6
- package/templates/default/components/pagination/PaginationButtons.tsx +8 -6
- package/templates/default/package.json +2 -2
- package/templates/default/app/layout.tsx +0 -40
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import { useRef } from 'react'
|
|
4
|
-
import
|
|
4
|
+
import { useI18n } from 'nextjs-cms/translations/client'
|
|
5
5
|
import SectionItemCard from '@/components/SectionItemCard'
|
|
6
6
|
import ContainerBox from '@/components/ContainerBox'
|
|
7
7
|
import Pagination from '@/components/pagination/Pagination'
|
|
@@ -15,6 +15,7 @@ import { capitalizeWords } from 'nextjs-cms/utils'
|
|
|
15
15
|
import ErrorComponent from '@/components/ErrorComponent'
|
|
16
16
|
|
|
17
17
|
export default function BrowsePage({ section, page }: { section: string; page: string }) {
|
|
18
|
+
const t = useI18n()
|
|
18
19
|
const pageInt = parseInt(page) ?? 1
|
|
19
20
|
const params = useSearchParams()
|
|
20
21
|
const router = useRouter()
|
|
@@ -50,24 +51,24 @@ export default function BrowsePage({ section, page }: { section: string; page: s
|
|
|
50
51
|
}}
|
|
51
52
|
>
|
|
52
53
|
<CardHeader>
|
|
53
|
-
<CardTitle>
|
|
54
|
+
<CardTitle>{t('search')} {data?.section?.title}</CardTitle>
|
|
54
55
|
{/*<CardDescription>Search by title</CardDescription>*/}
|
|
55
56
|
</CardHeader>
|
|
56
57
|
<CardContent>
|
|
57
58
|
<Input
|
|
58
59
|
className='border-accent-foreground bg-input'
|
|
59
60
|
ref={searchInputRef}
|
|
60
|
-
placeholder={`${capitalizeWords(
|
|
61
|
+
placeholder={`${capitalizeWords(t('search'))}...`}
|
|
61
62
|
defaultValue={q || ''}
|
|
62
63
|
/>
|
|
63
64
|
</CardContent>
|
|
64
65
|
<CardFooter className='border-t px-6 py-4'>
|
|
65
|
-
<Button>{capitalizeWords(
|
|
66
|
+
<Button>{capitalizeWords(t('search'))}</Button>
|
|
66
67
|
</CardFooter>
|
|
67
68
|
</form>
|
|
68
69
|
</Card>
|
|
69
70
|
) : null}
|
|
70
|
-
<ContainerBox title={
|
|
71
|
+
<ContainerBox title={t('browse')}>
|
|
71
72
|
|
|
72
73
|
|
|
73
74
|
{ data.items && data.items.length > 0 ? (
|
|
@@ -83,7 +84,7 @@ export default function BrowsePage({ section, page }: { section: string; page: s
|
|
|
83
84
|
</div>
|
|
84
85
|
) : (
|
|
85
86
|
<Alert variant='default' className='bg-primary text-primary-foreground my-8'>
|
|
86
|
-
<AlertDescription className='font-bold'>{
|
|
87
|
+
<AlertDescription className='font-bold'>{t('noItems')}</AlertDescription>
|
|
87
88
|
</Alert>
|
|
88
89
|
)}
|
|
89
90
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import LoadingSpinners from '@/components/LoadingSpinners'
|
|
2
2
|
import InfoCard from '@/components/InfoCard'
|
|
3
3
|
import React from 'react'
|
|
4
|
-
import
|
|
4
|
+
import { useI18n } from 'nextjs-cms/translations/client'
|
|
5
5
|
import { Button } from '@/components/ui/button'
|
|
6
6
|
import useModal from '@/hooks/useModal'
|
|
7
7
|
import { useToast } from '@/components/ui/use-toast'
|
|
@@ -22,6 +22,7 @@ export default function CategoryDeleteConfirmPage({
|
|
|
22
22
|
action: any
|
|
23
23
|
allowRecursiveDelete: boolean
|
|
24
24
|
}) {
|
|
25
|
+
const t = useI18n()
|
|
25
26
|
const { setModal } = useModal()
|
|
26
27
|
const { toast } = useToast()
|
|
27
28
|
|
|
@@ -39,7 +40,7 @@ export default function CategoryDeleteConfirmPage({
|
|
|
39
40
|
const deleteMutation = trpc.hasItemsSections.deleteItem.useMutation({
|
|
40
41
|
onError: (error) => {
|
|
41
42
|
setModal({
|
|
42
|
-
title:
|
|
43
|
+
title: t('delete'),
|
|
43
44
|
body: (
|
|
44
45
|
<div className='rounded border-2 border-dashed border-red-500 bg-red-100 p-2 text-red-800'>
|
|
45
46
|
{error.message}
|
|
@@ -55,8 +56,8 @@ export default function CategoryDeleteConfirmPage({
|
|
|
55
56
|
await action()
|
|
56
57
|
setModal(null)
|
|
57
58
|
toast({
|
|
58
|
-
title:
|
|
59
|
-
description:
|
|
59
|
+
title: t('delete'),
|
|
60
|
+
description: t('itemDeletedSuccessfully'),
|
|
60
61
|
variant: 'success',
|
|
61
62
|
})
|
|
62
63
|
},
|
|
@@ -74,10 +75,10 @@ export default function CategoryDeleteConfirmPage({
|
|
|
74
75
|
<div className='relative z-1 border-b-2 p-8 pt-12 font-extrabold text-foreground'>
|
|
75
76
|
<div className='absolute left-0 top-0 z-2 h-4 w-full bg-linear-to-r from-red-300 via-rose-500 to-pink-400'></div>
|
|
76
77
|
<h1 className='pb-4 text-3xl'>{sectionTitle}</h1>
|
|
77
|
-
<span>/{
|
|
78
|
+
<span>/{t('delete')}</span>
|
|
78
79
|
</div>
|
|
79
80
|
<div className='flex flex-col gap-2 px-8 py-4'>
|
|
80
|
-
<div className='mb-4 text-2xl'>{
|
|
81
|
+
<div className='mb-4 text-2xl'>{t('catDeleteTextLight')}</div>
|
|
81
82
|
{deleteMutation.isPending ? (
|
|
82
83
|
<div className='flex w-full items-center justify-center text-center'>
|
|
83
84
|
<LoadingSpinners />
|
|
@@ -87,7 +88,7 @@ export default function CategoryDeleteConfirmPage({
|
|
|
87
88
|
<InfoCard
|
|
88
89
|
result={{
|
|
89
90
|
key: 'info',
|
|
90
|
-
title: `${
|
|
91
|
+
title: `${t('catDeleteWarningLight')}`,
|
|
91
92
|
status: false,
|
|
92
93
|
}}
|
|
93
94
|
/>
|
|
@@ -97,20 +98,20 @@ export default function CategoryDeleteConfirmPage({
|
|
|
97
98
|
<InfoCard
|
|
98
99
|
result={{
|
|
99
100
|
key: 'info',
|
|
100
|
-
title: `${
|
|
101
|
+
title: `${t('recursiveCategoryDeleteWarning')}`,
|
|
101
102
|
status: false,
|
|
102
103
|
}}
|
|
103
104
|
/>
|
|
104
105
|
<div className='flex items-center space-x-2'>
|
|
105
106
|
<Switch id='recursive-category-delete' />
|
|
106
|
-
<Label htmlFor='airplane-mode'>{
|
|
107
|
+
<Label htmlFor='airplane-mode'>{t('recursiveCategoryDelete')}</Label>
|
|
107
108
|
</div>
|
|
108
109
|
</div>
|
|
109
110
|
) : null}
|
|
110
111
|
|
|
111
112
|
<div className='mt-2 flex flex-wrap gap-2'>
|
|
112
113
|
<Button variant='destructive' onClick={handleDelete}>
|
|
113
|
-
{
|
|
114
|
+
{t('delete')}
|
|
114
115
|
</Button>
|
|
115
116
|
|
|
116
117
|
<Button
|
|
@@ -119,7 +120,7 @@ export default function CategoryDeleteConfirmPage({
|
|
|
119
120
|
setModal(null)
|
|
120
121
|
}}
|
|
121
122
|
>
|
|
122
|
-
{
|
|
123
|
+
{t('cancel')}
|
|
123
124
|
</Button>
|
|
124
125
|
</div>
|
|
125
126
|
</div>
|
|
@@ -4,7 +4,7 @@ import SelectBox from '@/components/SelectBox'
|
|
|
4
4
|
import LoadingSpinners from '@/components/LoadingSpinners'
|
|
5
5
|
import { useAutoAnimate } from '@formkit/auto-animate/react'
|
|
6
6
|
import SelectInputButtons from '@/components/SelectInputButtons'
|
|
7
|
-
import
|
|
7
|
+
import { useI18n } from 'nextjs-cms/translations/client'
|
|
8
8
|
import { trpc } from '@/app/_trpc/client'
|
|
9
9
|
import { SelectOption } from 'nextjs-cms/core/fields'
|
|
10
10
|
import type { RouterOutputs } from 'nextjs-cms/api'
|
|
@@ -21,6 +21,7 @@ export default function CategorySectionSelectInput({
|
|
|
21
21
|
refetch?: any
|
|
22
22
|
isChild?: boolean
|
|
23
23
|
}) {
|
|
24
|
+
const t = useI18n()
|
|
24
25
|
/**
|
|
25
26
|
* Check and add the select option if it does not exist, the select option has a value of undefined
|
|
26
27
|
*/
|
|
@@ -28,7 +29,7 @@ export default function CategorySectionSelectInput({
|
|
|
28
29
|
if (!exists || exists.length === 0) {
|
|
29
30
|
if (input.options) {
|
|
30
31
|
// @ts-ignore - This is a type-hack to add the placeholder `select` option to the options array
|
|
31
|
-
input.options.unshift({ value: undefined, label:
|
|
32
|
+
input.options.unshift({ value: undefined, label: t('select') })
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
35
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import React from 'react'
|
|
4
|
-
import
|
|
4
|
+
import { useI18n } from 'nextjs-cms/translations/client'
|
|
5
5
|
import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js'
|
|
6
6
|
import { LiveUsersCount } from '@/components/analytics/LiveUsersCount'
|
|
7
7
|
import { TotalPageViews } from '@/components/analytics/TotalPageViews'
|
|
@@ -18,6 +18,7 @@ import { PlusCircle, FileText, BarChart3, Users, Settings, Eye } from 'lucide-re
|
|
|
18
18
|
ChartJS.register(ArcElement, Tooltip, Legend)
|
|
19
19
|
|
|
20
20
|
const DashboardNewPage = () => {
|
|
21
|
+
const t = useI18n()
|
|
21
22
|
// Analytics date range - last 30 days
|
|
22
23
|
const [fromDate, setFromDate] = React.useState<Date | string>('30daysAgo')
|
|
23
24
|
const [toDate, setToDate] = React.useState<Date | string>('today')
|
|
@@ -32,8 +33,8 @@ const DashboardNewPage = () => {
|
|
|
32
33
|
return (
|
|
33
34
|
<div className='w-full'>
|
|
34
35
|
<div className='text-foreground bg-linear-to-r from-sky-200 via-emerald-300 to-blue-600 p-8 font-extrabold dark:from-blue-800 dark:via-amber-700 dark:to-rose-900'>
|
|
35
|
-
<h1 className='text-3xl'>{
|
|
36
|
-
<p className='mt-2 text-lg opacity-90'>
|
|
36
|
+
<h1 className='text-3xl'>{t('dashboard')}</h1>
|
|
37
|
+
<p className='mt-2 text-lg opacity-90'>{t('cmsOverview')}</p>
|
|
37
38
|
</div>
|
|
38
39
|
|
|
39
40
|
<div className='flex flex-col gap-6 p-6'>
|
|
@@ -41,7 +42,7 @@ const DashboardNewPage = () => {
|
|
|
41
42
|
<div>
|
|
42
43
|
<h2 className='mb-4 flex items-center gap-2 text-2xl font-semibold'>
|
|
43
44
|
<BarChart3 className='h-6 w-6' />
|
|
44
|
-
|
|
45
|
+
{t('analyticsOverview')}
|
|
45
46
|
</h2>
|
|
46
47
|
<div className='sm-sidebar:grid-cols-2 md-sidebar:grid-cols-2 lg-sidebar:grid-cols-4 grid grid-cols-1 gap-4'>
|
|
47
48
|
<LiveUsersCount />
|
|
@@ -55,50 +56,50 @@ const DashboardNewPage = () => {
|
|
|
55
56
|
<div>
|
|
56
57
|
<h2 className='mb-4 flex items-center gap-2 text-2xl font-semibold'>
|
|
57
58
|
<FileText className='h-6 w-6' />
|
|
58
|
-
|
|
59
|
+
{t('contentStatistics')}
|
|
59
60
|
</h2>
|
|
60
61
|
<div className='sm-sidebar:grid-cols-2 md-sidebar:grid-cols-3 lg-sidebar:grid-cols-4 grid grid-cols-1 gap-4'>
|
|
61
62
|
<Card>
|
|
62
63
|
<CardHeader className='flex flex-row items-center justify-between space-y-0 pb-2'>
|
|
63
|
-
<CardTitle className='text-sm font-medium'>
|
|
64
|
+
<CardTitle className='text-sm font-medium'>{t('totalCars')}</CardTitle>
|
|
64
65
|
<FileText className='text-muted-foreground h-4 w-4' />
|
|
65
66
|
</CardHeader>
|
|
66
67
|
<CardContent>
|
|
67
68
|
<div className='text-2xl font-bold'>1,234</div>
|
|
68
|
-
<p className='text-muted-foreground text-xs'>+20.1%
|
|
69
|
+
<p className='text-muted-foreground text-xs'>+20.1% {t('fromLastMonth')}</p>
|
|
69
70
|
</CardContent>
|
|
70
71
|
</Card>
|
|
71
72
|
|
|
72
73
|
<Card>
|
|
73
74
|
<CardHeader className='flex flex-row items-center justify-between space-y-0 pb-2'>
|
|
74
|
-
<CardTitle className='text-sm font-medium'>
|
|
75
|
+
<CardTitle className='text-sm font-medium'>{t('realEstate')}</CardTitle>
|
|
75
76
|
<FileText className='text-muted-foreground h-4 w-4' />
|
|
76
77
|
</CardHeader>
|
|
77
78
|
<CardContent>
|
|
78
79
|
<div className='text-2xl font-bold'>856</div>
|
|
79
|
-
<p className='text-muted-foreground text-xs'>+15.3%
|
|
80
|
+
<p className='text-muted-foreground text-xs'>+15.3% {t('fromLastMonth')}</p>
|
|
80
81
|
</CardContent>
|
|
81
82
|
</Card>
|
|
82
83
|
|
|
83
84
|
<Card>
|
|
84
85
|
<CardHeader className='flex flex-row items-center justify-between space-y-0 pb-2'>
|
|
85
|
-
<CardTitle className='text-sm font-medium'>
|
|
86
|
+
<CardTitle className='text-sm font-medium'>{t('jobs')}</CardTitle>
|
|
86
87
|
<FileText className='text-muted-foreground h-4 w-4' />
|
|
87
88
|
</CardHeader>
|
|
88
89
|
<CardContent>
|
|
89
90
|
<div className='text-2xl font-bold'>432</div>
|
|
90
|
-
<p className='text-muted-foreground text-xs'>+7.2%
|
|
91
|
+
<p className='text-muted-foreground text-xs'>+7.2% {t('fromLastMonth')}</p>
|
|
91
92
|
</CardContent>
|
|
92
93
|
</Card>
|
|
93
94
|
|
|
94
95
|
<Card>
|
|
95
96
|
<CardHeader className='flex flex-row items-center justify-between space-y-0 pb-2'>
|
|
96
|
-
<CardTitle className='text-sm font-medium'>
|
|
97
|
+
<CardTitle className='text-sm font-medium'>{t('services')}</CardTitle>
|
|
97
98
|
<FileText className='text-muted-foreground h-4 w-4' />
|
|
98
99
|
</CardHeader>
|
|
99
100
|
<CardContent>
|
|
100
101
|
<div className='text-2xl font-bold'>298</div>
|
|
101
|
-
<p className='text-muted-foreground text-xs'>+12.5%
|
|
102
|
+
<p className='text-muted-foreground text-xs'>+12.5% {t('fromLastMonth')}</p>
|
|
102
103
|
</CardContent>
|
|
103
104
|
</Card>
|
|
104
105
|
</div>
|
|
@@ -106,11 +107,11 @@ const DashboardNewPage = () => {
|
|
|
106
107
|
|
|
107
108
|
{/* Recent Activity */}
|
|
108
109
|
<div>
|
|
109
|
-
<h2 className='mb-4 text-2xl font-semibold'>
|
|
110
|
+
<h2 className='mb-4 text-2xl font-semibold'>{t('recentActivity')}</h2>
|
|
110
111
|
<Card>
|
|
111
112
|
<CardHeader>
|
|
112
|
-
<CardTitle>
|
|
113
|
-
<CardDescription>
|
|
113
|
+
<CardTitle>{t('latestContentUpdates')}</CardTitle>
|
|
114
|
+
<CardDescription>{t('recentlyAddedOrModified')}</CardDescription>
|
|
114
115
|
</CardHeader>
|
|
115
116
|
<CardContent>
|
|
116
117
|
<div className='space-y-4'>
|
|
@@ -122,7 +123,7 @@ const DashboardNewPage = () => {
|
|
|
122
123
|
<Button variant='outline' size='sm' asChild>
|
|
123
124
|
<Link href='/browse/cars/1'>
|
|
124
125
|
<Eye className='mr-1 h-4 w-4' />
|
|
125
|
-
|
|
126
|
+
{t('view')}
|
|
126
127
|
</Link>
|
|
127
128
|
</Button>
|
|
128
129
|
</div>
|
|
@@ -135,7 +136,7 @@ const DashboardNewPage = () => {
|
|
|
135
136
|
<Button variant='outline' size='sm' asChild>
|
|
136
137
|
<Link href='/browse/real-estate/1'>
|
|
137
138
|
<Eye className='mr-1 h-4 w-4' />
|
|
138
|
-
|
|
139
|
+
{t('view')}
|
|
139
140
|
</Link>
|
|
140
141
|
</Button>
|
|
141
142
|
</div>
|
|
@@ -148,7 +149,7 @@ const DashboardNewPage = () => {
|
|
|
148
149
|
<Button variant='outline' size='sm' asChild>
|
|
149
150
|
<Link href='/browse/jobs/1'>
|
|
150
151
|
<Eye className='mr-1 h-4 w-4' />
|
|
151
|
-
|
|
152
|
+
{t('view')}
|
|
152
153
|
</Link>
|
|
153
154
|
</Button>
|
|
154
155
|
</div>
|
|
@@ -159,19 +160,19 @@ const DashboardNewPage = () => {
|
|
|
159
160
|
|
|
160
161
|
{/* Quick Actions */}
|
|
161
162
|
<div>
|
|
162
|
-
<h2 className='mb-4 text-2xl font-semibold'>
|
|
163
|
+
<h2 className='mb-4 text-2xl font-semibold'>{t('quickActions')}</h2>
|
|
163
164
|
<div className='sm-sidebar:grid-cols-2 md-sidebar:grid-cols-3 lg-sidebar:grid-cols-4 grid grid-cols-1 gap-4'>
|
|
164
165
|
<Card className='cursor-pointer transition-shadow hover:shadow-md'>
|
|
165
166
|
<CardHeader>
|
|
166
167
|
<CardTitle className='flex items-center gap-2'>
|
|
167
168
|
<PlusCircle className='h-5 w-5' />
|
|
168
|
-
|
|
169
|
+
{t('addNewCar')}
|
|
169
170
|
</CardTitle>
|
|
170
|
-
<CardDescription>
|
|
171
|
+
<CardDescription>{t('createCarListing')}</CardDescription>
|
|
171
172
|
</CardHeader>
|
|
172
173
|
<CardContent>
|
|
173
174
|
<Button asChild className='w-full'>
|
|
174
|
-
<Link href='/new/cars'>
|
|
175
|
+
<Link href='/new/cars'>{t('create')}</Link>
|
|
175
176
|
</Button>
|
|
176
177
|
</CardContent>
|
|
177
178
|
</Card>
|
|
@@ -180,13 +181,13 @@ const DashboardNewPage = () => {
|
|
|
180
181
|
<CardHeader>
|
|
181
182
|
<CardTitle className='flex items-center gap-2'>
|
|
182
183
|
<PlusCircle className='h-5 w-5' />
|
|
183
|
-
|
|
184
|
+
{t('addRealEstate')}
|
|
184
185
|
</CardTitle>
|
|
185
|
-
<CardDescription>
|
|
186
|
+
<CardDescription>{t('createPropertyListing')}</CardDescription>
|
|
186
187
|
</CardHeader>
|
|
187
188
|
<CardContent>
|
|
188
189
|
<Button asChild className='w-full'>
|
|
189
|
-
<Link href='/new/real-estate'>
|
|
190
|
+
<Link href='/new/real-estate'>{t('create')}</Link>
|
|
190
191
|
</Button>
|
|
191
192
|
</CardContent>
|
|
192
193
|
</Card>
|
|
@@ -195,13 +196,13 @@ const DashboardNewPage = () => {
|
|
|
195
196
|
<CardHeader>
|
|
196
197
|
<CardTitle className='flex items-center gap-2'>
|
|
197
198
|
<Users className='h-5 w-5' />
|
|
198
|
-
|
|
199
|
+
{t('manageAdmins')}
|
|
199
200
|
</CardTitle>
|
|
200
|
-
<CardDescription>
|
|
201
|
+
<CardDescription>{t('viewAndManageAdmins')}</CardDescription>
|
|
201
202
|
</CardHeader>
|
|
202
203
|
<CardContent>
|
|
203
204
|
<Button asChild variant='outline' className='w-full'>
|
|
204
|
-
<Link href='/admins'>
|
|
205
|
+
<Link href='/admins'>{t('manageAdmins')}</Link>
|
|
205
206
|
</Button>
|
|
206
207
|
</CardContent>
|
|
207
208
|
</Card>
|
|
@@ -210,13 +211,13 @@ const DashboardNewPage = () => {
|
|
|
210
211
|
<CardHeader>
|
|
211
212
|
<CardTitle className='flex items-center gap-2'>
|
|
212
213
|
<BarChart3 className='h-5 w-5' />
|
|
213
|
-
|
|
214
|
+
{t('viewAnalytics')}
|
|
214
215
|
</CardTitle>
|
|
215
|
-
<CardDescription>
|
|
216
|
+
<CardDescription>{t('detailedAnalytics')}</CardDescription>
|
|
216
217
|
</CardHeader>
|
|
217
218
|
<CardContent>
|
|
218
219
|
<Button asChild variant='outline' className='w-full'>
|
|
219
|
-
<Link href='/analytics'>
|
|
220
|
+
<Link href='/analytics'>{t('viewAnalytics')}</Link>
|
|
220
221
|
</Button>
|
|
221
222
|
</CardContent>
|
|
222
223
|
</Card>
|
|
@@ -225,22 +226,22 @@ const DashboardNewPage = () => {
|
|
|
225
226
|
|
|
226
227
|
{/* System Status */}
|
|
227
228
|
<div>
|
|
228
|
-
<h2 className='mb-4 text-2xl font-semibold'>
|
|
229
|
+
<h2 className='mb-4 text-2xl font-semibold'>{t('systemStatus')}</h2>
|
|
229
230
|
<div className='grid grid-cols-1 gap-4 md:grid-cols-2'>
|
|
230
|
-
<ContainerBox title='
|
|
231
|
+
<ContainerBox title={t('databaseStatus')}>
|
|
231
232
|
<div className='flex items-center gap-2'>
|
|
232
233
|
<div className='h-3 w-3 rounded-full bg-green-500'></div>
|
|
233
|
-
<span>
|
|
234
|
+
<span>{t('connected')}</span>
|
|
234
235
|
</div>
|
|
235
|
-
<p className='text-muted-foreground mt-2 text-sm'>
|
|
236
|
+
<p className='text-muted-foreground mt-2 text-sm'>{t('allConnectionsHealthy')}</p>
|
|
236
237
|
</ContainerBox>
|
|
237
238
|
|
|
238
|
-
<ContainerBox title='
|
|
239
|
+
<ContainerBox title={t('cacheStatus')}>
|
|
239
240
|
<div className='flex items-center gap-2'>
|
|
240
241
|
<div className='h-3 w-3 rounded-full bg-green-500'></div>
|
|
241
|
-
<span>
|
|
242
|
+
<span>{t('operational')}</span>
|
|
242
243
|
</div>
|
|
243
|
-
<p className='text-muted-foreground mt-2 text-sm'>
|
|
244
|
+
<p className='text-muted-foreground mt-2 text-sm'>{t('cacheFunctioning')}</p>
|
|
244
245
|
</ContainerBox>
|
|
245
246
|
</div>
|
|
246
247
|
</div>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import React, { useEffect } from 'react'
|
|
4
|
-
import
|
|
4
|
+
import { useI18n } from 'nextjs-cms/translations/client'
|
|
5
5
|
import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js'
|
|
6
6
|
import { humanReadableFileSize } from 'nextjs-cms/utils'
|
|
7
7
|
import PieChartBox from '@/components/PieChartBox'
|
|
@@ -11,6 +11,7 @@ import { trpc } from '@/app/_trpc/client'
|
|
|
11
11
|
ChartJS.register(ArcElement, Tooltip, Legend)
|
|
12
12
|
|
|
13
13
|
const DashboardPage = () => {
|
|
14
|
+
const t = useI18n()
|
|
14
15
|
const { isLoading, isError, data, error } = trpc.cpanelDashboard.getData.useQuery()
|
|
15
16
|
|
|
16
17
|
useEffect(() => {}, [])
|
|
@@ -18,7 +19,7 @@ const DashboardPage = () => {
|
|
|
18
19
|
return (
|
|
19
20
|
<div className='w-full'>
|
|
20
21
|
<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'>
|
|
21
|
-
<h1 className='text-3xl'>{
|
|
22
|
+
<h1 className='text-3xl'>{t('dashboard')}</h1>
|
|
22
23
|
</div>
|
|
23
24
|
<div className='flex flex-col gap-4 p-4'>
|
|
24
25
|
{isLoading && (
|
|
@@ -32,24 +33,24 @@ const DashboardPage = () => {
|
|
|
32
33
|
<PieChartBox
|
|
33
34
|
chartData={[
|
|
34
35
|
{
|
|
35
|
-
name:
|
|
36
|
+
name: t('totalDiskSpace'),
|
|
36
37
|
value: data.diskSpaceLimit - data.diskSpaceUsage,
|
|
37
38
|
fill: '#00C49F',
|
|
38
39
|
},
|
|
39
40
|
{
|
|
40
|
-
name:
|
|
41
|
+
name: t('usedDiskSpace'),
|
|
41
42
|
value: data.diskSpaceUsage,
|
|
42
43
|
fill: '#E1315B',
|
|
43
44
|
},
|
|
44
45
|
]}
|
|
45
46
|
chartBoxTitles={{
|
|
46
|
-
mainTitle:
|
|
47
|
+
mainTitle: t('diskSpace'),
|
|
47
48
|
totalUnitSubtitle: {
|
|
48
|
-
key:
|
|
49
|
+
key: t('totalDiskSpace'),
|
|
49
50
|
value: humanReadableFileSize(data.diskSpaceLimit),
|
|
50
51
|
},
|
|
51
52
|
usedUnitSubtitle: {
|
|
52
|
-
key:
|
|
53
|
+
key: t('usedDiskSpace'),
|
|
53
54
|
value: humanReadableFileSize(data.diskSpaceUsage),
|
|
54
55
|
},
|
|
55
56
|
}}
|
|
@@ -57,24 +58,24 @@ const DashboardPage = () => {
|
|
|
57
58
|
<PieChartBox
|
|
58
59
|
chartData={[
|
|
59
60
|
{
|
|
60
|
-
name:
|
|
61
|
+
name: t('totalBandwidth'),
|
|
61
62
|
value: data.bandwidthLimit - data.bandwidthUsage,
|
|
62
63
|
fill: '#1c89f1',
|
|
63
64
|
},
|
|
64
65
|
{
|
|
65
|
-
name:
|
|
66
|
+
name: t('usedBandwidth'),
|
|
66
67
|
value: data.bandwidthUsage,
|
|
67
68
|
fill: '#ff6688',
|
|
68
69
|
},
|
|
69
70
|
]}
|
|
70
71
|
chartBoxTitles={{
|
|
71
|
-
mainTitle:
|
|
72
|
+
mainTitle: t('thisMothBandwidth'),
|
|
72
73
|
totalUnitSubtitle: {
|
|
73
|
-
key:
|
|
74
|
+
key: t('totalBandwidth'),
|
|
74
75
|
value: humanReadableFileSize(data.bandwidthLimit),
|
|
75
76
|
},
|
|
76
77
|
usedUnitSubtitle: {
|
|
77
|
-
key:
|
|
78
|
+
key: t('usedBandwidth'),
|
|
78
79
|
value: humanReadableFileSize(data.bandwidthUsage),
|
|
79
80
|
},
|
|
80
81
|
}}
|
|
@@ -82,24 +83,24 @@ const DashboardPage = () => {
|
|
|
82
83
|
<PieChartBox
|
|
83
84
|
chartData={[
|
|
84
85
|
{
|
|
85
|
-
name:
|
|
86
|
+
name: t('totalEmails'),
|
|
86
87
|
value: data.emailsLimit - data.emailsUsage,
|
|
87
88
|
fill: '#c4ba56',
|
|
88
89
|
},
|
|
89
90
|
{
|
|
90
|
-
name:
|
|
91
|
+
name: t('usedEmails'),
|
|
91
92
|
value: data.emailsUsage,
|
|
92
93
|
fill: '#1404c4',
|
|
93
94
|
},
|
|
94
95
|
]}
|
|
95
96
|
chartBoxTitles={{
|
|
96
|
-
mainTitle:
|
|
97
|
+
mainTitle: t('emailAccounts'),
|
|
97
98
|
totalUnitSubtitle: {
|
|
98
|
-
key:
|
|
99
|
+
key: t('totalEmails'),
|
|
99
100
|
value: data.emailsLimit.toString(),
|
|
100
101
|
},
|
|
101
102
|
usedUnitSubtitle: {
|
|
102
|
-
key:
|
|
103
|
+
key: t('usedEmails'),
|
|
103
104
|
value: data.emailsUsage.toString(),
|
|
104
105
|
},
|
|
105
106
|
}}
|
|
@@ -107,24 +108,24 @@ const DashboardPage = () => {
|
|
|
107
108
|
<PieChartBox
|
|
108
109
|
chartData={[
|
|
109
110
|
{
|
|
110
|
-
name:
|
|
111
|
+
name: t('totalDatabases'),
|
|
111
112
|
value: data.dbsLimit - data.dbsCount,
|
|
112
113
|
fill: '#56c4b4',
|
|
113
114
|
},
|
|
114
115
|
{
|
|
115
|
-
name:
|
|
116
|
+
name: t('usedDatabases'),
|
|
116
117
|
value: data.dbsCount,
|
|
117
118
|
fill: '#625bb2',
|
|
118
119
|
},
|
|
119
120
|
]}
|
|
120
121
|
chartBoxTitles={{
|
|
121
|
-
mainTitle: '
|
|
122
|
+
mainTitle: t('mysqlDatabases'),
|
|
122
123
|
totalUnitSubtitle: {
|
|
123
|
-
key:
|
|
124
|
+
key: t('totalDatabases'),
|
|
124
125
|
value: data.dbsLimit.toString(),
|
|
125
126
|
},
|
|
126
127
|
usedUnitSubtitle: {
|
|
127
|
-
key:
|
|
128
|
+
key: t('usedDatabases'),
|
|
128
129
|
value: data.dbsCount.toString(),
|
|
129
130
|
},
|
|
130
131
|
}}
|
|
@@ -133,35 +134,35 @@ const DashboardPage = () => {
|
|
|
133
134
|
) : null}
|
|
134
135
|
</div>
|
|
135
136
|
<div className='grid grid-cols-1 gap-4 md:grid-cols-2'>
|
|
136
|
-
<ContainerBox title={
|
|
137
|
-
|
|
138
|
-
|
|
137
|
+
<ContainerBox title={t('accountInformation')}>
|
|
138
|
+
<div>{t('phpVersion')}: {data?.phpVersion}</div>
|
|
139
|
+
<div>{t('documentRoot')}: {data?.documentRoot}</div>
|
|
139
140
|
</ContainerBox>
|
|
140
141
|
|
|
141
|
-
<ContainerBox title='
|
|
142
|
+
<ContainerBox title={t('mysqlDatabases')}>
|
|
142
143
|
<div>
|
|
143
144
|
{data?.dbInfo ? (
|
|
144
145
|
<>
|
|
145
|
-
|
|
146
|
-
|
|
146
|
+
<div>{t('version')}: {data.dbInfo.version}</div>
|
|
147
|
+
<div>{t('remote')}: {data.dbInfo.is_remote}</div>
|
|
147
148
|
</>
|
|
148
149
|
) : (
|
|
149
|
-
<div>
|
|
150
|
+
<div>{t('mysqlNotInstalled')}</div>
|
|
150
151
|
)}
|
|
151
152
|
</div>
|
|
152
153
|
<div>
|
|
153
154
|
{data?.dbsList?.map((db: any, index: number) => (
|
|
154
155
|
<div key={`${db.name}_${db.database}_${index}`}>
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
156
|
+
<div>{t('database')}: {db.database}</div>
|
|
157
|
+
<div>{t('diskUsage')}: {humanReadableFileSize(db.disk_usage)}</div>
|
|
158
|
+
<div>{t('users')}: {db.users?.length}</div>
|
|
158
159
|
</div>
|
|
159
160
|
))}
|
|
160
161
|
</div>
|
|
161
162
|
</ContainerBox>
|
|
162
163
|
</div>
|
|
163
164
|
{/*<div>
|
|
164
|
-
<ContainerBox title={
|
|
165
|
+
<ContainerBox title={t('passengerApplications')}>
|
|
165
166
|
<div className='flex gap-4'>
|
|
166
167
|
{data?.passengerAppList?.map((app: any) => (
|
|
167
168
|
<ContainerBox title={app.name} key={app.name}>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Metadata } from 'next'
|
|
2
2
|
import Image from 'next/image'
|
|
3
|
+
import { useI18n } from 'nextjs-cms/translations/client'
|
|
3
4
|
|
|
4
5
|
export const metadata: Metadata = {
|
|
5
6
|
title: 'Dashboard',
|
|
@@ -7,6 +8,7 @@ export const metadata: Metadata = {
|
|
|
7
8
|
}
|
|
8
9
|
|
|
9
10
|
export default function DashboardPage() {
|
|
11
|
+
const t = useI18n()
|
|
10
12
|
return (
|
|
11
13
|
<>
|
|
12
14
|
<div className='md:hidden'>
|
|
@@ -33,7 +35,7 @@ export default function DashboardPage() {
|
|
|
33
35
|
</div>
|
|
34
36
|
<div className='flex-1 space-y-4 p-8 pt-6'>
|
|
35
37
|
<div className='flex items-center justify-between space-y-2'>
|
|
36
|
-
<h2 className='text-3xl font-bold tracking-tight'>
|
|
38
|
+
<h2 className='text-3xl font-bold tracking-tight'>{t('dashboard')}</h2>
|
|
37
39
|
<div className='flex items-center space-x-2'></div>
|
|
38
40
|
</div>
|
|
39
41
|
</div>
|