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 { 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='تصویر پروفایل'
+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 { 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='تصویر پروفایل'
+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 = () => { 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,
+5
View File
@@ -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[]>;