update user
deploy to danak / build_and_deploy (push) Has been cancelled

This commit is contained in:
2026-07-22 11:51:58 +03:30
parent ee1655493c
commit 4f2bf864e4
5 changed files with 102 additions and 6 deletions
+32 -1
View File
@@ -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='تصویر پروفایل'
+47 -4
View File
@@ -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='تصویر پروفایل'
+10 -1
View File
@@ -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,
+5
View File
@@ -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[]>;