This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { type FC, useEffect, useState } from 'react'
|
||||
import { type FC, useEffect, useMemo, useState } from 'react'
|
||||
import { useFormik } from 'formik'
|
||||
import * as Yup from 'yup'
|
||||
import { TickCircle } from 'iconsax-react'
|
||||
@@ -6,11 +6,13 @@ import { toast } from 'react-toastify'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import Button from '@/components/Button'
|
||||
import Input from '@/components/Input'
|
||||
import MultiSelect from '@/components/MultiSelect'
|
||||
import Select from '@/components/Select'
|
||||
import DatePicker from '@/components/DatePicker'
|
||||
import UploadBox from '@/components/UploadBox'
|
||||
import { useCreateUser } from './hooks/useUsersData'
|
||||
import { useSingleUpload } from '@/pages/uploader/hooks/useUploaderData'
|
||||
import { useGetUserGroups } from '@/pages/customers/groups/hooks/useUserGroupData'
|
||||
import type { CreateUserType } from './types/Types'
|
||||
import { Pages } from '@/config/Pages'
|
||||
import type { ErrorType } from '@/helpers/types'
|
||||
@@ -23,6 +25,7 @@ type CreateCustomerFormValues = {
|
||||
birthDate: string
|
||||
marriageDate: string
|
||||
gender: string
|
||||
groupIds: string[]
|
||||
}
|
||||
|
||||
const buildCreateUserPayload = (
|
||||
@@ -39,6 +42,7 @@ const buildCreateUserPayload = (
|
||||
if (values.marriageDate) payload.marriageDate = values.marriageDate
|
||||
if (values.gender !== '') payload.gender = values.gender === 'true'
|
||||
if (avatarUrl) payload.avatarUrl = avatarUrl
|
||||
if (values.groupIds.length > 0) payload.groupIds = values.groupIds
|
||||
|
||||
return payload
|
||||
}
|
||||
@@ -47,9 +51,19 @@ const CreateCustomer: FC = () => {
|
||||
const navigate = useNavigate()
|
||||
const { mutate: createUser, isPending } = useCreateUser()
|
||||
const { mutate: singleUpload, isPending: isUploading } = useSingleUpload()
|
||||
const { data: groupsData } = useGetUserGroups()
|
||||
const [avatarFile, setAvatarFile] = useState<File[]>([])
|
||||
const [avatarPreviewUrl, setAvatarPreviewUrl] = useState('')
|
||||
|
||||
const groupOptions = useMemo(
|
||||
() =>
|
||||
(groupsData?.data || []).map((group) => ({
|
||||
value: group.id,
|
||||
label: group.name,
|
||||
})),
|
||||
[groupsData],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (avatarFile.length > 0) {
|
||||
const url = URL.createObjectURL(avatarFile[0])
|
||||
@@ -67,6 +81,7 @@ const CreateCustomer: FC = () => {
|
||||
birthDate: '',
|
||||
marriageDate: '',
|
||||
gender: '',
|
||||
groupIds: [],
|
||||
},
|
||||
validationSchema: Yup.object().shape({
|
||||
phone: Yup.string().required('شماره تلفن الزامی است'),
|
||||
@@ -75,6 +90,7 @@ const CreateCustomer: FC = () => {
|
||||
birthDate: Yup.string(),
|
||||
marriageDate: Yup.string(),
|
||||
gender: Yup.string(),
|
||||
groupIds: Yup.array().of(Yup.string()),
|
||||
}),
|
||||
onSubmit: (values) => {
|
||||
const submitCustomer = (avatarUrl?: string) => {
|
||||
@@ -197,6 +213,21 @@ const CreateCustomer: FC = () => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='mt-5'>
|
||||
<MultiSelect
|
||||
label='گروهها'
|
||||
items={groupOptions}
|
||||
value={formik.values.groupIds}
|
||||
onChange={(values) => formik.setFieldValue('groupIds', values)}
|
||||
placeholder='انتخاب گروهها'
|
||||
error_text={
|
||||
formik.touched.groupIds && formik.errors.groupIds
|
||||
? String(formik.errors.groupIds)
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='mt-5'>
|
||||
<UploadBox
|
||||
label='تصویر پروفایل'
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
import { type FC, useEffect, useState } from 'react'
|
||||
import { type FC, useEffect, useMemo, useState } from 'react'
|
||||
import { useFormik } from 'formik'
|
||||
import { TickCircle } from 'iconsax-react'
|
||||
import { toast } from 'react-toastify'
|
||||
import { useLocation, useNavigate, useParams } from 'react-router-dom'
|
||||
import Button from '@/components/Button'
|
||||
import Input from '@/components/Input'
|
||||
import MultiSelect from '@/components/MultiSelect'
|
||||
import Select from '@/components/Select'
|
||||
import DatePicker from '@/components/DatePicker'
|
||||
import UploadBox from '@/components/UploadBox'
|
||||
import { useSingleUpload } from '@/pages/uploader/hooks/useUploaderData'
|
||||
import { useUpdateUser } from './hooks/useUsersData'
|
||||
import { useGetUserGroupsForUser, useUpdateUser } from './hooks/useUsersData'
|
||||
import { useGetUserGroups } from '@/pages/customers/groups/hooks/useUserGroupData'
|
||||
import type { CustomerListRow, UpdateUserType } from './types/Types'
|
||||
import { Pages } from '@/config/Pages'
|
||||
import type { ErrorType } from '@/helpers/types'
|
||||
@@ -21,6 +23,7 @@ type UpdateCustomerFormValues = {
|
||||
birthDate: string
|
||||
marriageDate: string
|
||||
gender: string
|
||||
groupIds: string[]
|
||||
}
|
||||
|
||||
type CustomerUpdateLocationState = {
|
||||
@@ -33,11 +36,27 @@ const UpdateCustomer: FC = () => {
|
||||
const { id } = useParams<{ id: string }>()
|
||||
const { mutate: updateUser, isPending: isUpdating } = useUpdateUser()
|
||||
const { mutate: singleUpload, isPending: isUploading } = useSingleUpload()
|
||||
const { data: userGroupsData } = useGetUserGroupsForUser(id || '')
|
||||
const { data: allGroupsData } = useGetUserGroups()
|
||||
const [avatarFile, setAvatarFile] = useState<File[]>([])
|
||||
const [avatarPreviewUrl, setAvatarPreviewUrl] = useState('')
|
||||
|
||||
const customer = (location.state as CustomerUpdateLocationState | null)?.customer
|
||||
|
||||
const initialGroupIds = useMemo(
|
||||
() => (userGroupsData?.data || []).map((group) => group.id),
|
||||
[userGroupsData],
|
||||
)
|
||||
|
||||
const groupOptions = useMemo(
|
||||
() =>
|
||||
(allGroupsData?.data || []).map((group) => ({
|
||||
value: group.id,
|
||||
label: group.name,
|
||||
})),
|
||||
[allGroupsData],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (!customer?.avatarUrl) {
|
||||
setAvatarPreviewUrl('')
|
||||
@@ -64,6 +83,7 @@ const UpdateCustomer: FC = () => {
|
||||
customer?.gender === undefined || customer?.gender === null
|
||||
? ''
|
||||
: String(customer.gender),
|
||||
groupIds: initialGroupIds,
|
||||
},
|
||||
enableReinitialize: true,
|
||||
onSubmit: (values) => {
|
||||
@@ -73,7 +93,9 @@ const UpdateCustomer: FC = () => {
|
||||
}
|
||||
|
||||
const submitUpdate = (avatarUrl?: string) => {
|
||||
const payload: UpdateUserType = {}
|
||||
const payload: UpdateUserType = {
|
||||
groupIds: values.groupIds,
|
||||
}
|
||||
|
||||
if (values.firstName.trim()) payload.firstName = values.firstName.trim()
|
||||
if (values.lastName.trim()) payload.lastName = values.lastName.trim()
|
||||
@@ -82,7 +104,18 @@ const UpdateCustomer: FC = () => {
|
||||
if (values.gender !== '') payload.gender = values.gender === 'true'
|
||||
if (avatarUrl) payload.avatarUrl = avatarUrl
|
||||
|
||||
if (Object.keys(payload).length === 0) {
|
||||
const groupsChanged =
|
||||
JSON.stringify([...values.groupIds].sort()) !==
|
||||
JSON.stringify([...initialGroupIds].sort())
|
||||
const hasProfileChanges =
|
||||
Boolean(payload.firstName) ||
|
||||
Boolean(payload.lastName) ||
|
||||
Boolean(payload.birthDate) ||
|
||||
Boolean(payload.marriageDate) ||
|
||||
payload.gender !== undefined ||
|
||||
Boolean(payload.avatarUrl)
|
||||
|
||||
if (!hasProfileChanges && !groupsChanged) {
|
||||
toast.info('حداقل یک فیلد را برای ویرایش وارد کنید')
|
||||
return
|
||||
}
|
||||
@@ -188,6 +221,16 @@ const UpdateCustomer: FC = () => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='mt-5'>
|
||||
<MultiSelect
|
||||
label='گروهها'
|
||||
items={groupOptions}
|
||||
value={formik.values.groupIds}
|
||||
onChange={(values) => formik.setFieldValue('groupIds', values)}
|
||||
placeholder='انتخاب گروهها'
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='mt-5'>
|
||||
<UploadBox
|
||||
label='تصویر پروفایل'
|
||||
|
||||
@@ -13,6 +13,14 @@ export const useGetUsers = (params?: GetUsersParams) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetUserGroupsForUser = (userId: string) => {
|
||||
return useQuery({
|
||||
queryKey: ["user-groups-for-user", userId],
|
||||
queryFn: () => api.getUserGroups(userId),
|
||||
enabled: !!userId,
|
||||
});
|
||||
};
|
||||
|
||||
export const useSearchUserByPhone = () => {
|
||||
return useMutation({
|
||||
mutationFn: api.searchUserByPhone,
|
||||
@@ -36,8 +44,9 @@ export const useUpdateUser = () => {
|
||||
return useMutation({
|
||||
mutationFn: ({ userId, params }: { userId: string; params: UpdateUserType }) =>
|
||||
api.updateUser(userId, params),
|
||||
onSuccess: () => {
|
||||
onSuccess: (_, { userId }) => {
|
||||
queryClient.invalidateQueries({ queryKey: ["users"] });
|
||||
queryClient.invalidateQueries({ queryKey: ["user-groups-for-user", userId] });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@ import type {
|
||||
CreateUserResponse,
|
||||
CreateUserType,
|
||||
GetCustomersResponse,
|
||||
GetUserGroupsForUserResponse,
|
||||
GetUsersParams,
|
||||
ImportUsersResponse,
|
||||
SearchUserByPhoneResponse,
|
||||
@@ -40,6 +41,13 @@ export const updateUser = async (userId: string, params: UpdateUserType) => {
|
||||
return data;
|
||||
};
|
||||
|
||||
export const getUserGroups = async (userId: string) => {
|
||||
const { data } = await axios.get<GetUserGroupsForUserResponse>(
|
||||
`/admin/users/${userId}/groups`,
|
||||
);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const createUserAddress = async (
|
||||
userId: string,
|
||||
params: CreateUserAddressType,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { IResponse } from "@/types/response.types";
|
||||
import type { RowDataType } from "@/components/types/TableTypes";
|
||||
import type { UserGroup } from "@/pages/customers/groups/types/Types";
|
||||
|
||||
export type User = {
|
||||
id: string;
|
||||
@@ -111,6 +112,7 @@ export type CreateUserType = {
|
||||
marriageDate?: string;
|
||||
gender?: boolean;
|
||||
avatarUrl?: string;
|
||||
groupIds?: string[];
|
||||
};
|
||||
|
||||
export type CreateUserResult = {
|
||||
@@ -143,6 +145,9 @@ export type UpdateUserType = {
|
||||
marriageDate?: string;
|
||||
gender?: boolean;
|
||||
avatarUrl?: string;
|
||||
groupIds?: string[];
|
||||
};
|
||||
|
||||
export type UpdateUserResponse = IResponse<User>;
|
||||
|
||||
export type GetUserGroupsForUserResponse = IResponse<UserGroup[]>;
|
||||
|
||||
Reference in New Issue
Block a user