434 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			434 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| 'use client'
 | ||
| 
 | ||
| import {zodResolver} from '@hookform/resolvers/zod'
 | ||
| import dynamic from 'next/dynamic'
 | ||
| import React, {useEffect, useMemo, useRef, useState} from 'react'
 | ||
| import {useFieldArray, useForm} from 'react-hook-form'
 | ||
| import toast from 'react-hot-toast'
 | ||
| import {z} from 'zod'
 | ||
| 
 | ||
| import {onProductCreateAction} from '@/actions/admin/product'
 | ||
| import {i18nDefaultLocale, i18nLocales} from '@/i18n-config'
 | ||
| import {BaseEditorConfig} from '@/lib/config/editor'
 | ||
| import {createProductFormSchema} from '@/lib/schemas/admin/product'
 | ||
| import {toEmptyParams} from '@/lib/utils'
 | ||
| import {Button} from '@/ui/button'
 | ||
| import {
 | ||
| 	Form,
 | ||
| 	FormControl,
 | ||
| 	FormDescription,
 | ||
| 	FormField,
 | ||
| 	FormItem,
 | ||
| 	FormLabel,
 | ||
| 	FormMessage
 | ||
| } from '@/ui/form'
 | ||
| import {Input} from '@/ui/input'
 | ||
| import {Switch} from '@/ui/switch'
 | ||
| import {Tabs, TabsContent, TabsList, TabsTrigger} from '@/ui/tabs'
 | ||
| 
 | ||
| const JoditEditor = dynamic(() => import('jodit-react'), {ssr: false})
 | ||
| 
 | ||
| let localesValues = {
 | ||
| 	title: '',
 | ||
| 	shortTitle: '',
 | ||
| 	headingTitle: '',
 | ||
| 	description: '',
 | ||
| 	content: '',
 | ||
| 	instruction: ''
 | ||
| }
 | ||
| 
 | ||
| let metaValues = {
 | ||
| 	title: '',
 | ||
| 	description: '',
 | ||
| 	keywords: '',
 | ||
| 	author: ''
 | ||
| }
 | ||
| 
 | ||
