create-nextjs-cms 0.5.91 → 0.5.93

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 (70) hide show
  1. package/package.json +1 -1
  2. package/templates/default/app/(auth)/auth/login/LoginPage.tsx +13 -12
  3. package/templates/default/app/(auth)/layout.tsx +56 -0
  4. package/templates/default/app/(rootLayout)/layout.tsx +58 -8
  5. package/templates/default/app/globals.css +1 -1
  6. package/templates/default/components/AdminCard.tsx +16 -15
  7. package/templates/default/components/AdminEditPage.tsx +8 -7
  8. package/templates/default/components/AdminPrivilegeCard.tsx +12 -11
  9. package/templates/default/components/AdminsPage.tsx +5 -4
  10. package/templates/default/components/AnalyticsPage.tsx +8 -7
  11. package/templates/default/components/BrowsePage.tsx +7 -6
  12. package/templates/default/components/CategoryDeleteConfirmPage.tsx +12 -11
  13. package/templates/default/components/CategorySectionSelectInput.tsx +3 -2
  14. package/templates/default/components/DashboardNewPage.tsx +40 -39
  15. package/templates/default/components/DashboardPage.tsx +34 -33
  16. package/templates/default/components/DashboardPageAlt.tsx +3 -1
  17. package/templates/default/components/Dropzone.tsx +8 -7
  18. package/templates/default/components/EmailCard.tsx +12 -11
  19. package/templates/default/components/EmailPasswordForm.tsx +7 -6
  20. package/templates/default/components/EmailQuotaForm.tsx +7 -6
  21. package/templates/default/components/EmailsPage.tsx +6 -5
  22. package/templates/default/components/GalleryPhoto.tsx +6 -5
  23. package/templates/default/components/ItemEditPage.tsx +5 -4
  24. package/templates/default/components/Layout.tsx +1 -1
  25. package/templates/default/components/LoadingSpinners.tsx +1 -1
  26. package/templates/default/components/LogPage.tsx +24 -9
  27. package/templates/default/components/Navbar.tsx +14 -13
  28. package/templates/default/components/NewAdminForm.tsx +12 -11
  29. package/templates/default/components/NewEmailForm.tsx +11 -10
  30. package/templates/default/components/NewPage.tsx +6 -5
  31. package/templates/default/components/NewVariantComponent.tsx +6 -5
  32. package/templates/default/components/PhotoGallery.tsx +4 -3
  33. package/templates/default/components/SectionItemCard.tsx +8 -8
  34. package/templates/default/components/SectionItemStatusBadge.tsx +3 -2
  35. package/templates/default/components/SectionPage.tsx +6 -5
  36. package/templates/default/components/SelectInputButtons.tsx +8 -7
  37. package/templates/default/components/SettingsPage.tsx +18 -17
  38. package/templates/default/components/Sidebar.tsx +23 -15
  39. package/templates/default/components/SidebarDropdownItem.tsx +11 -5
  40. package/templates/default/components/SidebarItem.tsx +1 -0
  41. package/templates/default/components/VariantCard.tsx +8 -8
  42. package/templates/default/components/VariantEditPage.tsx +5 -4
  43. package/templates/default/components/analytics/BounceRate.tsx +6 -5
  44. package/templates/default/components/analytics/LivePageViews.tsx +8 -7
  45. package/templates/default/components/analytics/LiveUsersCount.tsx +3 -2
  46. package/templates/default/components/analytics/MonthlyPageViews.tsx +3 -2
  47. package/templates/default/components/analytics/TopCountries.tsx +4 -3
  48. package/templates/default/components/analytics/TopDevices.tsx +4 -3
  49. package/templates/default/components/analytics/TopMediums.tsx +4 -3
  50. package/templates/default/components/analytics/TopSources.tsx +4 -3
  51. package/templates/default/components/analytics/TotalPageViews.tsx +3 -2
  52. package/templates/default/components/analytics/TotalSessions.tsx +3 -2
  53. package/templates/default/components/analytics/TotalUniqueUsers.tsx +3 -2
  54. package/templates/default/components/custom/RightHomeRoomVariantCard.tsx +9 -8
  55. package/templates/default/components/form/Form.tsx +5 -4
  56. package/templates/default/components/form/FormInputElement.tsx +3 -1
  57. package/templates/default/components/form/helpers/_section-hot-reload.js +1 -1
  58. package/templates/default/components/form/inputs/DateFormInput.tsx +3 -2
  59. package/templates/default/components/form/inputs/DocumentFormInput.tsx +16 -15
  60. package/templates/default/components/form/inputs/MapFormInput.tsx +5 -4
  61. package/templates/default/components/form/inputs/PhotoFormInput.tsx +16 -15
  62. package/templates/default/components/form/inputs/SelectFormInput.tsx +3 -2
  63. package/templates/default/components/form/inputs/TagsFormInput.tsx +3 -2
  64. package/templates/default/components/form/inputs/VideoFormInput.tsx +7 -6
  65. package/templates/default/components/pagination/PaginationButtons.tsx +8 -6
  66. package/templates/default/components/theme-toggle.tsx +1 -1
  67. package/templates/default/components/ui/spinner.tsx +16 -0
  68. package/templates/default/package.json +2 -2
  69. package/templates/default/app/layout.tsx +0 -40
  70. package/templates/default/components/spinner.tsx +0 -11
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-nextjs-cms",
3
- "version": "0.5.91",
3
+ "version": "0.5.93",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {
@@ -2,7 +2,7 @@
2
2
 
3
3
  import React, { useEffect, useRef, useState } from 'react'
4
4
  import { useRouter, useSearchParams } from 'next/navigation'
5
- import getString from 'nextjs-cms/translations'
5
+ import { useI18n } from 'nextjs-cms/translations/client'
6
6
  import InfoCard from '@/components/InfoCard'
7
7
  import Link from 'next/link'
8
8
  import { Switch } from '@/components/ui/switch'
@@ -20,6 +20,7 @@ type fromErrors = {
20
20
  }
21
21
 
22
22
  export default function LoginPage() {
23
+ const t = useI18n()
23
24
  const session = useSession()
24
25
  const searchParams = useSearchParams()
25
26
  const router = useRouter()
@@ -31,7 +32,7 @@ export default function LoginPage() {
31
32
  event.preventDefault()
32
33
  if (loginButtonRef.current) {
33
34
  loginButtonRef.current.disabled = true
34
- loginButtonRef.current.innerHTML = getString('loading')
35
+ loginButtonRef.current.innerHTML = t('loading')
35
36
  }
36
37
  try {
37
38
  await login({
@@ -41,12 +42,12 @@ export default function LoginPage() {
41
42
  } catch (error: any) {
42
43
  if (loginButtonRef.current) {
43
44
  loginButtonRef.current.disabled = false
44
- loginButtonRef.current.innerHTML = getString('login')
45
+ loginButtonRef.current.innerHTML = t('login')
45
46
  }
46
47
 
47
48
  if (!error.message) {
48
49
  setFormErrors({
49
- general: getString('server_error'),
50
+ general: t('serverError'),
50
51
  })
51
52
  } else {
52
53
  setFormErrors({ general: error.message })
@@ -92,7 +93,7 @@ export default function LoginPage() {
92
93
  nextjs-cms
93
94
  </span>
94
95
  <h2 className='mt-10 text-center text-2xl leading-9 font-bold tracking-tight'>
95
- {getString('login_to_your_account')}
96
+ {t('loginToYourAccount')}
96
97
  </h2>
97
98
  </div>
98
99
 
@@ -100,14 +101,14 @@ export default function LoginPage() {
100
101
  <form ref={formRef} className='space-y-6'>
101
102
  <div>
102
103
  <label htmlFor='username' className='block text-sm leading-6 font-medium'>
103
- {getString('username')}
104
+ {t('username')}
104
105
  </label>
105
106
  <div>
106
107
  <input
107
108
  id='username'
108
109
  name='username'
109
110
  type='text'
110
- placeholder={getString('username')}
111
+ placeholder={t('username')}
111
112
  required
112
113
  className='bg-background text-foreground block w-full rounded-md border-0 p-3 shadow-xs ring-1 ring-gray-300 outline-0 ring-inset placeholder:text-gray-400 focus:ring-2 focus:ring-indigo-600 focus:ring-inset sm:text-sm sm:leading-6'
113
114
  />
@@ -116,14 +117,14 @@ export default function LoginPage() {
116
117
 
117
118
  <div>
118
119
  <label htmlFor='password' className='block text-sm leading-6 font-medium'>
119
- {getString('password')}
120
+ {t('password')}
120
121
  </label>
121
122
  <div>
122
123
  <input
123
124
  id='password'
124
125
  name='password'
125
126
  type='password'
126
- placeholder={getString('password')}
127
+ placeholder={t('password')}
127
128
  required
128
129
  className='bg-background text-foreground block w-full rounded-md border-0 p-3 shadow-xs ring-1 ring-sky-300 outline-0 ring-inset placeholder:text-gray-400 focus:ring-2 focus:ring-indigo-600 focus:ring-inset sm:text-sm sm:leading-6'
129
130
  />
@@ -131,7 +132,7 @@ export default function LoginPage() {
131
132
  <div className='mt-2 text-end'>
132
133
  <div className='text-sm'>
133
134
  <Link href='#' className='font-semibold text-indigo-600 hover:text-indigo-500'>
134
- {getString('forgot_password')}
135
+ {t('forgotPassword')}
135
136
  </Link>
136
137
  </div>
137
138
  </div>
@@ -141,7 +142,7 @@ export default function LoginPage() {
141
142
  <div className='flex gap-2'>
142
143
  <div className='flex items-center space-x-2'>
143
144
  <Switch id='remember_me_check' name='rm' />
144
- <Label htmlFor='remember_me_check'>{getString('remember_me')}</Label>
145
+ <Label htmlFor='remember_me_check'>{t('rememberMe')}</Label>
145
146
  </div>
146
147
  </div>
147
148
  </div>
@@ -155,7 +156,7 @@ export default function LoginPage() {
155
156
  type='submit'
156
157
  className='flex w-full justify-center rounded-md bg-linear-to-r from-green-400 to-blue-500 font-extrabold hover:to-sky-400 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600 focus-visible:outline-solid'
157
158
  >
158
- {getString('login')}
159
+ {t('login')}
159
160
  </Button>
160
161
  </div>
161
162
  </form>
@@ -0,0 +1,56 @@
1
+ import { Inter, Cairo } from 'next/font/google'
2
+ import '../globals.css'
3
+ import type { Metadata } from 'next'
4
+ import { cn } from '@/lib/utils'
5
+ import { ThemeProvider } from '@/components/ThemeProvider'
6
+ import Providers from '@/app/providers'
7
+ import auth from 'nextjs-cms/auth'
8
+ import { getCMSConfig } from 'nextjs-cms/core'
9
+ import { I18nProviderClient } from 'nextjs-cms/translations/client'
10
+
11
+ const inter = Inter({
12
+ subsets: ['latin'],
13
+ variable: '--font-sans',
14
+ })
15
+
16
+ const cairo = Cairo({
17
+ subsets: ['arabic', 'latin'],
18
+ variable: '--font-cairo',
19
+ })
20
+
21
+ export async function generateMetadata(): Promise<Metadata> {
22
+ const cmsConfig = await getCMSConfig()
23
+ return {
24
+ title: cmsConfig.ui.title,
25
+ description: 'nextjs-cms',
26
+ }
27
+ }
28
+
29
+ export default async function RootLayout({ children }: { children: React.ReactNode }) {
30
+ const session = await auth()
31
+ const cmsConfig = await getCMSConfig()
32
+ const locale = session?.user?.locale ?? 'en'
33
+ const isRTL = locale === 'ar'
34
+
35
+ return (
36
+ <html lang={locale} dir={isRTL ? 'rtl' : 'ltr'} suppressHydrationWarning>
37
+ <body
38
+ className={cn(
39
+ 'bg-background min-h-screen font-sans antialiased',
40
+ isRTL ? cairo.variable : inter.variable,
41
+ )}
42
+ >
43
+ <I18nProviderClient locale={locale}>
44
+ <ThemeProvider
45
+ attribute='class'
46
+ defaultTheme={cmsConfig.ui.defaultTheme}
47
+ enableSystem
48
+ disableTransitionOnChange
49
+ >
50
+ <Providers session={session ?? undefined}>{children}</Providers>
51
+ </ThemeProvider>
52
+ </I18nProviderClient>
53
+ </body>
54
+ </html>
55
+ )
56
+ }
@@ -1,16 +1,66 @@
1
- import Layout from '@/components/Layout'
2
- import { api, HydrateClient } from 'nextjs-cms/api/trpc/server'
1
+ import { Inter, Cairo } from 'next/font/google'
2
+ import '../globals.css'
3
+ import type { Metadata } from 'next'
4
+ import { cn } from '@/lib/utils'
5
+ import { ThemeProvider } from '@/components/ThemeProvider'
6
+ import Providers from '@/app/providers'
7
+ import auth from 'nextjs-cms/auth'
3
8
  import { getCMSConfig } from 'nextjs-cms/core'
9
+ import { I18nProviderClient } from 'nextjs-cms/translations/client'
10
+ import { api, HydrateClient } from 'nextjs-cms/api/trpc/server'
11
+ import Layout from '@/components/Layout'
12
+
13
+ const inter = Inter({
14
+ subsets: ['latin'],
15
+ variable: '--font-sans',
16
+ })
17
+
18
+ const cairo = Cairo({
19
+ subsets: ['arabic', 'latin'],
20
+ variable: '--font-cairo',
21
+ })
22
+
23
+ export async function generateMetadata(): Promise<Metadata> {
24
+ const cmsConfig = await getCMSConfig()
25
+ return {
26
+ title: cmsConfig.ui.title,
27
+ description: 'nextjs-cms',
28
+ }
29
+ }
4
30
 
5
31
  export default async function CMSLayout({ children }: { children: React.ReactNode }) {
6
- await api.navigation.getSidebar.prefetch()
32
+ const session = await auth()
7
33
  const cmsConfig = await getCMSConfig()
34
+ const locale = session?.user?.locale ?? 'en'
35
+ const isRTL = locale === 'ar'
36
+ await api.navigation.getSidebar.prefetch()
8
37
 
9
38
  return (
10
- <HydrateClient>
11
- <Layout logoUrlPath={cmsConfig.ui.logo} logoText={cmsConfig.ui.logoText}>
12
- {children}
13
- </Layout>
14
- </HydrateClient>
39
+ <html lang={locale} dir={isRTL ? 'rtl' : 'ltr'} suppressHydrationWarning>
40
+ <body
41
+ className={cn(
42
+ 'bg-background min-h-screen font-sans antialiased',
43
+ isRTL ? cairo.variable : inter.variable,
44
+ )}
45
+ >
46
+ <I18nProviderClient locale={locale}>
47
+ <ThemeProvider
48
+ attribute='class'
49
+ defaultTheme={cmsConfig.ui.defaultTheme}
50
+ enableSystem
51
+ disableTransitionOnChange
52
+ >
53
+ <Providers session={session ?? undefined}>
54
+ <HydrateClient>
55
+ <Layout logoUrlPath={cmsConfig.ui.logo} logoText={cmsConfig.ui.logoText}>
56
+ {children}
57
+ </Layout>
58
+ </HydrateClient>
59
+ </Providers>
60
+ </ThemeProvider>
61
+ </I18nProviderClient>
62
+ </body>
63
+ </html>
64
+
15
65
  )
16
66
  }
@@ -22,7 +22,7 @@
22
22
  --breakpoint-2xl-sidebar: 1920px;
23
23
 
24
24
  --font-sans:
25
- var(--font-sans), ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji',
25
+ var(--font-cairo, var(--font-sans)), ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji',
26
26
  'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
27
27
 
28
28
  --color-border: hsl(var(--border));
@@ -1,4 +1,4 @@
1
- import getString from 'nextjs-cms/translations'
1
+ import { useI18n } from 'nextjs-cms/translations/client'
2
2
  import React from 'react'
3
3
  import useModal from '@/hooks/useModal'
4
4
  import ProtectedImage from '@/components/ProtectedImage'
@@ -19,6 +19,7 @@ export default function AdminCard({
19
19
  admin: RouterOutputs['admins']['list']['admins'][0]
20
20
  action: any
21
21
  }) {
22
+ const t = useI18n()
22
23
  const { setModal, modal, modalResponse, setModalResponse } = useModal()
23
24
  const { toast } = useToast()
24
25
 
@@ -29,14 +30,14 @@ export default function AdminCard({
29
30
  setModalResponse(null)
30
31
  toast({
31
32
  variant: 'success',
32
- title: getString('delete_admin'),
33
- description: getString('adminDeletedSuccessfully'),
33
+ title: t('deleteAdmin'),
34
+ description: t('adminDeletedSuccessfully'),
34
35
  })
35
36
  },
36
37
  onError: (error) => {
37
38
  toast({
38
39
  variant: 'destructive',
39
- title: getString('delete_admin'),
40
+ title: t('deleteAdmin'),
40
41
  description: error.message,
41
42
  })
42
43
  },
@@ -46,8 +47,8 @@ export default function AdminCard({
46
47
  if ([1, '1'].includes(admin.id)) {
47
48
  toast({
48
49
  variant: 'destructive',
49
- title: getString('delete_admin'),
50
- description: getString('masterAdminCannotBeDeleted'),
50
+ title: t('deleteAdmin'),
51
+ description: t('masterAdminCannotBeDeleted'),
51
52
  })
52
53
  return
53
54
  }
@@ -107,9 +108,9 @@ export default function AdminCard({
107
108
  variant='default'
108
109
  onClick={() => {
109
110
  setModal({
110
- title: getString('edit_admin'),
111
+ title: t('editAdmin'),
111
112
  body: [1, '1'].includes(admin.id) ? (
112
- <div className='p-4'>{getString('masterAdminCannotBeModified')}</div>
113
+ <div className='p-4'>{t('masterAdminCannotBeModified')}</div>
113
114
  ) : (
114
115
  <div className='p-4'>{<AdminEditPage id={admin.id} action={() => action()} />}</div>
115
116
  ),
@@ -120,25 +121,25 @@ export default function AdminCard({
120
121
  })
121
122
  }}
122
123
  >
123
- {getString('edit')}
124
+ {t('edit')}
124
125
  </Button>
125
126
  <Button
126
127
  variant='destructive'
127
128
  onClick={() => {
128
129
  setModal({
129
- title: getString('delete_admin'),
130
+ title: t('deleteAdmin'),
130
131
  body: [1, '1'].includes(admin.id) ? (
131
- <div className='p-4'>{getString('masterAdminCannotBeDeleted')}</div>
132
+ <div className='p-4'>{t('masterAdminCannotBeDeleted')}</div>
132
133
  ) : (
133
134
  <div className='p-4'>
134
135
  <div className='flex flex-col gap-4'>
135
- <div>{getString('delete_admin_text')}</div>
136
+ <div>{t('deleteAdminText')}</div>
136
137
  <div className='flex gap-2'>
137
138
  <button
138
139
  className='rounded bg-green-600 px-2 py-1 text-white'
139
140
  onClick={handleDeleteAdmin}
140
141
  >
141
- Yes
142
+ {t('yes')}
142
143
  </button>
143
144
  <button
144
145
  className='rounded bg-red-800 px-2 py-1 text-white'
@@ -146,7 +147,7 @@ export default function AdminCard({
146
147
  setModal(null)
147
148
  }}
148
149
  >
149
- No
150
+ {t('no')}
150
151
  </button>
151
152
  </div>
152
153
  </div>
@@ -158,7 +159,7 @@ export default function AdminCard({
158
159
  })
159
160
  }}
160
161
  >
161
- {getString('delete')}
162
+ {t('delete')}
162
163
  </Button>
163
164
  </CardFooter>
164
165
  </Card>
@@ -1,4 +1,4 @@
1
- import getString from 'nextjs-cms/translations'
1
+ import { useI18n } from 'nextjs-cms/translations/client'
2
2
  import React from 'react'
3
3
  import useModal from '@/hooks/useModal'
4
4
  import { trpc } from '@/app/_trpc/client'
@@ -7,6 +7,7 @@ import LoadingSpinners from './LoadingSpinners'
7
7
  import { Button } from '@/components/ui/button'
8
8
 
9
9
  export default function AdminEditPage({ id, action }: { id: string; action: any }) {
10
+ const t = useI18n()
10
11
  const { setModal } = useModal()
11
12
  const [allChecked, setAllChecked] = React.useState<boolean | 'edited' | null>(null)
12
13
  const utils = trpc.useUtils()
@@ -14,7 +15,7 @@ export default function AdminEditPage({ id, action }: { id: string; action: any
14
15
  const submitMutation = trpc.admins.update.useMutation({
15
16
  onError: (error) => {
16
17
  setModal({
17
- title: getString('edit_admin'),
18
+ title: t('editAdmin'),
18
19
  body: (
19
20
  <div className='rounded border-2 border-dashed border-red-500 bg-red-100 p-2 text-red-800'>
20
21
  {error.message}
@@ -30,7 +31,7 @@ export default function AdminEditPage({ id, action }: { id: string; action: any
30
31
  action()
31
32
  utils.admins.get.invalidate(id)
32
33
  setModal({
33
- title: getString('edit_admin'),
34
+ title: t('editAdmin'),
34
35
  body: (
35
36
  <div className='rounded border-2 border-dashed border-green-500 bg-green-100 p-2 text-green-800'>
36
37
  {data.status}
@@ -68,13 +69,13 @@ export default function AdminEditPage({ id, action }: { id: string; action: any
68
69
  return (
69
70
  <div>
70
71
  <h2>
71
- {getString('edit_admin_text')}: {data?.admin.user}
72
+ {t('editAdminText')}: {data?.admin.user}
72
73
  </h2>
73
74
  <div>
74
75
  {isLoading ? <LoadingSpinners /> : null}
75
76
  {data && data.allRoles && data.allRoles.length > 0 ? (
76
77
  <form onSubmit={handleSubmit}>
77
- <div className='mt-5 w-full font-semibold'>{getString('admin_privileges')}</div>
78
+ <div className='mt-5 w-full font-semibold'>{t('adminPrivileges')}</div>
78
79
  <div className='flex'>
79
80
  <Button
80
81
  className='border border-foreground'
@@ -90,7 +91,7 @@ export default function AdminEditPage({ id, action }: { id: string; action: any
90
91
  }
91
92
  }}
92
93
  >
93
- {allChecked === true ? getString('removeAll') : getString('checkAll')}
94
+ {allChecked === true ? t('removeAll') : t('checkAll')}
94
95
  </Button>
95
96
  </div>
96
97
  <div className='mt-2 grid w-full grid-cols-1 gap-2 md-sidebar:grid-cols-2 lg-sidebar:grid-cols-3 xl-sidebar:grid-cols-4 2xl-sidebar:grid-cols-5'>
@@ -112,7 +113,7 @@ export default function AdminEditPage({ id, action }: { id: string; action: any
112
113
  </div>
113
114
  <div className='mt-4'>
114
115
  <button type='submit' className='rounded bg-blue-700 px-2 py-1 font-bold text-white'>
115
- {getString('save')}
116
+ {t('save')}
116
117
  </button>
117
118
  </div>
118
119
  </form>
@@ -2,7 +2,7 @@ import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
2
2
  import { Checkbox } from '@/components/ui/checkbox'
3
3
  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
4
4
  import React, { useEffect } from 'react'
5
- import getString from 'nextjs-cms/translations'
5
+ import { useI18n } from 'nextjs-cms/translations/client'
6
6
  import { Badge } from '@/components/ui/badge'
7
7
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
8
8
  import type { RouterOutputs } from 'nextjs-cms/api'
@@ -24,6 +24,7 @@ export default function AdminRoleCard({
24
24
  publisher: boolean
25
25
  }
26
26
  }) {
27
+ const t = useI18n()
27
28
  const [checked, setChecked] = React.useState<boolean | 'indeterminate'>(defaultValue.operations === 'cud')
28
29
  const [publisherChecked, setPublisherChecked] = React.useState<boolean>(defaultValue.publisher)
29
30
  const [value, setValue] = React.useState<string>(defaultValue.operations)
@@ -85,7 +86,7 @@ export default function AdminRoleCard({
85
86
  htmlFor={privilege.title + '_new'}
86
87
  className='ms-2 block text-sm font-medium text-foreground'
87
88
  >
88
- Full Access
89
+ {t('fullAccess')}
89
90
  </label>
90
91
  </div>
91
92
 
@@ -107,7 +108,7 @@ export default function AdminRoleCard({
107
108
  htmlFor={`${privilege.title}_publisher`}
108
109
  className={`ms-2 block text-sm font-medium text-foreground`}
109
110
  >
110
- <span className={`${value === 'none' ? 'opacity-40' : ''}`}>Publisher</span>{' '}
111
+ <span className={`${value === 'none' ? 'opacity-40' : ''}`}>{t('publisher')}</span>{' '}
111
112
  <TooltipProvider delayDuration={0}>
112
113
  <Tooltip>
113
114
  <TooltipTrigger asChild>
@@ -116,7 +117,7 @@ export default function AdminRoleCard({
116
117
  </span>
117
118
  </TooltipTrigger>
118
119
  <TooltipContent>
119
- <p>Only publishers can publish/unpublish items</p>
120
+ <p>{t('publisherTooltip')}</p>
120
121
  </TooltipContent>
121
122
  </Tooltip>
122
123
  </TooltipProvider>
@@ -125,7 +126,7 @@ export default function AdminRoleCard({
125
126
  ) : null}
126
127
 
127
128
  <div className='mt-2 flex flex-col border-t p-2'>
128
- <div className='text-sm'>Custom:</div>
129
+ <div className='text-sm'>{t('customAccess')}</div>
129
130
  <Select
130
131
  defaultValue={'none'}
131
132
  value={value}
@@ -148,20 +149,20 @@ export default function AdminRoleCard({
148
149
  }}
149
150
  >
150
151
  <SelectTrigger className='w-[180px]'>
151
- <SelectValue placeholder={getString('select')} />
152
+ <SelectValue placeholder={t('select')} />
152
153
  </SelectTrigger>
153
154
  <SelectContent>
154
155
  <SelectItem value='none'>
155
- <Badge variant='outline'>No Access</Badge>
156
+ <Badge variant='outline'>{t('noAccess')}</Badge>
156
157
  </SelectItem>
157
158
  <SelectItem value='c'>
158
- <Badge variant='secondary'>C</Badge> <span className='italic'>Create</span>
159
+ <Badge variant='secondary'>C</Badge> <span className='italic'>{t('create')}</span>
159
160
  </SelectItem>
160
161
  <SelectItem value='u'>
161
- <Badge variant='secondary'>U</Badge> <span className='italic'>Update</span>
162
+ <Badge variant='secondary'>U</Badge> <span className='italic'>{t('update')}</span>
162
163
  </SelectItem>
163
164
  <SelectItem value='d'>
164
- <Badge variant='secondary'>D</Badge> <span className='italic'>Delete</span>
165
+ <Badge variant='secondary'>D</Badge> <span className='italic'>{t('delete')}</span>
165
166
  </SelectItem>
166
167
  <SelectItem value='cu'>
167
168
  <Badge variant='secondary'>C + U</Badge>
@@ -173,7 +174,7 @@ export default function AdminRoleCard({
173
174
  <Badge variant='secondary'>U + D</Badge>
174
175
  </SelectItem>
175
176
  <SelectItem value='cud'>
176
- <Badge variant='success'>C + U + D</Badge> (All)
177
+ <Badge variant='success'>C + U + D</Badge> {t('allPermissions')}
177
178
  </SelectItem>
178
179
  </SelectContent>
179
180
  </Select>
@@ -1,13 +1,14 @@
1
1
  'use client'
2
2
 
3
3
  import AdminCard from '@/components/AdminCard'
4
- import getString from 'nextjs-cms/translations'
4
+ import { useI18n } from 'nextjs-cms/translations/client'
5
5
  import NewAdminForm from '@/components/NewAdminForm'
6
6
  import ContainerBox from '@/components/ContainerBox'
7
7
  import { trpc } from '@/app/_trpc/client'
8
8
  import ErrorComponent from './ErrorComponent'
9
9
 
10
10
  const AdminsPage = () => {
11
+ const t = useI18n()
11
12
  const [data, {refetch}] = trpc.admins.list.useSuspenseQuery()
12
13
 
13
14
  if (data.error) {
@@ -17,16 +18,16 @@ const AdminsPage = () => {
17
18
  return (
18
19
  <div className='w-full'>
19
20
  <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'>
20
- <h1 className='text-3xl'>{getString('admins')}</h1>
21
+ <h1 className='text-3xl'>{t('admins')}</h1>
21
22
  </div>
22
23
 
23
24
  <div className='flex flex-col gap-2 p-4'>
24
- <ContainerBox title={getString('create_new_admin')}>
25
+ <ContainerBox title={t('createNewAdmin')}>
25
26
  {data && data.privileges && <NewAdminForm action={() => refetch()} privileges={data.privileges} />}
26
27
  </ContainerBox>
27
28
 
28
29
  {data && data.admins && data.admins.length > 0 && (
29
- <ContainerBox title={getString('admins_list')}>
30
+ <ContainerBox title={t('adminsList')}>
30
31
  <div className='mt-2 grid w-full grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3'>
31
32
  {data.admins.map((admin) => (
32
33
  <AdminCard key={admin.id} action={() => refetch()} admin={admin} />
@@ -1,7 +1,7 @@
1
1
  'use client'
2
2
  import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'
3
3
  import React, { useEffect, useRef } from 'react'
4
- import getString from 'nextjs-cms/translations'
4
+ import { useI18n } from 'nextjs-cms/translations/client'
5
5
  import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js'
6
6
  import { DatePickerWithRange } from '@/components/form/DateRangeFormInput'
7
7
  import { MonthlyPageViews } from '@/components/analytics/MonthlyPageViews'
@@ -19,6 +19,7 @@ import { trpc } from '@/app/_trpc/client'
19
19
  ChartJS.register(ArcElement, Tooltip, Legend)
20
20
 
21
21
  const AnalyticsPage = () => {
22
+ const t = useI18n()
22
23
  const controller = new AbortController()
23
24
  const datePickerRangeRef = useRef<HTMLDivElement>(null)
24
25
  const [fromDate, setFromDate] = React.useState<Date | string>('30daysAgo')
@@ -35,7 +36,7 @@ const AnalyticsPage = () => {
35
36
  return (
36
37
  <div className='w-full'>
37
38
  <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'>
38
- <h1 className='text-3xl'>{getString('analytics')}</h1>
39
+ <h1 className='text-3xl'>{t('analytics')}</h1>
39
40
  </div>
40
41
  <div className='p-4'>
41
42
  <div className='flex gap-2'>
@@ -82,11 +83,11 @@ const AnalyticsPage = () => {
82
83
  }}
83
84
  >
84
85
  <TabsList>
85
- <TabsTrigger value='today'>Today</TabsTrigger>
86
- <TabsTrigger value='lastWeek'>Last 7 Days</TabsTrigger>
87
- <TabsTrigger value='lastMonth'>Last 30 Days</TabsTrigger>
88
- <TabsTrigger value='lastYear'>Last 365 Days</TabsTrigger>
89
- <TabsTrigger value='custom'>Custom</TabsTrigger>
86
+ <TabsTrigger value='today'>{t('today')}</TabsTrigger>
87
+ <TabsTrigger value='lastWeek'>{t('last7Days')}</TabsTrigger>
88
+ <TabsTrigger value='lastMonth'>{t('last30Days')}</TabsTrigger>
89
+ <TabsTrigger value='lastYear'>{t('last365Days')}</TabsTrigger>
90
+ <TabsTrigger value='custom'>{t('custom')}</TabsTrigger>
90
91
  </TabsList>
91
92
  </Tabs>
92
93
  </Tabs>
@@ -1,7 +1,7 @@
1
1
  'use client'
2
2
 
3
3
  import { useRef } from 'react'
4
- import getString from 'nextjs-cms/translations'
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>Search {data?.section?.title}</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(getString('search'))}...`}
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(getString('search'))}</Button>
66
+ <Button>{capitalizeWords(t('search'))}</Button>
66
67
  </CardFooter>
67
68
  </form>
68
69
  </Card>
69
70
  ) : null}
70
- <ContainerBox title={getString('browse')}>
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'>{getString('noItems')}</AlertDescription>
87
+ <AlertDescription className='font-bold'>{t('noItems')}</AlertDescription>
87
88
  </Alert>
88
89
  )}
89
90