Files
bewell-in-ua/components/(protected)/admin/entity/crud-form.tsx
2025-03-11 02:54:09 +02:00

348 lines
9.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client'
import {zodResolver} from '@hookform/resolvers/zod'
import {EntityType} from '@prisma/client'
import dynamic from 'next/dynamic'
import React, {Suspense, useEffect, useMemo, useRef, useState} from 'react'
import {useFieldArray, useForm} from 'react-hook-form'
import toast from 'react-hot-toast'
import {z} from 'zod'
import {onEntityCreateEditAction} from '@/actions/admin/entity'
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue
} from '@/components/ui/select'
import {i18nDefaultLocale, i18nLocales} from '@/i18n-config'
import {BaseEditorConfig} from '@/lib/config/editor'
import {
EntityTypeDescription,
createEntityFormSchema
} from '@/lib/schemas/admin/entity'
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 = {
type: '',
media: '',
title: '',
annotation: '',
body: ''
}
let metaValues = {
title: '',
description: '',
keywords: '',
author: ''
}
export const EntityCrudForm = ({data}: {data?: any}) => {
const [loading, setLoading] = useState(false)
const [error, setError] = useState('')
const [success, setSuccess] = useState('')
const [annotation0, setAnnotation0] = useState(
data?.locales[0].annotation || ''
)
const [annotation1, setAnnotation1] = useState(
data?.locales[1].annotation || ''
)
const [body0, setBody0] = useState(data?.locales[0].body || '')
const [body1, setBody1] = useState(data?.locales[1].body || '')
const editor = useRef(null) //declared a null value
const config = useMemo(() => BaseEditorConfig, [])
config.maxWidth = '100%'
const form = useForm<z.infer<typeof createEntityFormSchema>>({
resolver: zodResolver(createEntityFormSchema),
mode: 'onBlur',
defaultValues: data
? (data => {
const {locales, meta} = data
return {
published: data.published,
image: data.image,
locales: toEmptyParams(locales) as any,
meta: meta
? (toEmptyParams(meta) as any)
: [{...metaValues}, {...metaValues}]
}
})(data)
: {
scopes: '',
published: false,
media: '',
slug: '',
locales: [
{lang: 'uk', ...localesValues},
{lang: 'ru', ...localesValues}
],
meta: [{...metaValues}, {...metaValues}]
}
})
const {register, setValue} = form
useEffect(() => {
register('locales.0.annotation')
register('locales.1.annotation')
register('locales.0.body')
register('locales.1.body')
}, [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 createEntityFormSchema>) => {
setLoading(true)
onEntityCreateEditAction(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='form-horizontal'
>
<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>
<div className='flex flex-row items-center justify-between gap-4 rounded-lg border bg-gray-50/25 px-4 pb-4'>
<div className='w-1/3'>
<FormField
control={form.control}
name='type'
render={({field}) => (
<FormItem>
<FormLabel>Тип сутності</FormLabel>
<Select
onValueChange={field.onChange}
defaultValue={field.value}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder='Потрібно обрати тип сутності' />
</SelectTrigger>
</FormControl>
<SelectContent>
{Object.keys(EntityType).map(
(entityType: string, index: number) => (
<SelectItem key={index} value={entityType}>
{EntityTypeDescription[entityType] || entityType}
</SelectItem>
)
)}
</SelectContent>
</Select>
<FormMessage className='ml-3' />
</FormItem>
)}
/>
</div>
<div className='w-1/3'>
<FormField
control={form.control}
name='slug'
render={({field}) => (
<FormItem>
<FormLabel>Аліас / Slug</FormLabel>
<FormControl>
<Input
type='text'
placeholder='вказіть аліас ресурсу'
{...field}
/>
</FormControl>
<FormMessage className='ml-3' />
</FormItem>
)}
/>
</div>
<div className='w-1/3'>
<FormField
control={form.control}
name='scopes'
render={({field}) => (
<FormItem>
<FormLabel>Область виведення</FormLabel>
<FormControl>
<Input
type='text'
placeholder='Веддіть дані у JSON форматі'
{...field}
/>
</FormControl>
<FormMessage className='ml-3' />
</FormItem>
)}
/>
</div>
</div>
</div>
<div className='flex flex-row items-center justify-between gap-4 rounded-lg border bg-gray-50 px-4 pb-4'>
<FormField
control={form.control}
name='media'
render={({field}) => (
<FormItem className='w-full'>
<FormLabel>
Медіа (файл на диску чи URL посилання на ресурс)
</FormLabel>
<FormControl>
<Input type='text' placeholder='' {...field} />
</FormControl>
<FormMessage className='ml-3' />
</FormItem>
)}
/>
</div>
<Tabs
defaultValue={i18nDefaultLocale}
className='mt-4 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'}>
<FormControl>
<Input type='text' placeholder='' {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className='w-full'>
<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-full'>
<FormLabel>Анотація / Коротка назва</FormLabel>
<JoditEditor
key={index + 4}
ref={editor}
config={config}
value={index === 0 ? annotation0 : annotation1}
className='mt-4 w-full'
onBlur={value => {
index === 0 ? setAnnotation0(value) : setAnnotation1(value)
setValue(`locales.${index}.annotation`, value)
}}
/>
</div>
<div className='w-full'>
<FormLabel>Текст</FormLabel>
<JoditEditor
key={index + 4}
ref={editor}
config={config}
value={index === 0 ? body0 : body1}
className='mt-4 w-full'
onBlur={value => {
index === 0 ? setBody0(value) : setBody1(value)
setValue(`locales.${index}.body`, value)
}}
/>
</div>
</TabsContent>
))}
</Tabs>
<Button type='submit' className='float-right my-4 w-[200px]'>
Створити
</Button>
</form>
</Form>
)
}