| export default function ProductCreateEditForm({data}: {data?: any}) {
 | ||
| 	const [loading, setLoading] = useState(false)
 | ||
| 	const [error, setError] = useState('')
 | ||
| 	const [success, setSuccess] = useState('')
 | ||
| 	const [description0, setDescription0] = useState(
 | ||
| 		data?.locales[0].description || ''
 | ||
| 	)
 | ||
| 	const [description1, setDescription1] = useState(
 | ||
| 		data?.locales[1].description || ''
 | ||
| 	)
 | ||
| 	const [content0, setContent0] = useState(data?.locales[0].content || '')
 | ||
| 	const [content1, setContent1] = useState(data?.locales[1].content || '')
 | ||
| 	const [instruction0, setInstruction0] = useState(
 | ||
| 		data?.locales[0].instruction || ''
 | ||
| 	)
 | ||
| 	const [instruction1, setInstruction1] = useState(
 | ||
| 		data?.locales[1].instruction || ''
 | ||
| 	)
 | ||
| 	const editor = useRef(null) //declared a null value
 | ||
| 
 | ||
| 	const config = useMemo(() => BaseEditorConfig, [])
 | ||
| 
 | ||
| 	const form = useForm<z.infer<typeof createProductFormSchema>>({
 | ||
| 		resolver: zodResolver(createProductFormSchema),
 | ||
| 		mode: 'onBlur',
 | ||
| 		defaultValues: data
 | ||
| 			? (data => {
 | ||
| 					const {locales, meta} = data
 | ||
| 
 | ||
| 					return {
 | ||
| 						published: data.toStore[0].published,
 | ||
| 						price: data.toStore[0].price,
 | ||
| 						pricePromotional: data.toStore[0].pricePromotional,
 | ||
| 						image: data.image,
 | ||
| 						locales: toEmptyParams(locales) as any,
 | ||
| 						meta: meta
 | ||
| 							? (toEmptyParams(meta) as any)
 | ||
| 							: [{...metaValues}, {...metaValues}]
 | ||
| 					}
 | ||
| 				})(data)
 | ||
| 			: {
 | ||
| 					published: false,
 | ||
| 					price: '0',
 | ||
| 					pricePromotional: '0',
 | ||
| 					image: '',
 | ||
| 					locales: [
 | ||
| 						{lang: 'uk', ...localesValues},
 | ||
| 						{lang: 'ru', ...localesValues}
 | ||
| 					],
 | ||
| 					meta: [{...metaValues}, {...metaValues}]
 | ||
| 				}
 | ||
| 	})
 | ||
| 
 | ||
| 	const {register, setValue} = form
 | ||
| 
 | ||
| 	useEffect(() => {
 | ||
| 		register('locales.0.description')
 | ||
| 		register('locales.0.content')
 | ||
| 		register('locales.0.instruction')
 | ||
| 		register('locales.1.description')
 | ||
| 		register('locales.1.content')
 | ||
| 		register('locales.1.instruction')
 | ||
| 	}, [register])
 | ||
| 
 | ||
| 	const {fields: localeFields} = useFieldArray({
 | ||
| 		name: 'locales',
 | ||
| 		control: form.control
 | ||
| 	})
 | ||
| 
 | ||
| 	const {fields: metaFields} = useFieldArray({
 | ||
| 		name: 'meta',
 | ||
| 		control: form.control
 | ||
| 	})
 | ||
| 
 | ||
| 	console.log(form.formState.errors)
 | ||
| 
 | ||
| 	const onSubmit = async (values: z.infer<typeof createProductFormSchema>) => {
 | ||
| 		setLoading(true)
 | ||
| 		onProductCreateAction(values).then((res: any) => {
 | ||
| 			if (res?.error) {
 | ||
| 				setError(res?.error)
 | ||
| 				setSuccess('')
 | ||
| 				setLoading(false)
 | ||
| 				toast.error(res?.error)
 | ||
| 			} else {
 | ||
| 				setSuccess(res?.success as string)
 | ||
| 				setError('')
 | ||
| 				setLoading(false)
 | ||
| 				toast.success(res?.success)
 | ||
| 			}
 | ||
| 		})
 | ||
| 	}
 | ||
| 
 | ||
| 	return (
 | ||
| 		<Form {...form}>
 | ||
| 			<form
 | ||
| 				action=''
 | ||
| 				onSubmit={form.handleSubmit(onSubmit)}
 | ||
| 				className='bgs-grasy-50/50 mx-auto mb-8 min-w-[640px] max-w-[992px] flex-1 space-y-5 rounded-lg border p-4'
 | ||
| 			>
 | ||
| 				<div className='mx-auto my-4 w-full space-y-4'>
 | ||
| 					<h1 className='mb-6 text-center text-2xl font-bold text-brand-violet'>
 | ||
| 						ДОДАВАННЯ ТОВАРУ ДО БАЗИ
 | ||
| 					</h1>
 | ||
| 					<div className='my-4 space-y-4'>
 | ||
| 						<FormField
 | ||
| 							control={form.control}
 | ||
| 							name='published'
 | ||
| 							render={({field}) => (
 | ||
| 								<FormItem className='flex flex-row items-center justify-between rounded-lg border bg-gray-50 p-4'>
 | ||
| 									<div className='space-y-0.5'>
 | ||
| 										<FormLabel className='text-base'>Опублікувати</FormLabel>
 | ||
| 										<FormDescription>
 | ||
| 											Відразу після збереження буде розміщено на сайті
 | ||
| 										</FormDescription>
 | ||
| 									</div>
 | ||
| 									<FormControl>
 | ||
| 										<Switch
 | ||
| 											checked={field.value}
 | ||
| 											onCheckedChange={field.onChange}
 | ||
| 										/>
 | ||
| 									</FormControl>
 | ||
| 								</FormItem>
 | ||
| 							)}
 | ||
| 						/>
 | ||
| 						<div className='flex flex-row items-center justify-between gap-4 rounded-lg border bg-gray-50 p-4'>
 | ||
| 							<div className='w-1/2'>
 | ||
| 								<FormField
 | ||
| 									control={form.control}
 | ||
| 									name='price'
 | ||
| 									render={({field}) => (
 | ||
| 										<FormItem className={'w-full'}>
 | ||
| 											<FormLabel>Ціна за одиницю товару</FormLabel>
 | ||
| 											<FormControl>
 | ||
| 												<Input type='text' placeholder='' {...field} />
 | ||
| 											</FormControl>
 | ||
| 											<FormMessage />
 | ||
| 										</FormItem>
 | ||
| 									)}
 | ||
| 								/>
 | ||
| 							</div>
 | ||
| 							<div className='w-1/2'>
 | ||
| 								<FormField
 | ||
| 									control={form.control}
 | ||
| 									name='pricePromotional'
 | ||
| 									render={({field}) => (
 | ||
| 										<FormItem className={'w-full'}>
 | ||
| 											<FormLabel>Акційна Ціна</FormLabel>
 | ||
| 											<FormControl>
 | ||
| 												<Input type='text' placeholder='' {...field} />
 | ||
| 											</FormControl>
 | ||
| 											<FormMessage />
 | ||
| 										</FormItem>
 | ||
| 									)}
 | ||
| 								/>
 | ||
| 							</div>
 | ||
| 						</div>
 | ||
| 						<fieldset className='flex gap-4 rounded-lg border bg-gray-50 p-4'>
 | ||
| 							<FormField
 | ||
| 								control={form.control}
 | ||
| 								name='image'
 | ||
| 								render={({field}) => (
 | ||
| 									<FormItem className={'w-full'}>
 | ||
| 										<FormLabel>Головне зображення</FormLabel>
 | ||
| 										<FormControl>
 | ||
| 											<Input type='text' placeholder='' {...field} />
 | ||
| 										</FormControl>
 | ||
| 										<FormDescription>
 | ||
| 											Вкажіть шліх до зображення відносно публічної папки
 | ||
| 										</FormDescription>
 | ||
| 										<FormMessage />
 | ||
| 									</FormItem>
 | ||
| 								)}
 | ||
| 							/>
 | ||
| 						</fieldset>
 | ||
| 					</div>
 | ||
| 					<Tabs
 | ||
| 						defaultValue={i18nDefaultLocale}
 | ||
| 						className='min-h-[560px] rounded-lg border p-4'
 | ||
| 					>
 | ||
| 						<TabsList className='grid w-full grid-cols-2'>
 | ||
| 							{i18nLocales.map(locale => (
 | ||
| 								<TabsTrigger key={locale.icon} value={locale.code}>
 | ||
| 									{locale.nameUkr}
 | ||
| 								</TabsTrigger>
 | ||
| 							))}
 | ||
| 						</TabsList>
 | ||
| 						{localeFields.map((_, index) => (
 | ||
| 							<TabsContent
 | ||
| 								id={`form-tab-${form.getValues(`locales.${index}.lang`)}`}
 | ||
| 								value={form.getValues(`locales.${index}.lang`)}
 | ||
| 								key={index}
 | ||
| 								className='space-y-4'
 | ||
| 							>
 | ||
| 								<FormField
 | ||
| 									control={form.control}
 | ||
| 									key={index}
 | ||
| 									name={`locales.${index}.lang`}
 | ||
| 									render={({field}) => (
 | ||
| 										<FormItem className={'w-full'}>
 | ||
| 											{/*<FormLabel>Мова</FormLabel>*/}
 | ||
| 											<FormControl>
 | ||
| 												<Input type='hidden' placeholder='' {...field} />
 | ||
| 											</FormControl>
 | ||
| 											<FormMessage />
 | ||
| 										</FormItem>
 | ||
| 									)}
 | ||
| 								/>
 | ||
| 								<fieldset className='flex gap-4 rounded-lg border bg-gray-50 p-4'>
 | ||
| 									<div className='w-1/2'>
 | ||
| 										<FormField
 | ||
| 											control={form.control}
 | ||
| 											key={index + 1}
 | ||
| 											name={`locales.${index}.title`}
 | ||
| 											render={({field}) => (
 | ||
| 												<FormItem className={'w-full'}>
 | ||
| 													<FormLabel>Назва товару</FormLabel>
 | ||
| 													<FormControl>
 | ||
| 														<Input placeholder='' {...field} />
 | ||
| 													</FormControl>
 | ||
| 													<FormMessage />
 | ||
| 												</FormItem>
 | ||
| 											)}
 | ||
| 										/>
 | ||
| 									</div>
 | ||
| 									<div className='w-1/2'>
 | ||
| 										<FormField
 | ||
| 											control={form.control}
 | ||
| 											key={index + 2}
 | ||
| 											name={`locales.${index}.shortTitle`}
 | ||
| 											render={({field}) => (
 | ||
| 												<FormItem className={'w-full'}>
 | ||
| 													<FormLabel>Скорочена назва товару</FormLabel>
 | ||
| 													<FormControl>
 | ||
| 														<Input placeholder='' {...field} />
 | ||
| 													</FormControl>
 | ||
| 													<FormMessage />
 | ||
| 												</FormItem>
 | ||
| 											)}
 | ||
| 										/>
 | ||
| 									</div>
 | ||
| 								</fieldset>
 | ||
| 
 | ||
| 								<fieldset className='rounded-lg border bg-gray-50 p-4'>
 | ||
| 									<FormField
 | ||
| 										control={form.control}
 | ||
| 										key={index + 3}
 | ||
| 										name={`locales.${index}.headingTitle`}
 | ||
| 										render={({field}) => (
 | ||
| 											<FormItem className={'w-full'}>
 | ||
| 												<FormLabel>
 | ||
| 													Назва товару у описі та коротка анотація
 | ||
| 												</FormLabel>
 | ||
| 												<FormControl>
 | ||
| 													<Input placeholder='' {...field} />
 | ||
| 												</FormControl>
 | ||
| 												<FormMessage />
 | ||
| 											</FormItem>
 | ||
| 										)}
 | ||
| 									/>
 | ||
| 
 | ||
| 									<JoditEditor
 | ||
| 										key={index + 4}
 | ||
| 										ref={editor}
 | ||
| 										config={config}
 | ||
| 										value={index === 0 ? description0 : description1}
 | ||
| 										className='mt-4 w-full'
 | ||
| 										onBlur={value => {
 | ||
| 											index === 0
 | ||
| 												? setDescription0(value)
 | ||
| 												: setDescription1(value)
 | ||
| 											setValue(`locales.${index}.description`, value)
 | ||
| 										}}
 | ||
| 									/>
 | ||
| 								</fieldset>
 | ||
| 								<fieldset className='rounded-lg border bg-gray-50 p-4'>
 | ||
| 									<FormLabel>Опис товару</FormLabel>
 | ||
| 									<JoditEditor
 | ||
| 										key={index + 5}
 | ||
| 										ref={editor}
 | ||
| 										config={config}
 | ||
| 										value={index === 0 ? content0 : content1}
 | ||
| 										className='mt-4 w-full'
 | ||
| 										onBlur={value => {
 | ||
| 											index === 0 ? setContent0(value) : setContent1(value)
 | ||
| 											setValue(`locales.${index}.content`, value)
 | ||
| 										}}
 | ||
| 									/>
 | ||
| 								</fieldset>
 | ||
| 								<fieldset className='rounded-lg border bg-gray-50 p-4'>
 | ||
| 									<FormLabel>Інструкція до товару</FormLabel>
 | ||
| 									<JoditEditor
 | ||
| 										key={index + 2125}
 | ||
| 										ref={editor}
 | ||
| 										config={config}
 | ||
| 										value={index === 0 ? instruction0 : instruction1}
 | ||
| 										className='mt-4 w-full'
 | ||
| 										onBlur={value => {
 | ||
| 											index === 0
 | ||
| 												? setInstruction0(value)
 | ||
| 												: setInstruction1(value)
 | ||
| 											setValue(`locales.${index}.instruction`, value)
 | ||
| 										}}
 | ||
| 									/>
 | ||
| 								</fieldset>
 | ||
| 							</TabsContent>
 | ||
| 						))}
 | ||
| 						{metaFields.map((_, index) => (
 | ||
| 							<TabsContent
 | ||
| 								id={`form-tab-${form.getValues(`locales.${index}.lang`)}`}
 | ||
| 								value={form.getValues(`locales.${index}.lang`)}
 | ||
| 								key={index}
 | ||
| 								className='space-y-4'
 | ||
| 							>
 | ||
| 								<fieldset className='rounded-lg border bg-gray-50 p-4'>
 | ||
| 									<legend className='rounded-lg border bg-gray-200 px-16 py-1 text-xl font-bold'>
 | ||
| 										META ДАНІ
 | ||
| 									</legend>
 | ||
| 									<FormField
 | ||
| 										control={form.control}
 | ||
| 										key={index + 'meta.title'}
 | ||
| 										name={`meta.${index}.title`}
 | ||
| 										render={({field}) => (
 | ||
| 											<FormItem className={'w-full'}>
 | ||
| 												<FormLabel>Назва</FormLabel>
 | ||
| 												<FormControl>
 | ||
| 													<Input type='text' placeholder='' {...field} />
 | ||
| 												</FormControl>
 | ||
| 												<FormMessage />
 | ||
| 											</FormItem>
 | ||
| 										)}
 | ||
| 									/>
 | ||
| 									<FormField
 | ||
| 										control={form.control}
 | ||
| 										key={index + 'meta.description'}
 | ||
| 										name={`meta.${index}.description`}
 | ||
| 										render={({field}) => (
 | ||
| 											<FormItem className={'w-full'}>
 | ||
| 												<FormLabel>Опис</FormLabel>
 | ||
| 												<FormControl>
 | ||
| 													<Input type='text' placeholder='' {...field} />
 | ||
| 												</FormControl>
 | ||
| 												<FormMessage />
 | ||
| 											</FormItem>
 | ||
| 										)}
 | ||
| 									/>
 | ||
| 									<FormField
 | ||
| 										control={form.control}
 | ||
| 										key={index + 'meta.keywords'}
 | ||
| 										name={`meta.${index}.keywords`}
 | ||
| 										render={({field}) => (
 | ||
| 											<FormItem className={'w-full'}>
 | ||
| 												<FormLabel>Ключові слова</FormLabel>
 | ||
| 												<FormControl>
 | ||
| 													<Input type='text' placeholder='' {...field} />
 | ||
| 												</FormControl>
 | ||
| 												<FormMessage />
 | ||
| 											</FormItem>
 | ||
| 										)}
 | ||
| 									/>
 | ||
| 									<FormField
 | ||
| 										control={form.control}
 | ||
| 										key={index + 'meta.author'}
 | ||
| 										name={`meta.${index}.author`}
 | ||
| 										render={({field}) => (
 | ||
| 											<FormItem className={'w-full'}>
 | ||
| 												<FormLabel>Автор</FormLabel>
 | ||
| 												<FormControl>
 | ||
| 													<Input type='text' placeholder='' {...field} />
 | ||
| 												</FormControl>
 | ||
| 												<FormMessage />
 | ||
| 											</FormItem>
 | ||
| 										)}
 | ||
| 									/>
 | ||
| 								</fieldset>
 | ||
| 							</TabsContent>
 | ||
| 						))}
 | ||
| 					</Tabs>
 | ||
| 				</div>
 | ||
| 
 | ||
| 				<Button type='submit' className='!mt-0 w-full'>
 | ||
| 					Створити
 | ||
| 				</Button>
 | ||
| 			</form>
 | ||
| 		</Form>
 | ||
| 	)
 | ||
| }
 |