create-nextjs-cms 0.7.8 → 0.7.10
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 +1 -1
- package/templates/default/components/DashboardPageAlt.tsx +2 -2
- package/templates/default/components/LogPage.tsx +1 -1
- package/templates/default/components/Navbar.tsx +4 -4
- package/templates/default/components/SelectBox.tsx +4 -4
- package/templates/default/components/Sidebar.tsx +2 -2
- package/templates/default/components/form/inputs/MultipleSelectFormInput.tsx +3 -1
- package/templates/default/components/form/inputs/PhotoFormInput.tsx +1 -1
- package/templates/default/components/form/inputs/SlugFormInput.tsx +3 -1
- package/templates/default/components/multi-select.tsx +9 -7
- package/templates/default/components/theme-toggle.tsx +4 -2
- package/templates/default/package.json +1 -1
package/package.json
CHANGED
|
@@ -16,14 +16,14 @@ export default function DashboardPage() {
|
|
|
16
16
|
src='/examples/dashboard-light.png'
|
|
17
17
|
width={1280}
|
|
18
18
|
height={866}
|
|
19
|
-
alt='
|
|
19
|
+
alt={t('dashboard') as string}
|
|
20
20
|
className='block dark:hidden'
|
|
21
21
|
/>
|
|
22
22
|
<Image
|
|
23
23
|
src='/examples/dashboard-dark.png'
|
|
24
24
|
width={1280}
|
|
25
25
|
height={866}
|
|
26
|
-
alt='
|
|
26
|
+
alt={t('dashboard') as string}
|
|
27
27
|
className='hidden dark:block'
|
|
28
28
|
/>
|
|
29
29
|
</div>
|
|
@@ -96,7 +96,7 @@ export default function Navbar(props: Props) {
|
|
|
96
96
|
className='border-foreground text-foreground hover:text-foreground/90 relative inline-flex items-center justify-center rounded-md border p-2 focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800 focus:outline-hidden'
|
|
97
97
|
>
|
|
98
98
|
<span className='absolute -inset-0.5' />
|
|
99
|
-
<span className='sr-only'>
|
|
99
|
+
<span className='sr-only'>{t('openMainMenu')}</span>
|
|
100
100
|
<HamburgerMenuIcon className='block h-6 w-6' aria-hidden='true' />
|
|
101
101
|
</button>
|
|
102
102
|
</div>
|
|
@@ -123,7 +123,7 @@ export default function Navbar(props: Props) {
|
|
|
123
123
|
<button
|
|
124
124
|
type='button'
|
|
125
125
|
className='relative flex h-10 items-center justify-center rounded-full focus:outline-hidden'
|
|
126
|
-
aria-label='
|
|
126
|
+
aria-label={t('notifications') as string}
|
|
127
127
|
>
|
|
128
128
|
<span className='absolute -inset-1.5' />
|
|
129
129
|
<BellIcon className='h-6 w-6' />
|
|
@@ -198,7 +198,7 @@ export default function Navbar(props: Props) {
|
|
|
198
198
|
className='relative flex h-10 items-center justify-center rounded-full'
|
|
199
199
|
>
|
|
200
200
|
<span className='absolute -inset-1.5' />
|
|
201
|
-
<span className='sr-only'>
|
|
201
|
+
<span className='sr-only'>{t('openUserMenu')}</span>
|
|
202
202
|
{session?.data?.user.image ? (
|
|
203
203
|
<ProtectedImage
|
|
204
204
|
section={'admins'}
|
|
@@ -215,7 +215,7 @@ export default function Navbar(props: Props) {
|
|
|
215
215
|
src='/blank_avatar.png'
|
|
216
216
|
height={40}
|
|
217
217
|
width={40}
|
|
218
|
-
alt='
|
|
218
|
+
alt={t('profileImage') as string}
|
|
219
219
|
className='rounded-full ring-2 ring-amber-300 ring-inset'
|
|
220
220
|
/>
|
|
221
221
|
)}
|
|
@@ -7,6 +7,7 @@ import { Button } from '@/components/ui/button'
|
|
|
7
7
|
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from '@/components/ui/command'
|
|
8
8
|
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
|
|
9
9
|
import { SelectOption } from 'nextjs-cms/core/fields'
|
|
10
|
+
import { useI18n } from 'nextjs-cms/translations/client'
|
|
10
11
|
|
|
11
12
|
export default function SelectBox({
|
|
12
13
|
items,
|
|
@@ -19,8 +20,7 @@ export default function SelectBox({
|
|
|
19
20
|
onChange: any
|
|
20
21
|
classname: string
|
|
21
22
|
}) {
|
|
22
|
-
|
|
23
|
-
// check for defaultValue and set it if it exists,
|
|
23
|
+
const t = useI18n()
|
|
24
24
|
const [selected, setSelected] = useState<SelectOption | undefined>(
|
|
25
25
|
defaultValue !== undefined && defaultValue !== null
|
|
26
26
|
? items.find((item) => item.value?.toString() === defaultValue.toString())
|
|
@@ -61,9 +61,9 @@ export default function SelectBox({
|
|
|
61
61
|
align='start'
|
|
62
62
|
>
|
|
63
63
|
<Command>
|
|
64
|
-
<CommandInput placeholder='
|
|
64
|
+
<CommandInput placeholder={t('searchDots') as string} className='h-9' />
|
|
65
65
|
<CommandList>
|
|
66
|
-
<CommandEmpty>
|
|
66
|
+
<CommandEmpty>{t('noItemFound')}</CommandEmpty>
|
|
67
67
|
<CommandGroup>
|
|
68
68
|
{items.map((item: SelectOption, index: number) => {
|
|
69
69
|
// Use label for searchable value, but include value in a way that we can identify it
|
|
@@ -60,7 +60,7 @@ const Sidebar = (props: SidebarProps & { logoUrlPath: string; logoText: string;
|
|
|
60
60
|
'justify-start gap-2 p-4': true,
|
|
61
61
|
})}
|
|
62
62
|
>
|
|
63
|
-
<Image src={props.logoUrlPath} alt='logo' width={32} height={32} />
|
|
63
|
+
<Image src={props.logoUrlPath} alt={t('logo') as string} width={32} height={32} />
|
|
64
64
|
<span className='bg-transparent px-2 font-normal tracking-[0.1em] text-white/90 transition-all duration-150'>
|
|
65
65
|
{props.logoText}
|
|
66
66
|
</span>
|
|
@@ -181,7 +181,7 @@ const Sidebar = (props: SidebarProps & { logoUrlPath: string; logoText: string;
|
|
|
181
181
|
src='/blank_avatar.png'
|
|
182
182
|
height={36}
|
|
183
183
|
width={36}
|
|
184
|
-
alt='
|
|
184
|
+
alt={t('profileImage') as string}
|
|
185
185
|
className='rounded-full ring-2 ring-amber-300 ring-inset'
|
|
186
186
|
/>
|
|
187
187
|
)}
|
|
@@ -3,8 +3,10 @@ import { useEffect, useState } from 'react'
|
|
|
3
3
|
import { SelectMultipleFieldClientConfig, SelectOption } from 'nextjs-cms/core/fields'
|
|
4
4
|
import { useController, useFormContext } from 'react-hook-form'
|
|
5
5
|
import { MultiSelect, MultiSelectOption } from '@/components/multi-select'
|
|
6
|
+
import { useI18n } from 'nextjs-cms/translations/client'
|
|
6
7
|
|
|
7
8
|
export default function MultipleSelectFormInput({ input }: { input: SelectMultipleFieldClientConfig }) {
|
|
9
|
+
const t = useI18n()
|
|
8
10
|
const { control } = useFormContext()
|
|
9
11
|
const {
|
|
10
12
|
field,
|
|
@@ -74,7 +76,7 @@ export default function MultipleSelectFormInput({ input }: { input: SelectMultip
|
|
|
74
76
|
options={multiSelectOptions}
|
|
75
77
|
onValueChange={handleValueChange}
|
|
76
78
|
defaultValue={currentValue}
|
|
77
|
-
|
|
79
|
+
placeholder={t('selectOptions') as string}
|
|
78
80
|
disabled={field.disabled}
|
|
79
81
|
className='w-full py-3 ring-2 ring-gray-300'
|
|
80
82
|
/>
|
|
@@ -126,7 +126,7 @@ export default function PhotoFormInput({ input, sectionName }: { input: PhotoFie
|
|
|
126
126
|
<Image
|
|
127
127
|
className='mb-4 rounded p-1 ring-3 ring-green-600'
|
|
128
128
|
src={image}
|
|
129
|
-
alt='
|
|
129
|
+
alt={t('newImage') as string}
|
|
130
130
|
fill={true}
|
|
131
131
|
style={{
|
|
132
132
|
objectFit: 'contain',
|
|
@@ -6,6 +6,7 @@ import type { SlugFieldClientConfig } from 'nextjs-cms/core/fields'
|
|
|
6
6
|
import { useFormContext, useController, useWatch } from 'react-hook-form'
|
|
7
7
|
import { InputGroup, InputGroupInput, InputGroupAddon } from '@/components/ui/input-group'
|
|
8
8
|
import { Link2 } from 'lucide-react'
|
|
9
|
+
import { useI18n } from 'nextjs-cms/translations/client'
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Convert a string to a URL-friendly slug (for real-time input).
|
|
@@ -41,6 +42,7 @@ export default function SlugFormInput({
|
|
|
41
42
|
direction?: 'row' | 'col'
|
|
42
43
|
disabled?: boolean
|
|
43
44
|
}) {
|
|
45
|
+
const t = useI18n()
|
|
44
46
|
const { control } = useFormContext()
|
|
45
47
|
const {
|
|
46
48
|
field,
|
|
@@ -109,7 +111,7 @@ export default function SlugFormInput({
|
|
|
109
111
|
required={input.required}
|
|
110
112
|
>
|
|
111
113
|
<InputGroup className='bg-input'>
|
|
112
|
-
<InputGroupAddon align='inline-start' title='
|
|
114
|
+
<InputGroupAddon align='inline-start' title={t('autoGeneratedFromLinkedField') as string}>
|
|
113
115
|
<Link2 className='h-4 w-4' />
|
|
114
116
|
</InputGroupAddon>
|
|
115
117
|
<InputGroupInput
|
|
@@ -3,6 +3,7 @@ import { cva, type VariantProps } from 'class-variance-authority'
|
|
|
3
3
|
import { CheckIcon, XCircle, ChevronDown, XIcon, WandSparkles } from 'lucide-react'
|
|
4
4
|
|
|
5
5
|
import { cn } from '@/lib/utils'
|
|
6
|
+
import { useI18n } from 'nextjs-cms/translations/client'
|
|
6
7
|
import { Separator } from '@/components/ui/separator'
|
|
7
8
|
import { Button } from '@/components/ui/button'
|
|
8
9
|
import { Badge } from '@/components/ui/badge'
|
|
@@ -329,6 +330,7 @@ export const MultiSelect = React.forwardRef<MultiSelectRef, MultiSelectProps>(
|
|
|
329
330
|
},
|
|
330
331
|
ref,
|
|
331
332
|
) => {
|
|
333
|
+
const t = useI18n()
|
|
332
334
|
const [selectedValues, setSelectedValues] = React.useState<string[]>(defaultValue)
|
|
333
335
|
const [isPopoverOpen, setIsPopoverOpen] = React.useState(false)
|
|
334
336
|
const [isAnimating, setIsAnimating] = React.useState(false)
|
|
@@ -911,7 +913,7 @@ export const MultiSelect = React.forwardRef<MultiSelectRef, MultiSelectProps>(
|
|
|
911
913
|
handleClear()
|
|
912
914
|
}
|
|
913
915
|
}}
|
|
914
|
-
aria-label={
|
|
916
|
+
aria-label={t('clearAllSelectedOptions', { count: String(selectedValues.length) }) as string}
|
|
915
917
|
className='text-muted-foreground hover:text-foreground focus:ring-ring mx-2 flex h-4 w-4 cursor-pointer items-center justify-center rounded-sm focus:ring-2 focus:ring-offset-1 focus:outline-none'
|
|
916
918
|
>
|
|
917
919
|
<XIcon className='h-4 w-4' />
|
|
@@ -935,7 +937,7 @@ export const MultiSelect = React.forwardRef<MultiSelectRef, MultiSelectProps>(
|
|
|
935
937
|
id={listboxId}
|
|
936
938
|
role='listbox'
|
|
937
939
|
aria-multiselectable='true'
|
|
938
|
-
aria-label='
|
|
940
|
+
aria-label={t('availableOptions') as string}
|
|
939
941
|
className={cn(
|
|
940
942
|
'w-auto p-0',
|
|
941
943
|
getPopoverAnimationClass(),
|
|
@@ -957,17 +959,17 @@ export const MultiSelect = React.forwardRef<MultiSelectRef, MultiSelectProps>(
|
|
|
957
959
|
<Command>
|
|
958
960
|
{searchable && (
|
|
959
961
|
<CommandInput
|
|
960
|
-
placeholder='
|
|
962
|
+
placeholder={t('searchOptionsDots') as string}
|
|
961
963
|
onKeyDown={handleInputKeyDown}
|
|
962
964
|
value={searchValue}
|
|
963
965
|
onValueChange={setSearchValue}
|
|
964
|
-
aria-label='
|
|
966
|
+
aria-label={t('searchThroughOptions') as string}
|
|
965
967
|
aria-describedby={`${multiSelectId}-search-help`}
|
|
966
968
|
/>
|
|
967
969
|
)}
|
|
968
970
|
{searchable && (
|
|
969
971
|
<div id={`${multiSelectId}-search-help`} className='sr-only'>
|
|
970
|
-
|
|
972
|
+
{t('filterOptionsHint')}
|
|
971
973
|
</div>
|
|
972
974
|
)}
|
|
973
975
|
<CommandList
|
|
@@ -1109,7 +1111,7 @@ export const MultiSelect = React.forwardRef<MultiSelectRef, MultiSelectProps>(
|
|
|
1109
1111
|
onSelect={handleClear}
|
|
1110
1112
|
className='flex-1 cursor-pointer justify-center'
|
|
1111
1113
|
>
|
|
1112
|
-
|
|
1114
|
+
{t('clear')}
|
|
1113
1115
|
</CommandItem>
|
|
1114
1116
|
<Separator orientation='vertical' className='flex h-full min-h-6' />
|
|
1115
1117
|
</>
|
|
@@ -1118,7 +1120,7 @@ export const MultiSelect = React.forwardRef<MultiSelectRef, MultiSelectProps>(
|
|
|
1118
1120
|
onSelect={() => setIsPopoverOpen(false)}
|
|
1119
1121
|
className='max-w-full flex-1 cursor-pointer justify-center'
|
|
1120
1122
|
>
|
|
1121
|
-
|
|
1123
|
+
{t('close')}
|
|
1122
1124
|
</CommandItem>
|
|
1123
1125
|
</div>
|
|
1124
1126
|
</CommandGroup>
|
|
@@ -2,8 +2,10 @@ import { useState, useEffect } from 'react'
|
|
|
2
2
|
import { useTheme } from 'next-themes'
|
|
3
3
|
import { MoonIcon, SunIcon } from '@radix-ui/react-icons'
|
|
4
4
|
import { Spinner } from '@/components/ui/spinner'
|
|
5
|
+
import { useI18n } from 'nextjs-cms/translations/client'
|
|
5
6
|
|
|
6
7
|
const ThemeToggle = () => {
|
|
8
|
+
const t = useI18n()
|
|
7
9
|
const [mounted, setMounted] = useState(false)
|
|
8
10
|
const { theme, setTheme } = useTheme()
|
|
9
11
|
|
|
@@ -12,7 +14,7 @@ const ThemeToggle = () => {
|
|
|
12
14
|
}, [])
|
|
13
15
|
|
|
14
16
|
if (!mounted) {
|
|
15
|
-
return <Spinner className='size-6' />
|
|
17
|
+
return <Spinner className='size-6' aria-label={t('loading') as string} />
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
return (
|
|
@@ -24,7 +26,7 @@ const ThemeToggle = () => {
|
|
|
24
26
|
className='text-foreground hover:text-foreground/90 relative flex h-10 items-center justify-center rounded-full focus:outline-hidden cursor-pointer'
|
|
25
27
|
>
|
|
26
28
|
<span className='absolute -inset-1.5' />
|
|
27
|
-
<span className='sr-only'>
|
|
29
|
+
<span className='sr-only'>{t('theme')}</span>
|
|
28
30
|
{theme === 'dark' ? (
|
|
29
31
|
<SunIcon className='h-6 w-6' aria-hidden='true' />
|
|
30
32
|
) : (
|