| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- import { useForm, useFieldArray } from 'react-hook-form'
- import { z } from 'zod'
- import { zodResolver } from '@hookform/resolvers/zod'
- import { AlertTriangle } from 'lucide-react'
- import { toast } from 'sonner'
- import {
- Dialog,
- DialogContent,
- DialogHeader,
- DialogTitle,
- } from '@/components/ui/dialog'
- import {
- Form,
- FormControl,
- FormField,
- FormItem,
- FormLabel,
- FormMessage,
- } from '@/components/ui/form'
- import { Table, TableBody, TableCell, TableRow } from '@/components/ui/table'
- import { Input } from '@/components/ui/input'
- import { Textarea } from '@/components/ui/textarea'
- import { Button } from '@/components/ui/button'
- import VideoPlayer from '@/components/video-player'
- import { useEvaluations } from './evaluations-provider'
- import { useEvaluation } from '@/hooks/evaluations/useEvaluation'
- import { useUpdateEvaluationDetails } from '@/hooks/evaluations/useUpdateEvaluationDetails'
- import { type Evaluation } from '@/types/evaluations.types'
- const schema = z.object({
- pros_comment: z.string(),
- cons_comment: z.string(),
- suggestion: z.string(),
- summary: z.string(),
- score: z.number({ error: 'نمره ارزیابی الزامی است' }),
- items: z.array(
- z.object({
- item_id: z.number(),
- name: z.string(),
- score: z.number({ error: 'نمره آیتم الزامی است' }),
- })
- ),
- })
- type FormValues = z.infer<typeof schema>
- function getDefaultValues(evaluation: Evaluation): FormValues {
- return {
- pros_comment: evaluation.pros_comment ?? '',
- cons_comment: evaluation.cons_comment ?? '',
- suggestion: evaluation.suggestion ?? '',
- summary: evaluation.summary ?? '',
- score: evaluation.score ? Number(evaluation.score) : 0,
- items: evaluation.items.map((item) => ({
- item_id: item.item.id,
- name: item.item.name,
- score: item.score,
- })),
- }
- }
- export function EvaluationsEditDialog() {
- const { open, setOpen, currentRow } = useEvaluations()
- const opened = open === 'edit'
- const { data: evaluation, isLoading } = useEvaluation(
- opened ? (currentRow?.id as number) : ''
- )
- const mediaSrc = evaluation?.media?.[0]?.url
- const handleClose = () => setOpen(null)
- return (
- <Dialog open={opened} modal onOpenChange={handleClose}>
- <DialogContent className='max-h-[90vh] max-w-5xl overflow-y-auto'>
- <DialogHeader>
- <DialogTitle>ویرایش ارزیابی</DialogTitle>
- </DialogHeader>
- {isLoading || !evaluation ? (
- <div className='py-10 text-center'>در حال دریافت اطلاعات...</div>
- ) : (
- <div className='flex flex-col gap-4'>
- {mediaSrc && <VideoPlayer src={mediaSrc} />}
- {evaluation.items.length === 0 ? (
- <div className='flex items-center justify-center gap-3 rounded-xl border bg-muted px-2 py-4 text-center text-sm font-bold text-primary'>
- <AlertTriangle />
- <span>ارزیابی برای این کاربر یافت نشد.</span>
- </div>
- ) : (
- <EvaluationEditForm
- key={evaluation.id}
- evaluation={evaluation}
- onClose={handleClose}
- />
- )}
- </div>
- )}
- </DialogContent>
- </Dialog>
- )
- }
- type EvaluationEditFormProps = {
- evaluation: Evaluation
- onClose: () => void
- }
- function EvaluationEditForm({ evaluation, onClose }: EvaluationEditFormProps) {
- const { mutate, isPending } = useUpdateEvaluationDetails()
- const form = useForm<FormValues>({
- resolver: zodResolver(schema),
- defaultValues: getDefaultValues(evaluation),
- })
- const { fields } = useFieldArray({
- control: form.control,
- name: 'items',
- })
- const onSubmit = (values: FormValues) => {
- mutate(
- {
- id: evaluation.id,
- payload: {
- pros_comment: values.pros_comment,
- cons_comment: values.cons_comment,
- suggestion: values.suggestion,
- summary: values.summary,
- score: values.score,
- items: values.items.map((item) => ({
- item_id: item.item_id,
- score: item.score,
- })),
- },
- },
- {
- onSuccess: () => {
- toast.success('ویرایش ارزیابی با موفقیت انجام شد.')
- onClose()
- },
- onError: () => {
- toast.error('خطا در ویرایش ارزیابی!')
- },
- }
- )
- }
- return (
- <Form {...form}>
- <form
- onSubmit={form.handleSubmit(onSubmit)}
- className='flex flex-col gap-4'
- >
- <h2>نتیجه ارزیابی</h2>
- <div className='overflow-hidden rounded-2xl border border-slate-200'>
- <Table>
- <TableBody>
- {fields.map((fieldItem, index) => (
- <TableRow
- key={fieldItem.id}
- className='border-b border-slate-100 last:border-0 hover:bg-transparent'
- >
- <TableCell className='p-3 text-right text-sm leading-5'>
- {fieldItem.name}
- </TableCell>
- <TableCell className='p-3'>
- <FormField
- control={form.control}
- name={`items.${index}.score`}
- render={({ field }) => (
- <FormItem>
- <div className='flex items-center justify-end gap-1'>
- <span className='text-foreground'>100 /</span>
- <FormControl>
- <Input
- type='number'
- className='h-8 w-20 text-center text-base'
- value={field.value ?? ''}
- onChange={(e) => {
- const value = e.target.value
- field.onChange(
- value === '' ? 0 : Number(value)
- )
- }}
- />
- </FormControl>
- </div>
- <FormMessage />
- </FormItem>
- )}
- />
- </TableCell>
- </TableRow>
- ))}
- <TableRow className='border-b border-slate-100 last:border-0 hover:bg-transparent'>
- <TableCell className='p-3 text-right text-sm leading-5 font-semibold'>
- مجموع عملکرد
- </TableCell>
- <TableCell className='p-3'>
- <FormField
- control={form.control}
- name='score'
- render={({ field }) => (
- <FormItem>
- <div className='flex items-center justify-end gap-1'>
- <span className='text-base font-semibold'>100 /</span>
- <FormControl>
- <Input
- type='number'
- className='h-8 w-20 text-center text-base font-semibold'
- value={field.value ?? ''}
- onChange={(e) => {
- const value = e.target.value
- field.onChange(
- value === '' ? 0 : Number(value)
- )
- }}
- />
- </FormControl>
- </div>
- <FormMessage />
- </FormItem>
- )}
- />
- </TableCell>
- </TableRow>
- </TableBody>
- </Table>
- </div>
- <FormField
- control={form.control}
- name='pros_comment'
- render={({ field }) => (
- <FormItem className='flex flex-col gap-2'>
- <FormLabel className='leading-6'>نقاط قوت:</FormLabel>
- <FormControl>
- <Textarea {...field} rows={4} />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
- <FormField
- control={form.control}
- name='cons_comment'
- render={({ field }) => (
- <FormItem className='flex flex-col gap-2'>
- <FormLabel className='leading-6'>نقاط ضعف:</FormLabel>
- <FormControl>
- <Textarea {...field} rows={4} />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
- <FormField
- control={form.control}
- name='suggestion'
- render={({ field }) => (
- <FormItem className='flex flex-col gap-2'>
- <FormLabel className='leading-6'>تمرین پیشنهادی:</FormLabel>
- <FormControl>
- <Textarea {...field} rows={4} />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
- <FormField
- control={form.control}
- name='summary'
- render={({ field }) => (
- <FormItem className='flex flex-col gap-2'>
- <FormLabel className='leading-6'>نتیجه:</FormLabel>
- <FormControl>
- <Textarea {...field} rows={4} />
- </FormControl>
- <FormMessage />
- </FormItem>
- )}
- />
- <div className='flex justify-end gap-3'>
- <Button type='button' variant='outline' onClick={onClose}>
- انصراف
- </Button>
- <Button type='submit' disabled={isPending}>
- {isPending ? 'در حال ارسال...' : 'ذخیره تغییرات'}
- </Button>
- </div>
- </form>
- </Form>
- )
- }
|