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