added tons of features

This commit is contained in:
2025-02-05 08:01:14 +02:00
parent 4ae0d8c545
commit 8138da6b1d
195 changed files with 12619 additions and 415 deletions

View File

@@ -0,0 +1,442 @@
'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 {z} from 'zod'
import {onProductCreateAction} from '@/actions/admin/product'
import {useToast} from '@/hooks/use-toast'
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 {toast} = useToast()
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({
variant: 'destructive',
title: res?.error,
description: res?.message
})
} else {
setSuccess(res?.success as string)
setError('')
setLoading(false)
toast({
variant: 'success',
title: res?.success,
description: res?.message
})
}
})
}
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>
)
}

View File

@@ -0,0 +1,169 @@
'use client'
import {zodResolver} from '@hookform/resolvers/zod'
import fs from 'node:fs'
import path from 'node:path'
import {useCallback, useState} from 'react'
import Dropzone from 'react-dropzone'
import {useFieldArray, useForm} from 'react-hook-form'
import {z} from 'zod'
import {onProductCreateAction} from '@/actions/admin/product'
import {createCategoryFormSchema} from '@/lib/schemas/admin/category'
import {createProductFormSchema} from '@/lib/schemas/admin/product'
import {cn, dump} from '@/lib/utils'
import {ResourceMessages} from '@/types'
import {Button} from '@/ui/button'
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage
} from '@/ui/form'
import {Input} from '@/ui/input'
export default function CreateForm() {
const [loading, setLoading] = useState(false)
const [error, setError] = useState('')
const [success, setSuccess] = useState('')
const form = useForm<z.infer<typeof createProductFormSchema>>({
resolver: zodResolver(createProductFormSchema),
mode: 'onBlur',
defaultValues: {
files: []
}
})
const {fields, append} = useFieldArray({
name: 'files',
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)
} else {
setSuccess(res?.success as string)
setError('')
setLoading(false)
}
})
}
return (
<div className='flex h-screen items-center justify-center'>
<Form {...form}>
<form
action=''
onSubmit={form.handleSubmit(onSubmit)}
className='max-w-md flex-1 space-y-5'
>
<div className='products_name_price_desc relative'>
{fields.map((_, index) => {
return (
<div key={index}>
<div className='mb-2 mt-7 text-xl font-bold'>
{/*{form.getValues(`files.${index}.file.name`)}*/}
{dump(form.getValues(`files.${index}`))}
</div>
<div className='flex gap-x-3'>
<FormField
control={form.control}
key={index}
name={`files.${index}.alt`}
render={({field}) => (
<FormItem>
<FormLabel>Файл Альт Ім'я</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage className='capitalize text-red-500' />
</FormItem>
)}
/>
<FormField
control={form.control}
key={index + 1}
name={`files.${index}.title`}
render={({field}) => (
<FormItem>
<FormLabel>Назва файлу</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage className='capitalize text-red-500' />
</FormItem>
)}
/>
</div>
</div>
)
})}
</div>
<div className='products relative'>
<FormField
control={form.control}
name='files'
render={() => (
<Dropzone
accept={{
'image/*': ['.jpg', '.jpeg', '.png']
}}
onDropAccepted={acceptedFiles => {
acceptedFiles.map(acceptedFile => {
console.log('acceptedFile', acceptedFile)
return append({
file: acceptedFile,
alt: '',
title: ''
})
})
}}
multiple={true}
maxSize={5000000}
>
{({getRootProps, getInputProps}) => (
<section>
<div {...getRootProps()}>
<input {...getInputProps()} />
<p>
Перетягніть тут, киньте кілька файлів або натисніть,
щоб вибрати файли
</p>
</div>
</section>
)}
</Dropzone>
)}
/>
</div>
<Button type='submit' className='!mt-0 w-full'>
Створити
</Button>
</form>
</Form>
</div>
)
// return (
// <div className='flex flex-col bg-zinc-200 py-10'>
// <h1 className='text-center text-3xl font-bold capitalize'>
// Створити галерею
// </h1>
// <div className='mx-auto mb-10 mt-6 flex min-h-[320px] w-[80%] flex-wrap gap-1 rounded-md bg-white p-5 shadow-sm'></div>
// <div className='flex justify-center'>
// <Button>Завантажити зображення</Button>
// </div>
// </div>
// )
}