edit my company
This commit is contained in:
@@ -9,6 +9,7 @@ type Props = {
|
|||||||
isMultiple?: boolean,
|
isMultiple?: boolean,
|
||||||
onChange?: (file: File[]) => void;
|
onChange?: (file: File[]) => void;
|
||||||
isReset?: boolean;
|
isReset?: boolean;
|
||||||
|
preview?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const UploadBox: FC<Props> = (props: Props) => {
|
const UploadBox: FC<Props> = (props: Props) => {
|
||||||
@@ -44,15 +45,11 @@ const UploadBox: FC<Props> = (props: Props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
if (props.isReset) {
|
if (props.isReset) {
|
||||||
setFiles([])
|
setFiles([])
|
||||||
}
|
}
|
||||||
|
|
||||||
}, [props.isReset])
|
}, [props.isReset])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div className='text-sm'>{props.label}</div>
|
<div className='text-sm'>{props.label}</div>
|
||||||
@@ -72,6 +69,14 @@ const UploadBox: FC<Props> = (props: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
files.length === 0 && props.preview && (
|
||||||
|
<div className='flex bg-gray-200 py-1.5 px-2 rounded-lg items-center gap-1'>
|
||||||
|
<div className='text-xs'>فایل موجود</div>
|
||||||
|
<img src={props.preview} alt="preview" className="h-4 w-4 object-cover" />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='lg:hidden gap-2 flex flex-wrap mt-4'>
|
<div className='lg:hidden gap-2 flex flex-wrap mt-4'>
|
||||||
@@ -83,6 +88,14 @@ const UploadBox: FC<Props> = (props: Props) => {
|
|||||||
</div>
|
</div>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
files.length === 0 && props.preview && (
|
||||||
|
<div className='flex bg-gray-200 py-1.5 px-2 rounded-lg items-center gap-1'>
|
||||||
|
<div className='text-xs'>فایل موجود</div>
|
||||||
|
<img src={props.preview} alt="preview" className="h-4 w-4 object-cover" />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ const CompanyDetail: FC = () => {
|
|||||||
color='black'
|
color='black'
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
شماره تماس : {data?.data?.company?.user?.phone}
|
شماره تماس : {data?.data?.company?.phone}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex gap-1 items-center'>
|
<div className='flex gap-1 items-center'>
|
||||||
@@ -85,7 +85,7 @@ const CompanyDetail: FC = () => {
|
|||||||
color='black'
|
color='black'
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
ایمیل : {data?.data?.company?.user?.email}
|
ایمیل : {data?.data?.company?.email}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex gap-1 items-center'>
|
<div className='flex gap-1 items-center'>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Add, Calendar, CallCalling, Location, Map1, MessageMinus, Profile, Setting2, Sms } from 'iconsax-react'
|
import { Add, Calendar, CallCalling, Edit, Location, Map1, MessageMinus, Profile, Setting2, Sms } from 'iconsax-react'
|
||||||
import { FC } from 'react'
|
import { FC } from 'react'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link, useNavigate, useParams } from 'react-router-dom'
|
||||||
import { useGetMyCompany } from '../home/hooks/useHomeData'
|
import { useGetMyCompany } from '../home/hooks/useHomeData'
|
||||||
import moment from 'moment-jalaali'
|
import moment from 'moment-jalaali'
|
||||||
import Button from '../../components/Button'
|
import Button from '../../components/Button'
|
||||||
@@ -9,7 +9,16 @@ import { Pages } from '../../config/Pages'
|
|||||||
import MyProduct from './components/MyProduct'
|
import MyProduct from './components/MyProduct'
|
||||||
const CompanyDetail: FC = () => {
|
const CompanyDetail: FC = () => {
|
||||||
|
|
||||||
|
const { slug } = useParams()
|
||||||
const { data } = useGetMyCompany()
|
const { data } = useGetMyCompany()
|
||||||
|
const navigate = useNavigate()
|
||||||
|
|
||||||
|
const handleEditClick = () => {
|
||||||
|
if (data?.data?.company) {
|
||||||
|
console.log('Navigating to edit with company data:', data.data.company);
|
||||||
|
navigate(buildPath(`${Pages.request}/${data.data.company.id}`, slug))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (data?.data?.company) {
|
if (data?.data?.company) {
|
||||||
return (
|
return (
|
||||||
@@ -22,24 +31,37 @@ const CompanyDetail: FC = () => {
|
|||||||
<div className='flex gap-3'>
|
<div className='flex gap-3'>
|
||||||
<img
|
<img
|
||||||
src={data?.data?.company?.profileImageUrl}
|
src={data?.data?.company?.profileImageUrl}
|
||||||
className='size-16 rounded-xl object-cover'
|
className='size-14 rounded-xl object-cover'
|
||||||
/>
|
/>
|
||||||
<div className='flex-1 items-center flex justify-between '>
|
<div className='flex-1 items-center flex justify-between '>
|
||||||
<div className='text-sm'>
|
<div className='text-xs'>
|
||||||
{data?.data?.company?.name}
|
{data?.data?.company?.name}
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center gap-1.5 text-xs'>
|
<div
|
||||||
<Map1
|
className='flex gap-2 text-[#0047FF] text-xs items-center cursor-pointer'
|
||||||
size={20}
|
onClick={handleEditClick}
|
||||||
color='black'
|
>
|
||||||
|
<Edit
|
||||||
|
size={16}
|
||||||
|
color='#0047FF'
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
آدرس در نقشه
|
ویرایش
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className='flex justify-end items-center gap-1.5 text-xs'>
|
||||||
|
<Map1
|
||||||
|
size={20}
|
||||||
|
color='black'
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
آدرس در نقشه
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className='mt-5 text-[10px]'>
|
<div className='mt-5 text-[10px]'>
|
||||||
<div className='flex justify-between'>
|
<div className='flex justify-between'>
|
||||||
<div className='flex gap-1 items-center'>
|
<div className='flex gap-1 items-center'>
|
||||||
@@ -48,7 +70,7 @@ const CompanyDetail: FC = () => {
|
|||||||
color='black'
|
color='black'
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
مدیرعامل: {data?.data?.company?.user?.fullName}
|
مدیرعامل: {data?.data?.company?.chiefExecutiveOfficer}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex gap-1 items-center'>
|
<div className='flex gap-1 items-center'>
|
||||||
@@ -68,7 +90,7 @@ const CompanyDetail: FC = () => {
|
|||||||
color='black'
|
color='black'
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
شماره تماس : {data?.data?.company?.user?.phone}
|
شماره تماس : {data?.data?.company?.phone}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex gap-1 items-center'>
|
<div className='flex gap-1 items-center'>
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ const Home: FC = () => {
|
|||||||
alt='banner'
|
alt='banner'
|
||||||
width={1920}
|
width={1920}
|
||||||
height={1080}
|
height={1080}
|
||||||
className='w-full rounded-3xl object-cover xl:mt-14 mt-8 min-h-[210px] max-h-[500px]'
|
className='w-full rounded-3xl object-cover min-h-[210px] max-h-[500px]'
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className='absolute flex items-center z-1 top-0 left-0 w-full h-full bg-black/10 rounded-3xl'>
|
<div className='absolute flex items-center z-1 top-0 left-0 w-full h-full bg-black/10 rounded-3xl'>
|
||||||
|
|||||||
+300
-179
@@ -1,4 +1,4 @@
|
|||||||
import { FC } from 'react'
|
import { FC, useEffect } from 'react'
|
||||||
import withAuth from '../../hoc/withAuth'
|
import withAuth from '../../hoc/withAuth'
|
||||||
import Button from '../../components/Button'
|
import Button from '../../components/Button'
|
||||||
import { TickCircle } from 'iconsax-react'
|
import { TickCircle } from 'iconsax-react'
|
||||||
@@ -8,12 +8,11 @@ import { useFormik } from 'formik'
|
|||||||
import * as Yup from 'yup'
|
import * as Yup from 'yup'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { toast } from '../../components/Toast'
|
import { toast } from '../../components/Toast'
|
||||||
import { ErrorType } from '../../helpers/types'
|
|
||||||
import { Pages } from '../../config/Pages'
|
import { Pages } from '../../config/Pages'
|
||||||
import { useNavigate, useParams } from 'react-router-dom'
|
import { useNavigate, useParams } from 'react-router-dom'
|
||||||
import DatePickerComponent from '../../components/DatePicker'
|
import DatePickerComponent from '../../components/DatePicker'
|
||||||
import moment from 'moment-jalaali'
|
import moment from 'moment-jalaali'
|
||||||
import { useCreateCompany, useMultiUpload, useSingleUpload } from './hooks/useRequestData'
|
import { useCreateCompany, useMultiUpload, useSingleUpload, useUpdateCompany, useGetCompanyById } from './hooks/useRequestData'
|
||||||
import { CreateCompanyType } from './types/Types'
|
import { CreateCompanyType } from './types/Types'
|
||||||
import UploadBox from '../../components/UploadBox'
|
import UploadBox from '../../components/UploadBox'
|
||||||
import CompanyServices from './components/CompanyServices'
|
import CompanyServices from './components/CompanyServices'
|
||||||
@@ -22,9 +21,11 @@ import { useGetIndustry } from '../home/hooks/useHomeData'
|
|||||||
import Select from '../../components/Select'
|
import Select from '../../components/Select'
|
||||||
import { IndustryItemType } from '../home/types/HomeTypes'
|
import { IndustryItemType } from '../home/types/HomeTypes'
|
||||||
import { buildPath } from '../../helpers/utils'
|
import { buildPath } from '../../helpers/utils'
|
||||||
|
|
||||||
const Request: FC = () => {
|
const Request: FC = () => {
|
||||||
|
|
||||||
const createCompany = useCreateCompany()
|
const createCompany = useCreateCompany()
|
||||||
|
const updateCompany = useUpdateCompany()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const { t } = useTranslation('global')
|
const { t } = useTranslation('global')
|
||||||
const [profileImage, setProfileImage] = useState<File | null>(null)
|
const [profileImage, setProfileImage] = useState<File | null>(null)
|
||||||
@@ -32,10 +33,16 @@ const Request: FC = () => {
|
|||||||
const multiUpload = useMultiUpload()
|
const multiUpload = useMultiUpload()
|
||||||
const singleUpload = useSingleUpload()
|
const singleUpload = useSingleUpload()
|
||||||
const { data: industries } = useGetIndustry()
|
const { data: industries } = useGetIndustry()
|
||||||
const { slug } = useParams()
|
const { slug, id } = useParams()
|
||||||
|
const isEdit = !!id
|
||||||
|
const { data: companyResponse, isLoading: isLoadingCompany } = useGetCompanyById(id)
|
||||||
|
const companyData = companyResponse?.data?.company || null
|
||||||
|
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||||
|
|
||||||
const [services, setServices] = useState<{ title: string; image?: File }[]>([])
|
const [services, setServices] = useState<{ title: string; image?: File; imageUrl?: string; existingImageUrl?: string }[]>([])
|
||||||
const [products, setProducts] = useState<{ title: string; image?: File }[]>([])
|
const [products, setProducts] = useState<{ title: string; image?: File; imageUrl?: string; existingImageUrl?: string }[]>([])
|
||||||
|
const [profileImageUrl, setProfileImageUrl] = useState<string>('')
|
||||||
|
const [coverImageUrl, setCoverImageUrl] = useState<string>('')
|
||||||
|
|
||||||
const formik = useFormik<CreateCompanyType>({
|
const formik = useFormik<CreateCompanyType>({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
@@ -68,192 +75,306 @@ const Request: FC = () => {
|
|||||||
industryId: Yup.string().required(t('errors.required')),
|
industryId: Yup.string().required(t('errors.required')),
|
||||||
}),
|
}),
|
||||||
onSubmit: async (values) => {
|
onSubmit: async (values) => {
|
||||||
if (!profileImage || !coverImage) {
|
values.dateOfEstablishment = moment(values.dateOfEstablishment, 'jYYYY-jMM-jDD').format('YYYY-MM-DD')
|
||||||
toast('لطفا تصاویر را انتخاب کنید', 'error')
|
try {
|
||||||
return
|
setIsSubmitting(true)
|
||||||
}
|
if (!isEdit && (!profileImage || !coverImage)) {
|
||||||
|
toast('لطفا تصاویر را انتخاب کنید', 'error')
|
||||||
let serviceImages: string[] = []
|
setIsSubmitting(false)
|
||||||
if (services.length > 0) {
|
return
|
||||||
const formDataServices = new FormData()
|
|
||||||
services.forEach(s => s.image && formDataServices.append('files', s.image))
|
|
||||||
const serviceUploadRes = await multiUpload.mutateAsync(formDataServices)
|
|
||||||
serviceImages = serviceUploadRes.data.map((item: { url: string }) => item?.url)
|
|
||||||
}
|
|
||||||
|
|
||||||
// آپلود تصاویر محصولات
|
|
||||||
let productImages: string[] = []
|
|
||||||
if (products.length > 0) {
|
|
||||||
const formDataProducts = new FormData()
|
|
||||||
products.forEach(p => p.image && formDataProducts.append('files', p.image))
|
|
||||||
const productUploadRes = await multiUpload.mutateAsync(formDataProducts)
|
|
||||||
productImages = productUploadRes.data.map((item: { url: string }) => item?.url)
|
|
||||||
}
|
|
||||||
|
|
||||||
// مقداردهی به values
|
|
||||||
values.services = services.map((s, idx) => ({
|
|
||||||
title: s.title,
|
|
||||||
imageUrl: serviceImages[idx] ? serviceImages[idx] : ''
|
|
||||||
}))
|
|
||||||
values.products = products.map((p, idx) => ({
|
|
||||||
title: p.title,
|
|
||||||
imageUrl: productImages[idx] ? productImages[idx] : ''
|
|
||||||
}))
|
|
||||||
|
|
||||||
const formDataProfile = new FormData()
|
|
||||||
formDataProfile.append('file', profileImage)
|
|
||||||
const profileUploadRes = await singleUpload.mutateAsync(formDataProfile)
|
|
||||||
values.profileImageUrl = profileUploadRes.data.url
|
|
||||||
|
|
||||||
const formDataCover = new FormData()
|
|
||||||
formDataCover.append('file', coverImage)
|
|
||||||
const coverUploadRes = await singleUpload.mutateAsync(formDataCover)
|
|
||||||
values.coverImageUrl = coverUploadRes.data.url
|
|
||||||
|
|
||||||
createCompany.mutate(values, {
|
|
||||||
onSuccess: () => {
|
|
||||||
toast('درخواست با موفقیت ثبت شد', 'success')
|
|
||||||
navigate(buildPath(Pages.dashboard, slug))
|
|
||||||
},
|
|
||||||
onError: (error: ErrorType) => {
|
|
||||||
toast(error?.response?.data?.error?.message[0], 'error')
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
// بررسی مقادیر ضروری برای آپدیت
|
||||||
|
if (isEdit && !values.profileImageUrl && !profileImage) {
|
||||||
|
toast('لطفا تصویر پروفایل را انتخاب کنید', 'error')
|
||||||
|
setIsSubmitting(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEdit && !values.coverImageUrl && !coverImage) {
|
||||||
|
toast('لطفا تصویر کاور را انتخاب کنید', 'error')
|
||||||
|
setIsSubmitting(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let serviceImages: string[] = []
|
||||||
|
if (services.length > 0) {
|
||||||
|
const newServices = services.filter(s => s.image)
|
||||||
|
if (newServices.length > 0) {
|
||||||
|
const formDataServices = new FormData()
|
||||||
|
newServices.forEach(s => s.image && formDataServices.append('files', s.image))
|
||||||
|
const serviceUploadRes = await multiUpload.mutateAsync(formDataServices)
|
||||||
|
serviceImages = serviceUploadRes.data.map((item: { url: string }) => item?.url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// آپلود تصاویر محصولات
|
||||||
|
let productImages: string[] = []
|
||||||
|
if (products.length > 0) {
|
||||||
|
const newProducts = products.filter(p => p.image)
|
||||||
|
if (newProducts.length > 0) {
|
||||||
|
const formDataProducts = new FormData()
|
||||||
|
newProducts.forEach(p => p.image && formDataProducts.append('files', p.image))
|
||||||
|
const productUploadRes = await multiUpload.mutateAsync(formDataProducts)
|
||||||
|
productImages = productUploadRes.data.map((item: { url: string }) => item?.url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// مقداردهی به values
|
||||||
|
values.services = services.map((s, idx) => {
|
||||||
|
const newImageIndex = services.filter((item, i) => item.image && i < idx).length
|
||||||
|
return {
|
||||||
|
title: s.title,
|
||||||
|
imageUrl: s.image ? (serviceImages[newImageIndex] || '') : (s.existingImageUrl || '')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
values.products = products.map((p, idx) => {
|
||||||
|
const newImageIndex = products.filter((item, i) => item.image && i < idx).length
|
||||||
|
return {
|
||||||
|
title: p.title,
|
||||||
|
imageUrl: p.image ? (productImages[newImageIndex] || '') : (p.existingImageUrl || '')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (profileImage) {
|
||||||
|
const formDataProfile = new FormData()
|
||||||
|
formDataProfile.append('file', profileImage)
|
||||||
|
const profileUploadRes = await singleUpload.mutateAsync(formDataProfile)
|
||||||
|
values.profileImageUrl = profileUploadRes.data.url
|
||||||
|
} else if (profileImageUrl) {
|
||||||
|
values.profileImageUrl = profileImageUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
if (coverImage) {
|
||||||
|
const formDataCover = new FormData()
|
||||||
|
formDataCover.append('file', coverImage)
|
||||||
|
const coverUploadRes = await singleUpload.mutateAsync(formDataCover)
|
||||||
|
values.coverImageUrl = coverUploadRes.data.url
|
||||||
|
} else if (coverImageUrl) {
|
||||||
|
values.coverImageUrl = coverImageUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEdit && companyData?.id) {
|
||||||
|
await handleUpdateCompany(companyData.id, values)
|
||||||
|
} else {
|
||||||
|
await handleCreateCompany(values)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
setIsSubmitting(false)
|
||||||
|
toast('خطا در ثبت اطلاعات', 'error')
|
||||||
|
console.error('Submit error:', error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const handleUpdateCompany = async (companyId: string, values: CreateCompanyType) => {
|
||||||
|
try {
|
||||||
|
console.log('Updating company with ID:', companyId);
|
||||||
|
console.log('Update data:', values);
|
||||||
|
await updateCompany.mutateAsync(
|
||||||
|
{ id: companyId, data: values }
|
||||||
|
)
|
||||||
|
toast('اطلاعات شرکت با موفقیت ویرایش شد', 'success')
|
||||||
|
navigate(buildPath(Pages.company.my, slug), { replace: true })
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('Update error:', error);
|
||||||
|
toast(error?.response?.data?.error?.message[0] || 'خطا در ویرایش اطلاعات', 'error')
|
||||||
|
} finally {
|
||||||
|
setIsSubmitting(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCreateCompany = async (values: CreateCompanyType) => {
|
||||||
|
try {
|
||||||
|
await createCompany.mutateAsync(values)
|
||||||
|
toast('درخواست با موفقیت ثبت شد', 'success')
|
||||||
|
navigate(buildPath(Pages.dashboard, slug), { replace: true })
|
||||||
|
} catch (error: any) {
|
||||||
|
toast(error?.response?.data?.error?.message[0] || 'خطا در ثبت درخواست', 'error')
|
||||||
|
} finally {
|
||||||
|
setIsSubmitting(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isEdit && companyData) {
|
||||||
|
formik.setValues({
|
||||||
|
phone: companyData.user?.phone || '',
|
||||||
|
name: companyData.name || '',
|
||||||
|
email: companyData?.email || '',
|
||||||
|
identificationNumber: companyData.identificationNumber || '',
|
||||||
|
dateOfEstablishment: moment(companyData.dateOfEstablishment, 'jYYYY-jMM-jDD').format('jYYYY-jMM-jDD') || '',
|
||||||
|
address: companyData.address || '',
|
||||||
|
mapAddressLink: companyData.mapAddressLink || '',
|
||||||
|
description: companyData.description || '',
|
||||||
|
websiteUrl: companyData.websiteUrl || '',
|
||||||
|
profileImageUrl: companyData.profileImageUrl || '',
|
||||||
|
coverImageUrl: companyData.coverImageUrl || '',
|
||||||
|
industryId: companyData.industry?.id || '',
|
||||||
|
chiefExecutiveOfficer: companyData.user?.fullName || '',
|
||||||
|
products: [],
|
||||||
|
services: [],
|
||||||
|
})
|
||||||
|
|
||||||
|
setProfileImageUrl(companyData.profileImageUrl || '')
|
||||||
|
setCoverImageUrl(companyData.coverImageUrl || '')
|
||||||
|
|
||||||
|
if (companyData.services?.length) {
|
||||||
|
setServices(companyData.services.map((service: any) => ({
|
||||||
|
title: service.title,
|
||||||
|
existingImageUrl: service.imageUrl
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (companyData.products?.length) {
|
||||||
|
setProducts(companyData.products.map((product: any) => ({
|
||||||
|
title: product.title,
|
||||||
|
existingImageUrl: product.imageUrl
|
||||||
|
})))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [isEdit, companyData])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='mt-4'>
|
<div className='mt-4'>
|
||||||
<div className='flex flex-col md:flex-row justify-between items-start md:items-center gap-4'>
|
{isLoadingCompany && isEdit ? (
|
||||||
<div className='text-base'>
|
<div className='flex justify-center items-center py-20'>
|
||||||
ثبت درخواست
|
<div className='animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-primary'></div>
|
||||||
</div>
|
</div>
|
||||||
|
) : (
|
||||||
<div className='flex justify-end w-full'>
|
<>
|
||||||
<Button
|
<div className='flex flex-col md:flex-row justify-between items-start md:items-center gap-4'>
|
||||||
className='w-fit px-4'
|
<div className='text-base'>
|
||||||
onClick={() => formik.handleSubmit()}
|
{isEdit ? 'ویرایش اطلاعات شرکت' : 'ثبت درخواست'}
|
||||||
isLoading={singleUpload.isPending || multiUpload.isPending}
|
|
||||||
>
|
|
||||||
<div className='flex items-center gap-2'>
|
|
||||||
<TickCircle size={20} color='white' />
|
|
||||||
<div>ثبت درخواست</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='mt-10 bg-white py-8 px-4 md:px-6 lg:px-10 rounded-3xl'>
|
<div className='flex justify-end w-full'>
|
||||||
<div className='text-lg font-medium mb-6'>اطلاعات درخواست</div>
|
<Button
|
||||||
|
className='w-fit px-4'
|
||||||
<div className='space-y-6'>
|
onClick={() => formik.handleSubmit()}
|
||||||
<Input
|
isLoading={isSubmitting}
|
||||||
label='نام شرکت'
|
>
|
||||||
name='name'
|
<div className='flex items-center gap-2'>
|
||||||
value={formik.values.name}
|
<TickCircle size={20} color='white' />
|
||||||
onChange={formik.handleChange}
|
<div>{isEdit ? 'ذخیره تغییرات' : 'ثبت درخواست'}</div>
|
||||||
error_text={formik.touched.name && formik.errors.name ? formik.errors.name : ''}
|
</div>
|
||||||
/>
|
</Button>
|
||||||
|
</div>
|
||||||
<div className=''>
|
|
||||||
<Input
|
|
||||||
label='نام مدیر عامل'
|
|
||||||
name='chiefExecutiveOfficer'
|
|
||||||
value={formik.values.chiefExecutiveOfficer}
|
|
||||||
onChange={formik.handleChange}
|
|
||||||
error_text={formik.touched.chiefExecutiveOfficer && formik.errors.chiefExecutiveOfficer ? formik.errors.chiefExecutiveOfficer : ''}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className='mt-10 bg-white py-8 px-4 md:px-6 lg:px-10 rounded-3xl'>
|
||||||
|
<div className='text-lg font-medium mb-6'>{isEdit ? 'اطلاعات شرکت' : 'اطلاعات درخواست'}</div>
|
||||||
|
|
||||||
<div className='grid grid-cols-1 md:grid-cols-2 gap-6'>
|
<div className='space-y-6'>
|
||||||
<Input
|
<Input
|
||||||
label={t('company.phone_number')}
|
label='نام شرکت'
|
||||||
name='phone'
|
name='name'
|
||||||
value={formik.values.phone}
|
value={formik.values.name}
|
||||||
onChange={formik.handleChange}
|
onChange={formik.handleChange}
|
||||||
error_text={formik.touched.phone && formik.errors.phone ? formik.errors.phone : ''}
|
error_text={formik.touched.name && formik.errors.name ? formik.errors.name : ''}
|
||||||
/>
|
/>
|
||||||
<Input
|
|
||||||
label={t('company.email')}
|
<div className=''>
|
||||||
name='email'
|
<Input
|
||||||
value={formik.values.email}
|
label='نام مدیر عامل'
|
||||||
onChange={formik.handleChange}
|
name='chiefExecutiveOfficer'
|
||||||
error_text={formik.touched.email && formik.errors.email ? formik.errors.email : ''}
|
value={formik.values.chiefExecutiveOfficer}
|
||||||
/>
|
onChange={formik.handleChange}
|
||||||
|
error_text={formik.touched.chiefExecutiveOfficer && formik.errors.chiefExecutiveOfficer ? formik.errors.chiefExecutiveOfficer : ''}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='grid grid-cols-1 md:grid-cols-2 gap-6'>
|
||||||
|
<Input
|
||||||
|
label={t('company.phone_number')}
|
||||||
|
name='phone'
|
||||||
|
value={formik.values.phone}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
error_text={formik.touched.phone && formik.errors.phone ? formik.errors.phone : ''}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
label={t('company.email')}
|
||||||
|
name='email'
|
||||||
|
value={formik.values.email}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
error_text={formik.touched.email && formik.errors.email ? formik.errors.email : ''}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='grid grid-cols-1 md:grid-cols-2 gap-6'>
|
||||||
|
<Input
|
||||||
|
label={t('company.national_id')}
|
||||||
|
name='identificationNumber'
|
||||||
|
value={formik.values.identificationNumber}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
error_text={formik.touched.identificationNumber && formik.errors.identificationNumber ? formik.errors.identificationNumber : ''}
|
||||||
|
/>
|
||||||
|
<DatePickerComponent
|
||||||
|
label={t('company.establishment_date')}
|
||||||
|
placeholder='تاریخ تاسیس'
|
||||||
|
onChange={(date) => formik.setFieldValue('dateOfEstablishment', date)}
|
||||||
|
error_text={formik.touched.dateOfEstablishment && formik.errors.dateOfEstablishment ? formik.errors.dateOfEstablishment : ''}
|
||||||
|
defaulValue={formik.values.dateOfEstablishment}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Select
|
||||||
|
label={t('company.industry')}
|
||||||
|
name='industryId'
|
||||||
|
items={industries?.data?.industries?.map((industry: IndustryItemType) => ({ label: industry.title, value: industry.id })) || []}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
error_text={formik.touched.industryId && formik.errors.industryId ? formik.errors.industryId : ''}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
label={t('company.map_link')}
|
||||||
|
name='mapAddressLink'
|
||||||
|
value={formik.values.mapAddressLink}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
error_text={formik.touched.mapAddressLink && formik.errors.mapAddressLink ? formik.errors.mapAddressLink : ''}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
label={t('company.address')}
|
||||||
|
name='address'
|
||||||
|
value={formik.values.address}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
error_text={formik.touched.address && formik.errors.address ? formik.errors.address : ''}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
label={t('company.short_description')}
|
||||||
|
name='description'
|
||||||
|
value={formik.values.description}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
error_text={formik.touched.description && formik.errors.description ? formik.errors.description : ''}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
label={t('company.website_url')}
|
||||||
|
name='websiteUrl'
|
||||||
|
value={formik.values.websiteUrl}
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
error_text={formik.touched.websiteUrl && formik.errors.websiteUrl ? formik.errors.websiteUrl : ''}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<UploadBox
|
||||||
|
label='تصویر پروفایل شرکت'
|
||||||
|
onChange={(file) => setProfileImage(file[0])}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<UploadBox
|
||||||
|
label='تصویر کاور'
|
||||||
|
onChange={(file) => setCoverImage(file[0])}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<CompanyServices value={services} onChange={setServices} />
|
||||||
|
<CompanyProducts value={products} onChange={setProducts} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
<div className='grid grid-cols-1 md:grid-cols-2 gap-6'>
|
)}
|
||||||
<Input
|
|
||||||
label={t('company.national_id')}
|
|
||||||
name='identificationNumber'
|
|
||||||
value={formik.values.identificationNumber}
|
|
||||||
onChange={formik.handleChange}
|
|
||||||
error_text={formik.touched.identificationNumber && formik.errors.identificationNumber ? formik.errors.identificationNumber : ''}
|
|
||||||
/>
|
|
||||||
<DatePickerComponent
|
|
||||||
label={t('company.establishment_date')}
|
|
||||||
placeholder='تاریخ تاسیس'
|
|
||||||
onChange={(date) => formik.setFieldValue('dateOfEstablishment', moment(date).format('YYYY-MM-DD'))}
|
|
||||||
error_text={formik.touched.dateOfEstablishment && formik.errors.dateOfEstablishment ? formik.errors.dateOfEstablishment : ''}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Select
|
|
||||||
label={t('company.industry')}
|
|
||||||
name='industryId'
|
|
||||||
items={industries?.data?.industries?.map((industry: IndustryItemType) => ({ label: industry.title, value: industry.id })) || []}
|
|
||||||
onChange={formik.handleChange}
|
|
||||||
error_text={formik.touched.industryId && formik.errors.industryId ? formik.errors.industryId : ''}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Input
|
|
||||||
label={t('company.map_link')}
|
|
||||||
name='mapAddressLink'
|
|
||||||
value={formik.values.mapAddressLink}
|
|
||||||
onChange={formik.handleChange}
|
|
||||||
error_text={formik.touched.mapAddressLink && formik.errors.mapAddressLink ? formik.errors.mapAddressLink : ''}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Input
|
|
||||||
label={t('company.address')}
|
|
||||||
name='address'
|
|
||||||
value={formik.values.address}
|
|
||||||
onChange={formik.handleChange}
|
|
||||||
error_text={formik.touched.address && formik.errors.address ? formik.errors.address : ''}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Input
|
|
||||||
label={t('company.short_description')}
|
|
||||||
name='description'
|
|
||||||
value={formik.values.description}
|
|
||||||
onChange={formik.handleChange}
|
|
||||||
error_text={formik.touched.description && formik.errors.description ? formik.errors.description : ''}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Input
|
|
||||||
label={t('company.website_url')}
|
|
||||||
name='websiteUrl'
|
|
||||||
value={formik.values.websiteUrl}
|
|
||||||
onChange={formik.handleChange}
|
|
||||||
error_text={formik.touched.websiteUrl && formik.errors.websiteUrl ? formik.errors.websiteUrl : ''}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<UploadBox
|
|
||||||
label='تصویر پروفایل شرکت'
|
|
||||||
onChange={(file) => setProfileImage(file[0])}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<UploadBox
|
|
||||||
label='تصویر کاور'
|
|
||||||
onChange={(file) => setCoverImage(file[0])}
|
|
||||||
/>
|
|
||||||
|
|
||||||
|
|
||||||
<CompanyServices value={services} onChange={setServices} />
|
|
||||||
<CompanyProducts value={products} onChange={setProducts} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ const CompanyProducts: FC<Props> = ({ value, onChange }) => {
|
|||||||
>
|
>
|
||||||
<div className='flex gap-2'>
|
<div className='flex gap-2'>
|
||||||
<Add size={22} color='white' />
|
<Add size={22} color='white' />
|
||||||
<div>افزودن خدمت</div>
|
<div>افزودن محصول</div>
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -75,8 +75,8 @@ const CompanyProducts: FC<Props> = ({ value, onChange }) => {
|
|||||||
{value.map((prd, idx) => (
|
{value.map((prd, idx) => (
|
||||||
<div key={idx} className='flex border p-2 rounded-xl items-center gap-2'>
|
<div key={idx} className='flex border p-2 rounded-xl items-center gap-2'>
|
||||||
<div className='flex items-center gap-2'>
|
<div className='flex items-center gap-2'>
|
||||||
{prd.image && <img src={URL.createObjectURL(prd.image)} alt='' className='size-6 min-w-6 rounded-full object-cover' />}
|
{prd.image && <img src={URL.createObjectURL(prd.image)} alt='' className='size-10 min-w-10 rounded-md object-cover' />}
|
||||||
{prd.existingImageUrl && !prd.image && <img src={prd.existingImageUrl} alt='' className='size-6 min-w-6 rounded-full object-cover' />}
|
{prd.existingImageUrl && !prd.image && <img src={prd.existingImageUrl} alt='' className='size-10 min-w-10 rounded-md object-cover' />}
|
||||||
</div>
|
</div>
|
||||||
<div className='w-full text-xs'>{prd.title}</div>
|
<div className='w-full text-xs'>{prd.title}</div>
|
||||||
<button type='button' onClick={() => handleRemoveProduct(idx)} className='ml-auto'>
|
<button type='button' onClick={() => handleRemoveProduct(idx)} className='ml-auto'>
|
||||||
|
|||||||
@@ -75,8 +75,8 @@ const CompanyServices: FC<Props> = ({ value, onChange }) => {
|
|||||||
{value.map((srv, idx) => (
|
{value.map((srv, idx) => (
|
||||||
<div key={idx} className='flex border p-2 rounded-xl items-center gap-2'>
|
<div key={idx} className='flex border p-2 rounded-xl items-center gap-2'>
|
||||||
<div className='flex items-center gap-2'>
|
<div className='flex items-center gap-2'>
|
||||||
{srv.image && <img src={URL.createObjectURL(srv.image)} alt='' className='size-6 min-w-6 rounded-full object-cover' />}
|
{srv.image && <img src={URL.createObjectURL(srv.image)} alt='' className='size-10 min-w-10 rounded-md object-cover' />}
|
||||||
{srv.existingImageUrl && !srv.image && <img src={srv.existingImageUrl} alt='' className='size-6 min-w-6 rounded-full object-cover' />}
|
{srv.existingImageUrl && !srv.image && <img src={srv.existingImageUrl} alt='' className='size-10 min-w-10 rounded-md object-cover' />}
|
||||||
</div>
|
</div>
|
||||||
<div className='w-full text-xs'>{srv.title}</div>
|
<div className='w-full text-xs'>{srv.title}</div>
|
||||||
<button type='button' onClick={() => handleRemoveService(idx)} className='ml-auto'>
|
<button type='button' onClick={() => handleRemoveService(idx)} className='ml-auto'>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import * as api from "../service/Service";
|
import * as api from "../service/Service";
|
||||||
import { useMutation } from "@tanstack/react-query";
|
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||||
import { CreateCompanyType } from "../types/Types";
|
import { CreateCompanyType } from "../types/Types";
|
||||||
|
|
||||||
export const useCreateCompany = () => {
|
export const useCreateCompany = () => {
|
||||||
@@ -8,6 +8,21 @@ export const useCreateCompany = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useUpdateCompany = () => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (variables: { id: string; data: CreateCompanyType }) =>
|
||||||
|
api.updateCompany(variables),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useGetCompanyById = (id: string | undefined) => {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ["company", id],
|
||||||
|
queryFn: () => (id ? api.getCompanyById(id) : null),
|
||||||
|
enabled: !!id,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const useSingleUpload = () => {
|
export const useSingleUpload = () => {
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: (variables: FormData) => api.uploadSingle(variables),
|
mutationFn: (variables: FormData) => api.uploadSingle(variables),
|
||||||
|
|||||||
@@ -6,6 +6,22 @@ export const createCompany = async (data: CreateCompanyType) => {
|
|||||||
return response.data;
|
return response.data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const updateCompany = async ({
|
||||||
|
id,
|
||||||
|
data,
|
||||||
|
}: {
|
||||||
|
id: string;
|
||||||
|
data: CreateCompanyType;
|
||||||
|
}) => {
|
||||||
|
const response = await axios.patch(`/companies/${id}/user`, data);
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCompanyById = async (id: string) => {
|
||||||
|
const response = await axios.get(`/companies/${id}`);
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
export const uploadSingle = async (params: FormData) => {
|
export const uploadSingle = async (params: FormData) => {
|
||||||
const { data } = await axios.post(`/uploader/single-file`, params);
|
const { data } = await axios.post(`/uploader/single-file`, params);
|
||||||
return data;
|
return data;
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ const MainRouter = () => {
|
|||||||
<Route path="reportCard/list" element={<ReportCardList />} />
|
<Route path="reportCard/list" element={<ReportCardList />} />
|
||||||
<Route path="profile" element={<Profile />} />
|
<Route path="profile" element={<Profile />} />
|
||||||
<Route path="request" element={<Request />} />
|
<Route path="request" element={<Request />} />
|
||||||
|
<Route path="request/:id" element={<Request />} />
|
||||||
<Route path="tickets" element={<TicketList />} />
|
<Route path="tickets" element={<TicketList />} />
|
||||||
<Route path="tickets/create" element={<TicketCreate />} />
|
<Route path="tickets/create" element={<TicketCreate />} />
|
||||||
<Route path="tickets/messages/:id" element={<TicketDetail />} />
|
<Route path="tickets/messages/:id" element={<TicketDetail />} />
|
||||||
|
|||||||
Reference in New Issue
Block a user