stuff done

This commit is contained in:
2025-03-11 02:54:09 +02:00
parent 58e7ed2f06
commit 516b45fad9
90 changed files with 2950 additions and 9458 deletions

View File

@@ -0,0 +1,347 @@
'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>
)
}

View File

@@ -3,6 +3,8 @@ import {
Home,
Inbox,
LayoutList,
List,
Newspaper,
Plus,
ScanBarcode,
Search,
@@ -44,6 +46,16 @@ const items = [
title: 'Товари',
url: `${ADMIN_DASHBOARD_PATH}/product`,
icon: ScanBarcode
},
{
title: 'Сутність',
url: `${ADMIN_DASHBOARD_PATH}/entity`,
icon: Newspaper
},
{
title: 'Замовлення',
url: `${ADMIN_DASHBOARD_PATH}/order`,
icon: List
}
// {
// title: 'Search',
@@ -70,7 +82,7 @@ export function AdminSidebar() {
<SidebarGroupContent>SidebarGroupAction</SidebarGroupContent>
</SidebarGroup>
<SidebarGroup>
<SidebarGroupLabel>Application</SidebarGroupLabel>
{/*<SidebarGroupLabel>Application</SidebarGroupLabel>*/}
<SidebarGroupContent>
<SidebarMenu>
{items.map(item => (
@@ -86,7 +98,7 @@ export function AdminSidebar() {
</SidebarMenu>
</SidebarGroupContent>
</SidebarGroup>
<Collapsible defaultOpen className='group/collapsible'>
<Collapsible defaultOpen className='group/collapsible' hidden={true}>
<SidebarGroup>
<SidebarGroupLabel asChild>
<CollapsibleTrigger>