create-nextjs-cms 0.9.26 → 0.9.28
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/globals.css +8 -0
- package/templates/default/components/form/inputs/document-form-input.tsx +11 -8
- package/templates/default/components/form/inputs/photo-form-input.tsx +11 -5
- package/templates/default/components/form/inputs/rich-text-form-input.tsx +12 -2
- package/templates/default/components/form/inputs/tags-form-input.tsx +5 -15
- package/templates/default/components/form/inputs/text-form-input.tsx +13 -3
- package/templates/default/components/form/inputs/textarea-form-input.tsx +15 -4
- package/templates/default/components/form/inputs/video-form-input.tsx +11 -8
- package/templates/default/dynamic-schemas/schema.ts +2 -18
- package/templates/default/env/env.ts +5 -1
- package/templates/default/package.json +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-nextjs-cms",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.28",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"tsx": "^4.20.6",
|
|
30
30
|
"typescript": "^5.9.2",
|
|
31
31
|
"@lzcms/eslint-config": "0.3.0",
|
|
32
|
-
"@lzcms/
|
|
33
|
-
"@lzcms/
|
|
32
|
+
"@lzcms/prettier-config": "0.1.0",
|
|
33
|
+
"@lzcms/tsconfig": "0.1.0"
|
|
34
34
|
},
|
|
35
35
|
"prettier": "@lzcms/prettier-config",
|
|
36
36
|
"scripts": {
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
@custom-variant dark (&:is(.dark *));
|
|
5
5
|
|
|
6
|
+
@source "../node_modules/@nextjscms/plugin-*/**/*.{tsx,ts,jsx,js}";
|
|
7
|
+
|
|
6
8
|
/**
|
|
7
9
|
* Background colors for the dashboard plugin
|
|
8
10
|
*/
|
|
@@ -198,9 +200,15 @@
|
|
|
198
200
|
* {
|
|
199
201
|
@apply border-border;
|
|
200
202
|
}
|
|
203
|
+
html {
|
|
204
|
+
scrollbar-gutter: stable;
|
|
205
|
+
}
|
|
201
206
|
body {
|
|
202
207
|
@apply bg-background text-foreground;
|
|
203
208
|
}
|
|
209
|
+
html body[data-scroll-locked] {
|
|
210
|
+
margin-right: 0 !important;
|
|
211
|
+
}
|
|
204
212
|
}
|
|
205
213
|
|
|
206
214
|
/* Progress bar indeterminate animation */
|
|
@@ -88,7 +88,7 @@ export default function DocumentFormInput({
|
|
|
88
88
|
file={input.value}
|
|
89
89
|
width={450}
|
|
90
90
|
height={620}
|
|
91
|
-
className='
|
|
91
|
+
className='mb-4 rounded p-1 ring-3 ring-gray-400'
|
|
92
92
|
/>
|
|
93
93
|
)
|
|
94
94
|
|
|
@@ -127,19 +127,22 @@ export default function DocumentFormInput({
|
|
|
127
127
|
<CardContent className='flex flex-col gap-1'>
|
|
128
128
|
<div className='mb-2 flex flex-col text-sm'>
|
|
129
129
|
{input.maxFileSize ? (
|
|
130
|
-
<div
|
|
130
|
+
<div className='flex flex-wrap items-center gap-2'>
|
|
131
131
|
<InfoIcon size={14} />
|
|
132
132
|
<span>{t('maxFileSize')}:</span>
|
|
133
133
|
<Badge
|
|
134
|
+
dir='ltr'
|
|
134
135
|
variant={'secondary'}
|
|
135
136
|
>{`${input.maxFileSize.size} ${input.maxFileSize.unit.toUpperCase()}`}</Badge>
|
|
136
137
|
</div>
|
|
137
138
|
) : null}
|
|
138
139
|
{input.extensions ? (
|
|
139
|
-
<div
|
|
140
|
+
<div className='flex flex-wrap items-center gap-2'>
|
|
140
141
|
<InfoIcon size={14} />
|
|
141
142
|
<span>{t('allowedExtensions')}:</span>
|
|
142
|
-
<Badge variant={'secondary'}>
|
|
143
|
+
<Badge dir='ltr' variant={'secondary'}>
|
|
144
|
+
{input.extensions.join(', ')}
|
|
145
|
+
</Badge>
|
|
143
146
|
</div>
|
|
144
147
|
) : null}
|
|
145
148
|
</div>
|
|
@@ -165,12 +168,12 @@ export default function DocumentFormInput({
|
|
|
165
168
|
<ChevronRight fontSize='large' />
|
|
166
169
|
<div className='relative'>
|
|
167
170
|
<X
|
|
168
|
-
className='absolute -
|
|
171
|
+
className='absolute -top-3 -right-3 z-10 cursor-pointer rounded-full border-2 border-gray-500 bg-white p-1'
|
|
169
172
|
onClick={clearSelectedFile}
|
|
170
173
|
/>
|
|
171
174
|
<embed
|
|
172
175
|
src={image}
|
|
173
|
-
className='
|
|
176
|
+
className='mb-4 max-w-full rounded p-1 ring-3 ring-green-600'
|
|
174
177
|
width={350}
|
|
175
178
|
height={520}
|
|
176
179
|
/>
|
|
@@ -204,7 +207,7 @@ export default function DocumentFormInput({
|
|
|
204
207
|
<div className='w-[400px] max-w-full'>
|
|
205
208
|
<button
|
|
206
209
|
type='button'
|
|
207
|
-
className='
|
|
210
|
+
className='flex w-full flex-wrap items-center justify-center gap-1.5 rounded border bg-linear-to-r from-red-600 to-amber-500 p-2 text-center text-sm font-bold text-white uppercase drop-shadow-sm hover:from-red-400 hover:to-amber-400'
|
|
208
211
|
onClick={() => {
|
|
209
212
|
setModal({
|
|
210
213
|
title: t('deleteDocument'),
|
|
@@ -244,7 +247,7 @@ export default function DocumentFormInput({
|
|
|
244
247
|
<div className='dark:border-neutral my-2 flex w-[400px] max-w-full flex-col gap-1 rounded-lg border border-gray-400 p-2'>
|
|
245
248
|
<button
|
|
246
249
|
type='button'
|
|
247
|
-
className='
|
|
250
|
+
className='flex w-full flex-wrap items-center justify-center gap-1.5 rounded border bg-linear-to-r from-blue-700 to-sky-500 p-2 text-center text-sm font-bold text-white uppercase drop-shadow-sm hover:from-blue-500 hover:to-sky-400'
|
|
248
251
|
onClick={() => {
|
|
249
252
|
if (fileInputContainerRef.current?.firstChild) {
|
|
250
253
|
;(
|
|
@@ -122,29 +122,35 @@ export default function PhotoFormInput({
|
|
|
122
122
|
<CardContent className='flex flex-col gap-1'>
|
|
123
123
|
<div className='mb-2 flex flex-col text-sm'>
|
|
124
124
|
{input.size ? (
|
|
125
|
-
<div
|
|
125
|
+
<div className='flex flex-wrap items-center gap-2'>
|
|
126
126
|
<InfoIcon size={14} />
|
|
127
127
|
<span>
|
|
128
128
|
{t('strict' in input.size ? 'imageDimensionsMustBe' : 'imageRecommendedDimensions')}
|
|
129
129
|
:
|
|
130
130
|
</span>
|
|
131
|
-
<Badge
|
|
131
|
+
<Badge
|
|
132
|
+
dir='ltr'
|
|
133
|
+
variant={'secondary'}
|
|
134
|
+
>{`${input.size.width} x ${input.size.height}`}</Badge>
|
|
132
135
|
</div>
|
|
133
136
|
) : null}
|
|
134
137
|
{input.maxFileSize ? (
|
|
135
|
-
<div
|
|
138
|
+
<div className='flex flex-wrap items-center gap-2'>
|
|
136
139
|
<InfoIcon size={14} />
|
|
137
140
|
<span>{t('maxFileSize')}:</span>
|
|
138
141
|
<Badge
|
|
142
|
+
dir='ltr'
|
|
139
143
|
variant={'secondary'}
|
|
140
144
|
>{`${input.maxFileSize.size} ${input.maxFileSize.unit.toUpperCase()}`}</Badge>
|
|
141
145
|
</div>
|
|
142
146
|
) : null}
|
|
143
147
|
{input.extensions ? (
|
|
144
|
-
<div
|
|
148
|
+
<div className='flex flex-wrap items-center gap-2'>
|
|
145
149
|
<InfoIcon size={14} />
|
|
146
150
|
<span>{t('allowedExtensions')}:</span>
|
|
147
|
-
<Badge variant={'secondary'}>
|
|
151
|
+
<Badge dir='ltr' variant={'secondary'}>
|
|
152
|
+
{input.extensions.join(', ')}
|
|
153
|
+
</Badge>
|
|
148
154
|
</div>
|
|
149
155
|
) : null}
|
|
150
156
|
</div>
|
|
@@ -6,6 +6,7 @@ import { useAxiosPrivate } from 'nextjs-cms/api/client'
|
|
|
6
6
|
import { RichTextFieldClientConfig } from 'nextjs-cms/core/fields'
|
|
7
7
|
import { FieldError, useFormContext } from 'react-hook-form'
|
|
8
8
|
import { useLocale } from '@/components/form/content-locale-context'
|
|
9
|
+
import { useUILanguage } from 'nextjs-cms/translations/client'
|
|
9
10
|
|
|
10
11
|
export default function RichTextFormInput({ input }: { input: RichTextFieldClientConfig }) {
|
|
11
12
|
const { setValue, register, formState } = useFormContext()
|
|
@@ -15,6 +16,7 @@ export default function RichTextFormInput({ input }: { input: RichTextFieldClien
|
|
|
15
16
|
const [initialValue, setInitialValue] = useState('')
|
|
16
17
|
const [key, setKey] = useState(0) // Step 1: Add key state
|
|
17
18
|
const locale = useLocale()
|
|
19
|
+
const uiLanguage = useUILanguage()
|
|
18
20
|
// const [editorPhotos, setEditorPhotos] = useState<string[]>([])
|
|
19
21
|
// const { theme } = useTheme()
|
|
20
22
|
const axiosPrivate = useAxiosPrivate()
|
|
@@ -115,8 +117,16 @@ export default function RichTextFormInput({ input }: { input: RichTextFieldClien
|
|
|
115
117
|
menubar: 'file edit view insert format tools table tc help',
|
|
116
118
|
plugins: plugins,
|
|
117
119
|
toolbar: toolbar,
|
|
118
|
-
|
|
119
|
-
|
|
120
|
+
directionality:
|
|
121
|
+
input.rtl !== undefined
|
|
122
|
+
? input.rtl
|
|
123
|
+
? 'rtl'
|
|
124
|
+
: 'ltr'
|
|
125
|
+
: locale
|
|
126
|
+
? locale.rtl
|
|
127
|
+
? 'rtl'
|
|
128
|
+
: 'ltr'
|
|
129
|
+
: uiLanguage.dir,
|
|
120
130
|
content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:14px }',
|
|
121
131
|
// skin: theme === 'light' ? 'oxide' : 'oxide-dark',
|
|
122
132
|
// content_css: theme === 'light' ? 'default' : 'dark',
|
|
@@ -10,13 +10,7 @@ import { useSearchParams } from 'next/navigation'
|
|
|
10
10
|
import { cn } from '@/lib/utils'
|
|
11
11
|
import { trpc } from '@/app/_trpc/client'
|
|
12
12
|
|
|
13
|
-
export default function TagsFormInput({
|
|
14
|
-
input,
|
|
15
|
-
sectionName,
|
|
16
|
-
}: {
|
|
17
|
-
input: TagsFieldClientConfig
|
|
18
|
-
sectionName: string
|
|
19
|
-
}) {
|
|
13
|
+
export default function TagsFormInput({ input, sectionName }: { input: TagsFieldClientConfig; sectionName: string }) {
|
|
20
14
|
const t = useI18n()
|
|
21
15
|
const searchParams = useSearchParams()
|
|
22
16
|
const localeParam = searchParams.get('locale')
|
|
@@ -101,16 +95,12 @@ export default function TagsFormInput({
|
|
|
101
95
|
if (showDropdown && filteredSuggestions.length > 0) {
|
|
102
96
|
if (event.key === 'ArrowDown') {
|
|
103
97
|
event.preventDefault()
|
|
104
|
-
setHighlightedIndex((prev) =>
|
|
105
|
-
prev < filteredSuggestions.length - 1 ? prev + 1 : 0,
|
|
106
|
-
)
|
|
98
|
+
setHighlightedIndex((prev) => (prev < filteredSuggestions.length - 1 ? prev + 1 : 0))
|
|
107
99
|
return
|
|
108
100
|
}
|
|
109
101
|
if (event.key === 'ArrowUp') {
|
|
110
102
|
event.preventDefault()
|
|
111
|
-
setHighlightedIndex((prev) =>
|
|
112
|
-
prev > 0 ? prev - 1 : filteredSuggestions.length - 1,
|
|
113
|
-
)
|
|
103
|
+
setHighlightedIndex((prev) => (prev > 0 ? prev - 1 : filteredSuggestions.length - 1))
|
|
114
104
|
return
|
|
115
105
|
}
|
|
116
106
|
if (event.key === 'Enter' && highlightedIndex >= 0) {
|
|
@@ -180,7 +170,7 @@ export default function TagsFormInput({
|
|
|
180
170
|
<div className='relative'>
|
|
181
171
|
<div
|
|
182
172
|
className={cn(
|
|
183
|
-
'bg-input text-foreground w-full rounded px-3 py-1 shadow-xs ring-2 ring-gray-300 outline-0 transition-colors
|
|
173
|
+
'bg-input text-foreground w-full rounded px-3 py-1 shadow-xs ring-2 ring-gray-300 outline-0 transition-colors focus-within:ring-blue-500 hover:ring-gray-400',
|
|
184
174
|
'flex min-h-9 flex-wrap items-center gap-1 text-sm',
|
|
185
175
|
'cursor-text',
|
|
186
176
|
field.disabled && 'cursor-not-allowed opacity-50',
|
|
@@ -192,7 +182,7 @@ export default function TagsFormInput({
|
|
|
192
182
|
key={`${tag}-${index}`}
|
|
193
183
|
variant='secondary'
|
|
194
184
|
className={cn(
|
|
195
|
-
'flex items-center gap-1 px-2 py-1 text-xs transition-all duration-200
|
|
185
|
+
'border-foreground/40 flex items-center gap-1 px-2 py-1 text-xs transition-all duration-200',
|
|
196
186
|
highlightedTag === tag ? 'bg-success/20 border-success/50 animate-pulse' : '',
|
|
197
187
|
)}
|
|
198
188
|
>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
1
|
import FormInputElement from '@/components/form/form-input-element'
|
|
3
2
|
import type { TextFieldClientConfig } from 'nextjs-cms/core/fields'
|
|
4
3
|
import { useFormContext, useController } from 'react-hook-form'
|
|
5
4
|
import { useLocale } from '@/components/form/content-locale-context'
|
|
5
|
+
import { useUILanguage } from 'nextjs-cms/translations/client'
|
|
6
6
|
|
|
7
7
|
export default function TextFormInput({
|
|
8
8
|
input,
|
|
@@ -23,8 +23,8 @@ export default function TextFormInput({
|
|
|
23
23
|
defaultValue: input.value ?? undefined,
|
|
24
24
|
disabled: disabled,
|
|
25
25
|
})
|
|
26
|
+
const uiLanguage = useUILanguage()
|
|
26
27
|
const locale = useLocale()
|
|
27
|
-
|
|
28
28
|
return (
|
|
29
29
|
<FormInputElement
|
|
30
30
|
validationError={error}
|
|
@@ -36,7 +36,17 @@ export default function TextFormInput({
|
|
|
36
36
|
<input
|
|
37
37
|
placeholder={input.placeholder ? input.placeholder : input.label}
|
|
38
38
|
type='text'
|
|
39
|
-
dir={
|
|
39
|
+
dir={
|
|
40
|
+
input.rtl !== undefined
|
|
41
|
+
? input.rtl
|
|
42
|
+
? 'rtl'
|
|
43
|
+
: 'ltr'
|
|
44
|
+
: locale
|
|
45
|
+
? locale.rtl
|
|
46
|
+
? 'rtl'
|
|
47
|
+
: 'ltr'
|
|
48
|
+
: uiLanguage.dir
|
|
49
|
+
}
|
|
40
50
|
readOnly={disabled}
|
|
41
51
|
disabled={field.disabled}
|
|
42
52
|
name={field.name}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
1
|
import FormInputElement from '@/components/form/form-input-element'
|
|
3
2
|
import { TextAreaFieldClientConfig } from 'nextjs-cms/core/fields'
|
|
4
|
-
import {
|
|
3
|
+
import { useController, useFormContext } from 'react-hook-form'
|
|
5
4
|
import { useLocale } from '@/components/form/content-locale-context'
|
|
5
|
+
import { useUILanguage } from 'nextjs-cms/translations/client'
|
|
6
6
|
|
|
7
7
|
export default function TextareaFormInput({
|
|
8
8
|
input,
|
|
@@ -24,6 +24,7 @@ export default function TextareaFormInput({
|
|
|
24
24
|
disabled: input.readonly,
|
|
25
25
|
})
|
|
26
26
|
const locale = useLocale()
|
|
27
|
+
const uiLanguage = useUILanguage()
|
|
27
28
|
|
|
28
29
|
return (
|
|
29
30
|
<FormInputElement
|
|
@@ -35,7 +36,17 @@ export default function TextareaFormInput({
|
|
|
35
36
|
>
|
|
36
37
|
<textarea
|
|
37
38
|
placeholder={input.label}
|
|
38
|
-
dir={
|
|
39
|
+
dir={
|
|
40
|
+
input.rtl !== undefined
|
|
41
|
+
? input.rtl
|
|
42
|
+
? 'rtl'
|
|
43
|
+
: 'ltr'
|
|
44
|
+
: locale
|
|
45
|
+
? locale.rtl
|
|
46
|
+
? 'rtl'
|
|
47
|
+
: 'ltr'
|
|
48
|
+
: uiLanguage.dir
|
|
49
|
+
}
|
|
39
50
|
readOnly={input.readonly}
|
|
40
51
|
disabled={field.disabled}
|
|
41
52
|
name={field.name}
|
|
@@ -43,7 +54,7 @@ export default function TextareaFormInput({
|
|
|
43
54
|
onBlur={field.onBlur}
|
|
44
55
|
value={field.value}
|
|
45
56
|
ref={field.ref}
|
|
46
|
-
className='h-[150px] w-full rounded
|
|
57
|
+
className='bg-input text-foreground h-[150px] w-full rounded p-3 shadow-xs ring-2 ring-gray-300 outline-0 hover:ring-gray-400 focus:ring-blue-500'
|
|
47
58
|
></textarea>
|
|
48
59
|
</FormInputElement>
|
|
49
60
|
)
|
|
@@ -88,7 +88,7 @@ export default function VideoFormInput({
|
|
|
88
88
|
file={input.value}
|
|
89
89
|
width={400}
|
|
90
90
|
height={280}
|
|
91
|
-
className='
|
|
91
|
+
className='mb-4 rounded p-1 ring-3 ring-gray-400'
|
|
92
92
|
/>
|
|
93
93
|
)
|
|
94
94
|
|
|
@@ -127,19 +127,22 @@ export default function VideoFormInput({
|
|
|
127
127
|
<CardContent className='flex flex-col gap-1'>
|
|
128
128
|
<div className='mb-2 flex flex-col text-sm'>
|
|
129
129
|
{input.maxFileSize ? (
|
|
130
|
-
<div
|
|
130
|
+
<div className='flex flex-wrap items-center gap-2'>
|
|
131
131
|
<InfoIcon size={14} />
|
|
132
132
|
<span>{t('maxFileSize')}:</span>
|
|
133
133
|
<Badge
|
|
134
|
+
dir='ltr'
|
|
134
135
|
variant={'secondary'}
|
|
135
136
|
>{`${input.maxFileSize.size} ${input.maxFileSize.unit.toUpperCase()}`}</Badge>
|
|
136
137
|
</div>
|
|
137
138
|
) : null}
|
|
138
139
|
{input.extensions ? (
|
|
139
|
-
<div
|
|
140
|
+
<div className='flex flex-wrap items-center gap-2'>
|
|
140
141
|
<InfoIcon size={14} />
|
|
141
142
|
<span>{t('allowedExtensions')}:</span>
|
|
142
|
-
<Badge variant={'secondary'}>
|
|
143
|
+
<Badge dir='ltr' variant={'secondary'}>
|
|
144
|
+
{input.extensions.join(', ')}
|
|
145
|
+
</Badge>
|
|
143
146
|
</div>
|
|
144
147
|
) : null}
|
|
145
148
|
</div>
|
|
@@ -165,12 +168,12 @@ export default function VideoFormInput({
|
|
|
165
168
|
<ChevronRight fontSize='large' />
|
|
166
169
|
<div className='relative'>
|
|
167
170
|
<X
|
|
168
|
-
className='absolute -
|
|
171
|
+
className='absolute -top-3 -right-3 z-10 cursor-pointer rounded-full border-2 border-gray-500 bg-white p-1'
|
|
169
172
|
onClick={clearSelectedFile}
|
|
170
173
|
/>
|
|
171
174
|
<embed
|
|
172
175
|
src={image}
|
|
173
|
-
className='
|
|
176
|
+
className='mb-4 max-w-full rounded p-1 ring-3 ring-green-600'
|
|
174
177
|
width={300}
|
|
175
178
|
height={190}
|
|
176
179
|
/>
|
|
@@ -204,7 +207,7 @@ export default function VideoFormInput({
|
|
|
204
207
|
<div className='w-[400px] max-w-full'>
|
|
205
208
|
<button
|
|
206
209
|
type='button'
|
|
207
|
-
className='
|
|
210
|
+
className='flex w-full flex-wrap items-center justify-center gap-1.5 rounded border bg-linear-to-r from-red-600 to-amber-500 p-2 text-center text-sm font-bold text-white uppercase drop-shadow-sm hover:from-red-400 hover:to-amber-400'
|
|
208
211
|
onClick={() => {
|
|
209
212
|
setModal({
|
|
210
213
|
title: t('deleteVideo'),
|
|
@@ -244,7 +247,7 @@ export default function VideoFormInput({
|
|
|
244
247
|
<div className='dark:border-neutral my-2 flex w-[400px] max-w-full flex-col gap-1 rounded-lg border border-gray-400 p-2'>
|
|
245
248
|
<button
|
|
246
249
|
type='button'
|
|
247
|
-
className='
|
|
250
|
+
className='flex w-full flex-wrap items-center justify-center gap-1.5 rounded border bg-linear-to-r from-blue-700 to-sky-500 p-2 text-center text-sm font-bold text-white uppercase drop-shadow-sm hover:from-blue-500 hover:to-sky-400'
|
|
248
251
|
onClick={() => {
|
|
249
252
|
if (fileInputContainerRef.current?.firstChild) {
|
|
250
253
|
;(
|
|
@@ -46,25 +46,9 @@ export const TestSectionTable = mysqlTable('test_section', {
|
|
|
46
46
|
|
|
47
47
|
export const TestTagsTable = mysqlTable('test_tags', {
|
|
48
48
|
testId: int('test_id').notNull(),
|
|
49
|
-
tagName: varchar('tag_name', { length: 255 }).notNull()
|
|
50
|
-
locale: varchar('locale', { length: 255 }).notNull()
|
|
49
|
+
tagName: varchar('tag_name', { length: 255 }).notNull()
|
|
51
50
|
}, (table) => [
|
|
52
|
-
primaryKey({ columns: [table.testId, table.tagName
|
|
53
|
-
]);
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
export const TestSectionLocalesTable = mysqlTable('test_section_locales', {
|
|
57
|
-
id: int('id').autoincrement().notNull().primaryKey(),
|
|
58
|
-
parentId: int('parent_id').notNull(),
|
|
59
|
-
locale: varchar('locale', { length: 10 }).notNull(),
|
|
60
|
-
contentType: mysqlEnum('content_type', ['ad', 'user']).notNull(),
|
|
61
|
-
price: int('price'),
|
|
62
|
-
appId: varchar('app_id', { length: 36 }).notNull(),
|
|
63
|
-
title: varchar('title', { length: 255 }).notNull(),
|
|
64
|
-
photo: varchar('photo', { length: 255 }),
|
|
65
|
-
category: varchar('category', { length: 255 }).notNull()
|
|
66
|
-
}, (table) => [
|
|
67
|
-
unique('test_section_locales_parent_id_locale_unique').on(table.parentId, table.locale)
|
|
51
|
+
primaryKey({ columns: [table.testId, table.tagName] })
|
|
68
52
|
]);
|
|
69
53
|
|
|
70
54
|
|
|
@@ -12,7 +12,6 @@ export const env = createEnv({
|
|
|
12
12
|
CSRF_TOKEN_SECRET: z.string(),
|
|
13
13
|
ACCESS_TOKEN_EXPIRATION: z.string(),
|
|
14
14
|
REFRESH_TOKEN_EXPIRATION: z.string(),
|
|
15
|
-
NODE_ENV: z.enum(['development', 'test', 'production']).default('development'),
|
|
16
15
|
},
|
|
17
16
|
|
|
18
17
|
/**
|
|
@@ -25,9 +24,14 @@ export const env = createEnv({
|
|
|
25
24
|
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY: z.string().optional(),
|
|
26
25
|
},
|
|
27
26
|
|
|
27
|
+
shared: {
|
|
28
|
+
NODE_ENV: z.enum(['development', 'test', 'production']).default('development'),
|
|
29
|
+
},
|
|
30
|
+
|
|
28
31
|
experimental__runtimeEnv: {
|
|
29
32
|
NEXT_PUBLIC_RICHTEXT_INLINE_PUBLIC_URL: process.env.NEXT_PUBLIC_RICHTEXT_INLINE_PUBLIC_URL,
|
|
30
33
|
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY: process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY,
|
|
34
|
+
NODE_ENV: process.env.NODE_ENV,
|
|
31
35
|
},
|
|
32
36
|
/**
|
|
33
37
|
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially
|