|
@@ -1,18 +1,12 @@
|
|
|
-'use client'
|
|
|
|
|
-
|
|
|
|
|
-import { z } from 'zod'
|
|
|
|
|
import { useForm } from 'react-hook-form'
|
|
import { useForm } from 'react-hook-form'
|
|
|
-import { zodResolver } from '@hookform/resolvers/zod'
|
|
|
|
|
-import { showSubmittedData } from '@/lib/show-submitted-data'
|
|
|
|
|
-import { Button } from '@/components/ui/button'
|
|
|
|
|
|
|
+
|
|
|
import {
|
|
import {
|
|
|
Dialog,
|
|
Dialog,
|
|
|
DialogContent,
|
|
DialogContent,
|
|
|
- DialogDescription,
|
|
|
|
|
- DialogFooter,
|
|
|
|
|
DialogHeader,
|
|
DialogHeader,
|
|
|
DialogTitle,
|
|
DialogTitle,
|
|
|
} from '@/components/ui/dialog'
|
|
} from '@/components/ui/dialog'
|
|
|
|
|
+
|
|
|
import {
|
|
import {
|
|
|
Form,
|
|
Form,
|
|
|
FormControl,
|
|
FormControl,
|
|
@@ -21,305 +15,601 @@ import {
|
|
|
FormLabel,
|
|
FormLabel,
|
|
|
FormMessage,
|
|
FormMessage,
|
|
|
} from '@/components/ui/form'
|
|
} from '@/components/ui/form'
|
|
|
|
|
+
|
|
|
import { Input } from '@/components/ui/input'
|
|
import { Input } from '@/components/ui/input'
|
|
|
-import { PasswordInput } from '@/components/password-input'
|
|
|
|
|
-import { SelectDropdown } from '@/components/select-dropdown'
|
|
|
|
|
-import { roles } from '../data/data'
|
|
|
|
|
-import { type User } from '../data/schema'
|
|
|
|
|
-
|
|
|
|
|
-const formSchema = z
|
|
|
|
|
- .object({
|
|
|
|
|
- firstName: z.string().min(1, 'First Name is required.'),
|
|
|
|
|
- lastName: z.string().min(1, 'Last Name is required.'),
|
|
|
|
|
- username: z.string().min(1, 'Username is required.'),
|
|
|
|
|
- phoneNumber: z.string().min(1, 'Phone number is required.'),
|
|
|
|
|
- email: z.email({
|
|
|
|
|
- error: (iss) => (iss.input === '' ? 'Email is required.' : undefined),
|
|
|
|
|
- }),
|
|
|
|
|
- password: z.string().transform((pwd) => pwd.trim()),
|
|
|
|
|
- role: z.string().min(1, 'Role is required.'),
|
|
|
|
|
- confirmPassword: z.string().transform((pwd) => pwd.trim()),
|
|
|
|
|
- isEdit: z.boolean(),
|
|
|
|
|
- })
|
|
|
|
|
- .refine(
|
|
|
|
|
- (data) => {
|
|
|
|
|
- if (data.isEdit && !data.password) return true
|
|
|
|
|
- return data.password.length > 0
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- message: 'Password is required.',
|
|
|
|
|
- path: ['password'],
|
|
|
|
|
- }
|
|
|
|
|
- )
|
|
|
|
|
- .refine(
|
|
|
|
|
- ({ isEdit, password }) => {
|
|
|
|
|
- if (isEdit && !password) return true
|
|
|
|
|
- return password.length >= 8
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- message: 'Password must be at least 8 characters long.',
|
|
|
|
|
- path: ['password'],
|
|
|
|
|
- }
|
|
|
|
|
- )
|
|
|
|
|
- .refine(
|
|
|
|
|
- ({ isEdit, password }) => {
|
|
|
|
|
- if (isEdit && !password) return true
|
|
|
|
|
- return /[a-z]/.test(password)
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- message: 'Password must contain at least one lowercase letter.',
|
|
|
|
|
- path: ['password'],
|
|
|
|
|
- }
|
|
|
|
|
- )
|
|
|
|
|
- .refine(
|
|
|
|
|
- ({ isEdit, password }) => {
|
|
|
|
|
- if (isEdit && !password) return true
|
|
|
|
|
- return /\d/.test(password)
|
|
|
|
|
- },
|
|
|
|
|
- {
|
|
|
|
|
- message: 'Password must contain at least one number.',
|
|
|
|
|
- path: ['password'],
|
|
|
|
|
- }
|
|
|
|
|
- )
|
|
|
|
|
- .refine(
|
|
|
|
|
- ({ isEdit, password, confirmPassword }) => {
|
|
|
|
|
- if (isEdit && !password) return true
|
|
|
|
|
- return password === confirmPassword
|
|
|
|
|
|
|
+import { Textarea } from '@/components/ui/textarea'
|
|
|
|
|
+import {
|
|
|
|
|
+ Select,
|
|
|
|
|
+ SelectContent,
|
|
|
|
|
+ SelectItem,
|
|
|
|
|
+ SelectTrigger,
|
|
|
|
|
+ SelectValue,
|
|
|
|
|
+} from '@/components/ui/select'
|
|
|
|
|
+
|
|
|
|
|
+import { Button } from '@/components/ui/button'
|
|
|
|
|
+
|
|
|
|
|
+import { useCreateUser } from '@/hooks/users/useCreateUser'
|
|
|
|
|
+import { useUpdateUser } from '@/hooks/users/useUpdateUser'
|
|
|
|
|
+import { useUserDetail } from '@/hooks/users/useUserDetail'
|
|
|
|
|
+import { useProvinces } from '@/hooks/location/useProvinces'
|
|
|
|
|
+import { useCities } from '@/hooks/location/useCities'
|
|
|
|
|
+
|
|
|
|
|
+import {
|
|
|
|
|
+ genderOption,
|
|
|
|
|
+ footOptions,
|
|
|
|
|
+ positionOptions,
|
|
|
|
|
+ skillLevelOptions,
|
|
|
|
|
+ activityHistoryOptions,
|
|
|
|
|
+} from '@/constants/users/users-constants'
|
|
|
|
|
+
|
|
|
|
|
+import { useUsers } from './users-provider'
|
|
|
|
|
+import type { CreateUserPayload } from '@/types/users.types'
|
|
|
|
|
+import { useEffect } from 'react'
|
|
|
|
|
+
|
|
|
|
|
+export function UsersActionDialog() {
|
|
|
|
|
+ const { open, setOpen, currentRow } = useUsers()
|
|
|
|
|
+ const isAdd = open === 'add'
|
|
|
|
|
+ const isEdit = open === 'edit'
|
|
|
|
|
+ const isView = open === 'view'
|
|
|
|
|
+ const opened = isAdd || isEdit || isView
|
|
|
|
|
+ const readOnly = isView
|
|
|
|
|
+
|
|
|
|
|
+ const { data: user, isLoading } = useUserDetail(currentRow?.id as number)
|
|
|
|
|
+
|
|
|
|
|
+ const form = useForm<CreateUserPayload>({
|
|
|
|
|
+ defaultValues: {
|
|
|
|
|
+ username: '',
|
|
|
|
|
+ phone: '',
|
|
|
|
|
+ first_name: '',
|
|
|
|
|
+ last_name: '',
|
|
|
|
|
+ email: '',
|
|
|
|
|
+ province_id: undefined,
|
|
|
|
|
+ city_id: undefined,
|
|
|
|
|
+ gender: undefined,
|
|
|
|
|
+ birth_date: '',
|
|
|
|
|
+ height: undefined,
|
|
|
|
|
+ weight: undefined,
|
|
|
|
|
+ foot_specialization: '',
|
|
|
|
|
+ post_skill: '',
|
|
|
|
|
+ skill_level: '',
|
|
|
|
|
+ activity_history: false,
|
|
|
|
|
+ team_name: '',
|
|
|
|
|
+ favorite_iranian_team: '',
|
|
|
|
|
+ favorite_foreign_team: '',
|
|
|
|
|
+ shirt_number: undefined,
|
|
|
|
|
+ bio: '',
|
|
|
},
|
|
},
|
|
|
- {
|
|
|
|
|
- message: "Passwords don't match.",
|
|
|
|
|
- path: ['confirmPassword'],
|
|
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ if (user && (isEdit || isView)) {
|
|
|
|
|
+ form.reset({
|
|
|
|
|
+ username: user.username ?? '',
|
|
|
|
|
+ phone: user.phone ?? '',
|
|
|
|
|
+ first_name: user.first_name ?? '',
|
|
|
|
|
+ last_name: user.last_name ?? '',
|
|
|
|
|
+ email: user.email ?? '',
|
|
|
|
|
+ province_id: user.province_id ?? undefined,
|
|
|
|
|
+ city_id: user.city_id ?? undefined,
|
|
|
|
|
+ gender: user.gender ?? undefined,
|
|
|
|
|
+ birth_date: user.birth_date ?? '',
|
|
|
|
|
+ height: user.height ?? undefined,
|
|
|
|
|
+ weight: user.weight ?? undefined,
|
|
|
|
|
+ foot_specialization: user.foot_specialization ?? '',
|
|
|
|
|
+ post_skill: user.post_skill ?? '',
|
|
|
|
|
+ skill_level: user.skill_level ?? '',
|
|
|
|
|
+ activity_history: Boolean(user.activity_history),
|
|
|
|
|
+ team_name: user.team_name ?? '',
|
|
|
|
|
+ favorite_iranian_team: user.favorite_iranian_team ?? '',
|
|
|
|
|
+ favorite_foreign_team: user.favorite_foreign_team ?? '',
|
|
|
|
|
+ shirt_number: user.shirt_number ?? undefined,
|
|
|
|
|
+ bio: user.bio ?? '',
|
|
|
|
|
+ })
|
|
|
|
|
+ } else if (isAdd) {
|
|
|
|
|
+ form.reset({
|
|
|
|
|
+ username: '',
|
|
|
|
|
+ phone: '',
|
|
|
|
|
+ first_name: '',
|
|
|
|
|
+ last_name: '',
|
|
|
|
|
+ email: '',
|
|
|
|
|
+ province_id: undefined,
|
|
|
|
|
+ city_id: undefined,
|
|
|
|
|
+ gender: undefined,
|
|
|
|
|
+ birth_date: '',
|
|
|
|
|
+ height: undefined,
|
|
|
|
|
+ weight: undefined,
|
|
|
|
|
+ foot_specialization: '',
|
|
|
|
|
+ post_skill: '',
|
|
|
|
|
+ skill_level: '',
|
|
|
|
|
+ activity_history: false,
|
|
|
|
|
+ team_name: '',
|
|
|
|
|
+ favorite_iranian_team: '',
|
|
|
|
|
+ favorite_foreign_team: '',
|
|
|
|
|
+ shirt_number: undefined,
|
|
|
|
|
+ bio: '',
|
|
|
|
|
+ })
|
|
|
}
|
|
}
|
|
|
- )
|
|
|
|
|
-type UserForm = z.infer<typeof formSchema>
|
|
|
|
|
|
|
+ }, [user, isEdit, isView, isAdd, form])
|
|
|
|
|
|
|
|
-type UserActionDialogProps = {
|
|
|
|
|
- currentRow?: User
|
|
|
|
|
- open: boolean
|
|
|
|
|
- onOpenChange: (open: boolean) => void
|
|
|
|
|
-}
|
|
|
|
|
|
|
+ const provinceId = form.watch('province_id')
|
|
|
|
|
|
|
|
-export function UsersActionDialog({
|
|
|
|
|
- currentRow,
|
|
|
|
|
- open,
|
|
|
|
|
- onOpenChange,
|
|
|
|
|
-}: UserActionDialogProps) {
|
|
|
|
|
- const isEdit = !!currentRow
|
|
|
|
|
- const form = useForm<UserForm>({
|
|
|
|
|
- resolver: zodResolver(formSchema),
|
|
|
|
|
- defaultValues: isEdit
|
|
|
|
|
- ? {
|
|
|
|
|
- ...currentRow,
|
|
|
|
|
- password: '',
|
|
|
|
|
- confirmPassword: '',
|
|
|
|
|
- isEdit,
|
|
|
|
|
- }
|
|
|
|
|
- : {
|
|
|
|
|
- firstName: '',
|
|
|
|
|
- lastName: '',
|
|
|
|
|
- username: '',
|
|
|
|
|
- email: '',
|
|
|
|
|
- role: '',
|
|
|
|
|
- phoneNumber: '',
|
|
|
|
|
- password: '',
|
|
|
|
|
- confirmPassword: '',
|
|
|
|
|
- isEdit,
|
|
|
|
|
- },
|
|
|
|
|
- })
|
|
|
|
|
|
|
+ const { data: provinces = [] } = useProvinces()
|
|
|
|
|
+ const { data: cities = [] } = useCities(provinceId as number)
|
|
|
|
|
|
|
|
- const onSubmit = (values: UserForm) => {
|
|
|
|
|
- form.reset()
|
|
|
|
|
- showSubmittedData(values)
|
|
|
|
|
- onOpenChange(false)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ const createMutation = useCreateUser()
|
|
|
|
|
+ const updateMutation = useUpdateUser()
|
|
|
|
|
+
|
|
|
|
|
+ const onSubmit = (values: CreateUserPayload) => {
|
|
|
|
|
+ if (isView) return
|
|
|
|
|
+
|
|
|
|
|
+ if (isAdd) {
|
|
|
|
|
+ createMutation.mutate(values, {
|
|
|
|
|
+ onSuccess: () => setOpen(null),
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- const isPasswordTouched = !!form.formState.dirtyFields.password
|
|
|
|
|
|
|
+ if (isEdit && currentRow) {
|
|
|
|
|
+ updateMutation.mutate(
|
|
|
|
|
+ { id: currentRow.id, payload: values },
|
|
|
|
|
+ {
|
|
|
|
|
+ onSuccess: () => setOpen(null),
|
|
|
|
|
+ }
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
return (
|
|
return (
|
|
|
- <Dialog
|
|
|
|
|
- open={open}
|
|
|
|
|
- onOpenChange={(state) => {
|
|
|
|
|
- form.reset()
|
|
|
|
|
- onOpenChange(state)
|
|
|
|
|
- }}
|
|
|
|
|
- >
|
|
|
|
|
- <DialogContent className='sm:max-w-lg'>
|
|
|
|
|
- <DialogHeader className='text-start'>
|
|
|
|
|
- <DialogTitle>{isEdit ? 'Edit User' : 'Add New User'}</DialogTitle>
|
|
|
|
|
- <DialogDescription>
|
|
|
|
|
- {isEdit ? 'Update the user here. ' : 'Create new user here. '}
|
|
|
|
|
- Click save when you're done.
|
|
|
|
|
- </DialogDescription>
|
|
|
|
|
|
|
+ <Dialog open={opened} modal={true} onOpenChange={() => setOpen(null)}>
|
|
|
|
|
+ <DialogContent className='max-h-[90vh] max-w-5xl overflow-y-auto'>
|
|
|
|
|
+ <DialogHeader>
|
|
|
|
|
+ <DialogTitle>
|
|
|
|
|
+ {isAdd && 'افزودن کاربر'}
|
|
|
|
|
+ {isEdit && 'ویرایش کاربر'}
|
|
|
|
|
+ {isView && 'مشاهده کاربر'}
|
|
|
|
|
+ </DialogTitle>
|
|
|
</DialogHeader>
|
|
</DialogHeader>
|
|
|
- <div className='h-105 w-[calc(100%+0.75rem)] overflow-y-auto py-1 pe-3'>
|
|
|
|
|
|
|
+
|
|
|
|
|
+ {isLoading ? (
|
|
|
|
|
+ <div className='py-10 text-center'>در حال دریافت اطلاعات...</div>
|
|
|
|
|
+ ) : (
|
|
|
<Form {...form}>
|
|
<Form {...form}>
|
|
|
- <form
|
|
|
|
|
- id='user-form'
|
|
|
|
|
- onSubmit={form.handleSubmit(onSubmit)}
|
|
|
|
|
- className='space-y-4 px-0.5'
|
|
|
|
|
- >
|
|
|
|
|
- <FormField
|
|
|
|
|
- control={form.control}
|
|
|
|
|
- name='firstName'
|
|
|
|
|
- render={({ field }) => (
|
|
|
|
|
- <FormItem className='grid grid-cols-6 items-center space-y-0 gap-x-4 gap-y-1'>
|
|
|
|
|
- <FormLabel className='col-span-2 text-end'>
|
|
|
|
|
- First Name
|
|
|
|
|
- </FormLabel>
|
|
|
|
|
- <FormControl>
|
|
|
|
|
- <Input
|
|
|
|
|
- placeholder='John'
|
|
|
|
|
- className='col-span-4'
|
|
|
|
|
- autoComplete='off'
|
|
|
|
|
- {...field}
|
|
|
|
|
- />
|
|
|
|
|
- </FormControl>
|
|
|
|
|
- <FormMessage className='col-span-4 col-start-3' />
|
|
|
|
|
- </FormItem>
|
|
|
|
|
- )}
|
|
|
|
|
- />
|
|
|
|
|
- <FormField
|
|
|
|
|
- control={form.control}
|
|
|
|
|
- name='lastName'
|
|
|
|
|
- render={({ field }) => (
|
|
|
|
|
- <FormItem className='grid grid-cols-6 items-center space-y-0 gap-x-4 gap-y-1'>
|
|
|
|
|
- <FormLabel className='col-span-2 text-end'>
|
|
|
|
|
- Last Name
|
|
|
|
|
- </FormLabel>
|
|
|
|
|
- <FormControl>
|
|
|
|
|
- <Input
|
|
|
|
|
- placeholder='Doe'
|
|
|
|
|
- className='col-span-4'
|
|
|
|
|
- autoComplete='off'
|
|
|
|
|
- {...field}
|
|
|
|
|
- />
|
|
|
|
|
- </FormControl>
|
|
|
|
|
- <FormMessage className='col-span-4 col-start-3' />
|
|
|
|
|
- </FormItem>
|
|
|
|
|
- )}
|
|
|
|
|
- />
|
|
|
|
|
- <FormField
|
|
|
|
|
- control={form.control}
|
|
|
|
|
- name='username'
|
|
|
|
|
- render={({ field }) => (
|
|
|
|
|
- <FormItem className='grid grid-cols-6 items-center space-y-0 gap-x-4 gap-y-1'>
|
|
|
|
|
- <FormLabel className='col-span-2 text-end'>
|
|
|
|
|
- Username
|
|
|
|
|
- </FormLabel>
|
|
|
|
|
- <FormControl>
|
|
|
|
|
- <Input
|
|
|
|
|
- placeholder='john_doe'
|
|
|
|
|
- className='col-span-4'
|
|
|
|
|
- {...field}
|
|
|
|
|
- />
|
|
|
|
|
- </FormControl>
|
|
|
|
|
- <FormMessage className='col-span-4 col-start-3' />
|
|
|
|
|
- </FormItem>
|
|
|
|
|
- )}
|
|
|
|
|
- />
|
|
|
|
|
- <FormField
|
|
|
|
|
- control={form.control}
|
|
|
|
|
- name='email'
|
|
|
|
|
- render={({ field }) => (
|
|
|
|
|
- <FormItem className='grid grid-cols-6 items-center space-y-0 gap-x-4 gap-y-1'>
|
|
|
|
|
- <FormLabel className='col-span-2 text-end'>Email</FormLabel>
|
|
|
|
|
- <FormControl>
|
|
|
|
|
- <Input
|
|
|
|
|
- placeholder='john.doe@gmail.com'
|
|
|
|
|
- className='col-span-4'
|
|
|
|
|
- {...field}
|
|
|
|
|
- />
|
|
|
|
|
- </FormControl>
|
|
|
|
|
- <FormMessage className='col-span-4 col-start-3' />
|
|
|
|
|
- </FormItem>
|
|
|
|
|
- )}
|
|
|
|
|
- />
|
|
|
|
|
- <FormField
|
|
|
|
|
- control={form.control}
|
|
|
|
|
- name='phoneNumber'
|
|
|
|
|
- render={({ field }) => (
|
|
|
|
|
- <FormItem className='grid grid-cols-6 items-center space-y-0 gap-x-4 gap-y-1'>
|
|
|
|
|
- <FormLabel className='col-span-2 text-end'>
|
|
|
|
|
- Phone Number
|
|
|
|
|
- </FormLabel>
|
|
|
|
|
- <FormControl>
|
|
|
|
|
- <Input
|
|
|
|
|
- placeholder='+123456789'
|
|
|
|
|
- className='col-span-4'
|
|
|
|
|
- {...field}
|
|
|
|
|
- />
|
|
|
|
|
- </FormControl>
|
|
|
|
|
- <FormMessage className='col-span-4 col-start-3' />
|
|
|
|
|
- </FormItem>
|
|
|
|
|
- )}
|
|
|
|
|
- />
|
|
|
|
|
- <FormField
|
|
|
|
|
- control={form.control}
|
|
|
|
|
- name='role'
|
|
|
|
|
- render={({ field }) => (
|
|
|
|
|
- <FormItem className='grid grid-cols-6 items-center space-y-0 gap-x-4 gap-y-1'>
|
|
|
|
|
- <FormLabel className='col-span-2 text-end'>Role</FormLabel>
|
|
|
|
|
- <SelectDropdown
|
|
|
|
|
- defaultValue={field.value}
|
|
|
|
|
- onValueChange={field.onChange}
|
|
|
|
|
- placeholder='Select a role'
|
|
|
|
|
- className='col-span-4'
|
|
|
|
|
- items={roles.map(({ label, value }) => ({
|
|
|
|
|
- label,
|
|
|
|
|
- value,
|
|
|
|
|
- }))}
|
|
|
|
|
- />
|
|
|
|
|
- <FormMessage className='col-span-4 col-start-3' />
|
|
|
|
|
- </FormItem>
|
|
|
|
|
- )}
|
|
|
|
|
- />
|
|
|
|
|
- <FormField
|
|
|
|
|
- control={form.control}
|
|
|
|
|
- name='password'
|
|
|
|
|
- render={({ field }) => (
|
|
|
|
|
- <FormItem className='grid grid-cols-6 items-center space-y-0 gap-x-4 gap-y-1'>
|
|
|
|
|
- <FormLabel className='col-span-2 text-end'>
|
|
|
|
|
- Password
|
|
|
|
|
- </FormLabel>
|
|
|
|
|
- <FormControl>
|
|
|
|
|
- <PasswordInput
|
|
|
|
|
- placeholder='e.g., S3cur3P@ssw0rd'
|
|
|
|
|
- className='col-span-4'
|
|
|
|
|
- {...field}
|
|
|
|
|
- />
|
|
|
|
|
- </FormControl>
|
|
|
|
|
- <FormMessage className='col-span-4 col-start-3' />
|
|
|
|
|
- </FormItem>
|
|
|
|
|
- )}
|
|
|
|
|
- />
|
|
|
|
|
|
|
+ <form onSubmit={form.handleSubmit(onSubmit)} className='space-y-6'>
|
|
|
|
|
+ <div className='grid grid-cols-1 gap-4 md:grid-cols-2'>
|
|
|
|
|
+ <FormField
|
|
|
|
|
+ control={form.control}
|
|
|
|
|
+ name='first_name'
|
|
|
|
|
+ rules={{ required: 'نام الزامی است' }}
|
|
|
|
|
+ render={({ field }) => (
|
|
|
|
|
+ <FormItem>
|
|
|
|
|
+ <FormLabel>نام</FormLabel>
|
|
|
|
|
+ <FormControl>
|
|
|
|
|
+ <Input {...field} disabled={readOnly} />
|
|
|
|
|
+ </FormControl>
|
|
|
|
|
+ <FormMessage />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ )}
|
|
|
|
|
+ />
|
|
|
|
|
+
|
|
|
|
|
+ <FormField
|
|
|
|
|
+ control={form.control}
|
|
|
|
|
+ name='last_name'
|
|
|
|
|
+ rules={{ required: 'نام خانوادگی الزامی است' }}
|
|
|
|
|
+ render={({ field }) => (
|
|
|
|
|
+ <FormItem>
|
|
|
|
|
+ <FormLabel>نام خانوادگی</FormLabel>
|
|
|
|
|
+ <FormControl>
|
|
|
|
|
+ <Input {...field} disabled={readOnly} />
|
|
|
|
|
+ </FormControl>
|
|
|
|
|
+ <FormMessage />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ )}
|
|
|
|
|
+ />
|
|
|
|
|
+
|
|
|
|
|
+ <FormField
|
|
|
|
|
+ control={form.control}
|
|
|
|
|
+ name='username'
|
|
|
|
|
+ rules={{ required: 'نام کاربری الزامی است' }}
|
|
|
|
|
+ render={({ field }) => (
|
|
|
|
|
+ <FormItem>
|
|
|
|
|
+ <FormLabel>نام کاربری</FormLabel>
|
|
|
|
|
+ <FormControl>
|
|
|
|
|
+ <Input {...field} disabled={readOnly} />
|
|
|
|
|
+ </FormControl>
|
|
|
|
|
+ <FormMessage />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ )}
|
|
|
|
|
+ />
|
|
|
|
|
+
|
|
|
|
|
+ <FormField
|
|
|
|
|
+ control={form.control}
|
|
|
|
|
+ name='phone'
|
|
|
|
|
+ rules={{
|
|
|
|
|
+ required: 'شماره موبایل الزامی است',
|
|
|
|
|
+ minLength: {
|
|
|
|
|
+ value: 10,
|
|
|
|
|
+ message: 'شماره موبایل نامعتبر است',
|
|
|
|
|
+ },
|
|
|
|
|
+ }}
|
|
|
|
|
+ render={({ field }) => (
|
|
|
|
|
+ <FormItem>
|
|
|
|
|
+ <FormLabel>موبایل</FormLabel>
|
|
|
|
|
+ <FormControl>
|
|
|
|
|
+ <Input {...field} disabled={readOnly} />
|
|
|
|
|
+ </FormControl>
|
|
|
|
|
+ <FormMessage />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ )}
|
|
|
|
|
+ />
|
|
|
|
|
+
|
|
|
|
|
+ <FormField
|
|
|
|
|
+ control={form.control}
|
|
|
|
|
+ name='email'
|
|
|
|
|
+ render={({ field }) => (
|
|
|
|
|
+ <FormItem>
|
|
|
|
|
+ <FormLabel>ایمیل</FormLabel>
|
|
|
|
|
+ <FormControl>
|
|
|
|
|
+ <Input
|
|
|
|
|
+ {...field}
|
|
|
|
|
+ disabled={readOnly}
|
|
|
|
|
+ value={field.value ?? ''}
|
|
|
|
|
+ />
|
|
|
|
|
+ </FormControl>
|
|
|
|
|
+ <FormMessage />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ )}
|
|
|
|
|
+ />
|
|
|
|
|
+
|
|
|
|
|
+ <FormField
|
|
|
|
|
+ control={form.control}
|
|
|
|
|
+ name='birth_date'
|
|
|
|
|
+ rules={{ required: 'تاریخ تولد الزامی است' }}
|
|
|
|
|
+ render={({ field }) => (
|
|
|
|
|
+ <FormItem>
|
|
|
|
|
+ <FormLabel>تاریخ تولد</FormLabel>
|
|
|
|
|
+ <FormControl>
|
|
|
|
|
+ <Input type='date' {...field} disabled={readOnly} />
|
|
|
|
|
+ </FormControl>
|
|
|
|
|
+ <FormMessage />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ )}
|
|
|
|
|
+ />
|
|
|
|
|
+
|
|
|
|
|
+ <FormField
|
|
|
|
|
+ control={form.control}
|
|
|
|
|
+ name='gender'
|
|
|
|
|
+ rules={{ required: 'جنسیت الزامی است' }}
|
|
|
|
|
+ render={({ field }) => (
|
|
|
|
|
+ <FormItem>
|
|
|
|
|
+ <FormLabel>جنسیت</FormLabel>
|
|
|
|
|
+
|
|
|
|
|
+ <Select
|
|
|
|
|
+ disabled={readOnly}
|
|
|
|
|
+ value={field.value?.toString() ?? ''}
|
|
|
|
|
+ onValueChange={(value) => field.onChange(Number(value))}
|
|
|
|
|
+ >
|
|
|
|
|
+ <FormControl>
|
|
|
|
|
+ <SelectTrigger>
|
|
|
|
|
+ <SelectValue placeholder='انتخاب جنسیت' />
|
|
|
|
|
+ </SelectTrigger>
|
|
|
|
|
+ </FormControl>
|
|
|
|
|
+
|
|
|
|
|
+ <SelectContent>
|
|
|
|
|
+ {genderOption.map((item) => (
|
|
|
|
|
+ <SelectItem
|
|
|
|
|
+ key={item.value}
|
|
|
|
|
+ value={String(item.value)}
|
|
|
|
|
+ >
|
|
|
|
|
+ {item.label}
|
|
|
|
|
+ </SelectItem>
|
|
|
|
|
+ ))}
|
|
|
|
|
+ </SelectContent>
|
|
|
|
|
+ </Select>
|
|
|
|
|
+
|
|
|
|
|
+ <FormMessage />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ )}
|
|
|
|
|
+ />
|
|
|
|
|
+
|
|
|
|
|
+ <FormField
|
|
|
|
|
+ control={form.control}
|
|
|
|
|
+ name='province_id'
|
|
|
|
|
+ rules={{ required: 'انتخاب استان الزامی است' }}
|
|
|
|
|
+ render={({ field }) => (
|
|
|
|
|
+ <FormItem>
|
|
|
|
|
+ <FormLabel>استان</FormLabel>
|
|
|
|
|
+ <Select
|
|
|
|
|
+ disabled={readOnly}
|
|
|
|
|
+ value={field.value ? field.value.toString() : ''}
|
|
|
|
|
+ onValueChange={(value) => {
|
|
|
|
|
+ field.onChange(value ? Number(value) : undefined)
|
|
|
|
|
+ form.setValue('city_id', undefined)
|
|
|
|
|
+ }}
|
|
|
|
|
+ >
|
|
|
|
|
+ <FormControl>
|
|
|
|
|
+ <SelectTrigger>
|
|
|
|
|
+ <SelectValue placeholder='انتخاب استان' />
|
|
|
|
|
+ </SelectTrigger>
|
|
|
|
|
+ </FormControl>
|
|
|
|
|
+
|
|
|
|
|
+ <SelectContent>
|
|
|
|
|
+ {provinces.map((province) => (
|
|
|
|
|
+ <SelectItem
|
|
|
|
|
+ key={province.id}
|
|
|
|
|
+ value={String(province.id)}
|
|
|
|
|
+ >
|
|
|
|
|
+ {province.name}
|
|
|
|
|
+ </SelectItem>
|
|
|
|
|
+ ))}
|
|
|
|
|
+ </SelectContent>
|
|
|
|
|
+ </Select>
|
|
|
|
|
+
|
|
|
|
|
+ <FormMessage />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ )}
|
|
|
|
|
+ />
|
|
|
|
|
+
|
|
|
|
|
+ <FormField
|
|
|
|
|
+ control={form.control}
|
|
|
|
|
+ name='city_id'
|
|
|
|
|
+ rules={{ required: 'انتخاب شهر الزامی است' }}
|
|
|
|
|
+ render={({ field }) => (
|
|
|
|
|
+ <FormItem>
|
|
|
|
|
+ <FormLabel>شهر</FormLabel>
|
|
|
|
|
+
|
|
|
|
|
+ <Select
|
|
|
|
|
+ disabled={readOnly || !provinceId}
|
|
|
|
|
+ value={field.value ? String(field.value) : ''}
|
|
|
|
|
+ onValueChange={(value) => field.onChange(Number(value))}
|
|
|
|
|
+ >
|
|
|
|
|
+ <FormControl>
|
|
|
|
|
+ <SelectTrigger>
|
|
|
|
|
+ <SelectValue placeholder='ابتدا استان را انتخاب کنید' />
|
|
|
|
|
+ </SelectTrigger>
|
|
|
|
|
+ </FormControl>
|
|
|
|
|
+
|
|
|
|
|
+ <SelectContent>
|
|
|
|
|
+ {cities.map((city) => (
|
|
|
|
|
+ <SelectItem key={city.id} value={String(city.id)}>
|
|
|
|
|
+ {city.name}
|
|
|
|
|
+ </SelectItem>
|
|
|
|
|
+ ))}
|
|
|
|
|
+ </SelectContent>
|
|
|
|
|
+ </Select>
|
|
|
|
|
+
|
|
|
|
|
+ <FormMessage />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ )}
|
|
|
|
|
+ />
|
|
|
|
|
+
|
|
|
|
|
+ <FormField
|
|
|
|
|
+ control={form.control}
|
|
|
|
|
+ name='height'
|
|
|
|
|
+ render={({ field }) => (
|
|
|
|
|
+ <FormItem>
|
|
|
|
|
+ <FormLabel>قد</FormLabel>
|
|
|
|
|
+
|
|
|
|
|
+ <FormControl>
|
|
|
|
|
+ <Input
|
|
|
|
|
+ type='number'
|
|
|
|
|
+ value={field.value ?? ''}
|
|
|
|
|
+ disabled={readOnly}
|
|
|
|
|
+ onChange={(e) =>
|
|
|
|
|
+ field.onChange(
|
|
|
|
|
+ e.target.value === ''
|
|
|
|
|
+ ? undefined
|
|
|
|
|
+ : Number(e.target.value)
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ />
|
|
|
|
|
+ </FormControl>
|
|
|
|
|
+
|
|
|
|
|
+ <FormMessage />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ )}
|
|
|
|
|
+ />
|
|
|
|
|
+
|
|
|
|
|
+ <FormField
|
|
|
|
|
+ control={form.control}
|
|
|
|
|
+ name='weight'
|
|
|
|
|
+ render={({ field }) => (
|
|
|
|
|
+ <FormItem>
|
|
|
|
|
+ <FormLabel>وزن</FormLabel>
|
|
|
|
|
+
|
|
|
|
|
+ <FormControl>
|
|
|
|
|
+ <Input
|
|
|
|
|
+ type='number'
|
|
|
|
|
+ value={field.value ?? ''}
|
|
|
|
|
+ disabled={readOnly}
|
|
|
|
|
+ onChange={(e) =>
|
|
|
|
|
+ field.onChange(
|
|
|
|
|
+ e.target.value === ''
|
|
|
|
|
+ ? undefined
|
|
|
|
|
+ : Number(e.target.value)
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ />
|
|
|
|
|
+ </FormControl>
|
|
|
|
|
+
|
|
|
|
|
+ <FormMessage />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ )}
|
|
|
|
|
+ />
|
|
|
|
|
+
|
|
|
|
|
+ <FormField
|
|
|
|
|
+ control={form.control}
|
|
|
|
|
+ name='foot_specialization'
|
|
|
|
|
+ render={({ field }) => (
|
|
|
|
|
+ <FormItem>
|
|
|
|
|
+ <FormLabel>پای تخصصی</FormLabel>
|
|
|
|
|
+
|
|
|
|
|
+ <Select
|
|
|
|
|
+ disabled={readOnly}
|
|
|
|
|
+ value={field.value ?? ''}
|
|
|
|
|
+ onValueChange={field.onChange}
|
|
|
|
|
+ >
|
|
|
|
|
+ <FormControl>
|
|
|
|
|
+ <SelectTrigger>
|
|
|
|
|
+ <SelectValue placeholder='انتخاب کنید' />
|
|
|
|
|
+ </SelectTrigger>
|
|
|
|
|
+ </FormControl>
|
|
|
|
|
+
|
|
|
|
|
+ <SelectContent>
|
|
|
|
|
+ {footOptions.map((item) => (
|
|
|
|
|
+ <SelectItem key={item.value} value={item.value}>
|
|
|
|
|
+ {item.label}
|
|
|
|
|
+ </SelectItem>
|
|
|
|
|
+ ))}
|
|
|
|
|
+ </SelectContent>
|
|
|
|
|
+ </Select>
|
|
|
|
|
+
|
|
|
|
|
+ <FormMessage />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ )}
|
|
|
|
|
+ />
|
|
|
|
|
+
|
|
|
|
|
+ <FormField
|
|
|
|
|
+ control={form.control}
|
|
|
|
|
+ name='post_skill'
|
|
|
|
|
+ render={({ field }) => (
|
|
|
|
|
+ <FormItem>
|
|
|
|
|
+ <FormLabel>پست تخصصی</FormLabel>
|
|
|
|
|
+
|
|
|
|
|
+ <Select
|
|
|
|
|
+ disabled={readOnly}
|
|
|
|
|
+ value={field.value ?? ''}
|
|
|
|
|
+ onValueChange={field.onChange}
|
|
|
|
|
+ >
|
|
|
|
|
+ <FormControl>
|
|
|
|
|
+ <SelectTrigger>
|
|
|
|
|
+ <SelectValue placeholder='انتخاب کنید' />
|
|
|
|
|
+ </SelectTrigger>
|
|
|
|
|
+ </FormControl>
|
|
|
|
|
+
|
|
|
|
|
+ <SelectContent>
|
|
|
|
|
+ {positionOptions.map((item) => (
|
|
|
|
|
+ <SelectItem key={item.value} value={item.value}>
|
|
|
|
|
+ {item.label}
|
|
|
|
|
+ </SelectItem>
|
|
|
|
|
+ ))}
|
|
|
|
|
+ </SelectContent>
|
|
|
|
|
+ </Select>
|
|
|
|
|
+
|
|
|
|
|
+ <FormMessage />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ )}
|
|
|
|
|
+ />
|
|
|
|
|
+
|
|
|
|
|
+ <FormField
|
|
|
|
|
+ control={form.control}
|
|
|
|
|
+ name='skill_level'
|
|
|
|
|
+ render={({ field }) => (
|
|
|
|
|
+ <FormItem>
|
|
|
|
|
+ <FormLabel>سطح مهارت</FormLabel>
|
|
|
|
|
+
|
|
|
|
|
+ <Select
|
|
|
|
|
+ disabled={readOnly}
|
|
|
|
|
+ value={field.value ?? ''}
|
|
|
|
|
+ onValueChange={field.onChange}
|
|
|
|
|
+ >
|
|
|
|
|
+ <FormControl>
|
|
|
|
|
+ <SelectTrigger>
|
|
|
|
|
+ <SelectValue placeholder='انتخاب کنید' />
|
|
|
|
|
+ </SelectTrigger>
|
|
|
|
|
+ </FormControl>
|
|
|
|
|
+
|
|
|
|
|
+ <SelectContent>
|
|
|
|
|
+ {skillLevelOptions.map((item) => (
|
|
|
|
|
+ <SelectItem key={item.value} value={item.value}>
|
|
|
|
|
+ {item.label}
|
|
|
|
|
+ </SelectItem>
|
|
|
|
|
+ ))}
|
|
|
|
|
+ </SelectContent>
|
|
|
|
|
+ </Select>
|
|
|
|
|
+
|
|
|
|
|
+ <FormMessage />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ )}
|
|
|
|
|
+ />
|
|
|
|
|
+
|
|
|
|
|
+ <FormField
|
|
|
|
|
+ control={form.control}
|
|
|
|
|
+ name='activity_history'
|
|
|
|
|
+ render={({ field }) => (
|
|
|
|
|
+ <FormItem>
|
|
|
|
|
+ <FormLabel>سابقه فعالیت</FormLabel>
|
|
|
|
|
+
|
|
|
|
|
+ <Select
|
|
|
|
|
+ disabled={readOnly}
|
|
|
|
|
+ value={String(field.value)}
|
|
|
|
|
+ onValueChange={(v) => field.onChange(v === 'true')}
|
|
|
|
|
+ >
|
|
|
|
|
+ <FormControl>
|
|
|
|
|
+ <SelectTrigger>
|
|
|
|
|
+ <SelectValue placeholder='انتخاب کنید' />
|
|
|
|
|
+ </SelectTrigger>
|
|
|
|
|
+ </FormControl>
|
|
|
|
|
+
|
|
|
|
|
+ <SelectContent>
|
|
|
|
|
+ {activityHistoryOptions.map((item) => (
|
|
|
|
|
+ <SelectItem
|
|
|
|
|
+ key={String(item.value)}
|
|
|
|
|
+ value={String(item.value)}
|
|
|
|
|
+ >
|
|
|
|
|
+ {item.label}
|
|
|
|
|
+ </SelectItem>
|
|
|
|
|
+ ))}
|
|
|
|
|
+ </SelectContent>
|
|
|
|
|
+ </Select>
|
|
|
|
|
+
|
|
|
|
|
+ <FormMessage />
|
|
|
|
|
+ </FormItem>
|
|
|
|
|
+ )}
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
<FormField
|
|
<FormField
|
|
|
control={form.control}
|
|
control={form.control}
|
|
|
- name='confirmPassword'
|
|
|
|
|
|
|
+ name='bio'
|
|
|
render={({ field }) => (
|
|
render={({ field }) => (
|
|
|
- <FormItem className='grid grid-cols-6 items-center space-y-0 gap-x-4 gap-y-1'>
|
|
|
|
|
- <FormLabel className='col-span-2 text-end'>
|
|
|
|
|
- Confirm Password
|
|
|
|
|
- </FormLabel>
|
|
|
|
|
|
|
+ <FormItem>
|
|
|
|
|
+ <FormLabel>بیوگرافی</FormLabel>
|
|
|
|
|
+
|
|
|
<FormControl>
|
|
<FormControl>
|
|
|
- <PasswordInput
|
|
|
|
|
- disabled={!isPasswordTouched}
|
|
|
|
|
- placeholder='e.g., S3cur3P@ssw0rd'
|
|
|
|
|
- className='col-span-4'
|
|
|
|
|
|
|
+ <Textarea
|
|
|
{...field}
|
|
{...field}
|
|
|
|
|
+ disabled={readOnly}
|
|
|
|
|
+ value={field.value ?? ''}
|
|
|
/>
|
|
/>
|
|
|
</FormControl>
|
|
</FormControl>
|
|
|
- <FormMessage className='col-span-4 col-start-3' />
|
|
|
|
|
|
|
+
|
|
|
|
|
+ <FormMessage />
|
|
|
</FormItem>
|
|
</FormItem>
|
|
|
)}
|
|
)}
|
|
|
/>
|
|
/>
|
|
|
|
|
+
|
|
|
|
|
+ {!readOnly && (
|
|
|
|
|
+ <div className='flex justify-end gap-2'>
|
|
|
|
|
+ <Button
|
|
|
|
|
+ type='button'
|
|
|
|
|
+ variant='outline'
|
|
|
|
|
+ onClick={() => setOpen(null)}
|
|
|
|
|
+ >
|
|
|
|
|
+ انصراف
|
|
|
|
|
+ </Button>
|
|
|
|
|
+
|
|
|
|
|
+ <Button
|
|
|
|
|
+ type='submit'
|
|
|
|
|
+ disabled={
|
|
|
|
|
+ createMutation.isPending || updateMutation.isPending
|
|
|
|
|
+ }
|
|
|
|
|
+ >
|
|
|
|
|
+ {isAdd ? 'ایجاد کاربر' : 'ذخیره تغییرات'}
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ )}
|
|
|
</form>
|
|
</form>
|
|
|
</Form>
|
|
</Form>
|
|
|
- </div>
|
|
|
|
|
- <DialogFooter>
|
|
|
|
|
- <Button type='submit' form='user-form'>
|
|
|
|
|
- Save changes
|
|
|
|
|
- </Button>
|
|
|
|
|
- </DialogFooter>
|
|
|
|
|
|
|
+ )}
|
|
|
</DialogContent>
|
|
</DialogContent>
|
|
|
</Dialog>
|
|
</Dialog>
|
|
|
)
|
|
)
|