fix footer upload

This commit is contained in:
hamid zarghami
2025-10-25 16:16:43 +03:30
parent 1683cb9ffe
commit f9122d2661
4 changed files with 96 additions and 167 deletions
+29 -96
View File
@@ -52,9 +52,9 @@ const SiteSetting: FC = () => {
const { data: shopData, isLoading: isShopLoading } = useGetShop()
const updateSiteSetting = useUpdateSiteSetting()
const [isDataLoaded, setIsDataLoaded] = useState(false)
const [uploadingIndexes, setUploadingIndexes] = useState<Set<number>>(new Set())
const [pendingFiles, setPendingFiles] = useState<{ [key: string]: File }>({})
const singleUpload = useSingleUpload()
const [logoFile, setLogoFile] = useState<File[]>([])
const [postalCodeFile, setPostalCodeFile] = useState<File[]>([])
@@ -73,34 +73,31 @@ const SiteSetting: FC = () => {
validateOnChange: false,
validateOnBlur: false,
onSubmit: async (values) => {
let finalDownloadAppDetails = values.downloadAppDetails
let finalSocialMediaLinks = values.socialMediaLinks
let siteLogo = values.siteLogo
let shopPostalCode = values.shopPostalCode
// اول همه فایل‌های pending رو آپلود کن
if (Object.keys(pendingFiles).length > 0) {
const uploadedData = await uploadAllPendingFiles()
// بروزرسانی downloadAppDetails با URL های واقعی
finalDownloadAppDetails = values.downloadAppDetails.map(item => {
const uploadedUrl = uploadedData[item.id]
return uploadedUrl ? { ...item, pic: uploadedUrl } : item
})
// بروزرسانی socialMediaLinks با URL های واقعی
finalSocialMediaLinks = values.socialMediaLinks.map(item => {
const uploadedUrl = uploadedData[item.id]
return uploadedUrl ? { ...item, icon: uploadedUrl } : item
})
// پاک کردن pending files بعد از آپلود موفق
setPendingFiles({})
// آپلود لوگو سایت اگر فایل جدید انتخاب شده
if (logoFile.length > 0) {
const logoResponse = await singleUpload.mutateAsync(logoFile[0])
siteLogo = logoResponse.results?.url?.url || undefined
}
// آپلود کد پستی فروشگاه اگر فایل جدید انتخاب شده
if (postalCodeFile.length > 0) {
const postalResponse = await singleUpload.mutateAsync(postalCodeFile[0])
shopPostalCode = postalResponse.results?.url?.url || undefined
}
// transform data برای ارسال به API (حذف id)
const transformedValues = {
...values,
downloadAppDetails: finalDownloadAppDetails.map((item) => ({
siteLogo,
shopPostalCode,
downloadAppDetails: values.downloadAppDetails.map((item) => ({
pic: item.pic,
url: item.url
})),
socialMediaLinks: finalSocialMediaLinks.map((item) => ({
socialMediaLinks: values.socialMediaLinks.map((item) => ({
icon: item.icon,
url: item.url
}))
@@ -110,8 +107,8 @@ const SiteSetting: FC = () => {
updateSiteSetting.mutate(transformedValues, {
onSuccess: () => {
toast.success('تنظیمات سایت با موفقیت بروزرسانی شد')
// پاک کردن pending files بعد از submit موفق
setPendingFiles({})
setLogoFile([])
setPostalCodeFile([])
},
onError: (error) => {
toast.error(extractErrorMessage(error))
@@ -151,64 +148,6 @@ const SiteSetting: FC = () => {
}, [data, shopData, isLoading, isShopLoading])
const uploadAllPendingFiles = async (): Promise<{ [key: string]: string }> => {
const uploadedUrls: { [key: string]: string } = {}
for (const [id, file] of Object.entries(pendingFiles)) {
// پیدا کردن index آیتم در downloadAppDetails
let index = formik.values.downloadAppDetails?.findIndex(item => item.id === id)
let isDownloadApp = true
if (index === undefined || index === -1) {
// اگر در downloadAppDetails نبود، در socialMediaLinks جستجو کن
index = formik.values.socialMediaLinks?.findIndex(item => item.id === id)
isDownloadApp = false
}
if (index === undefined || index === -1) continue
try {
setUploadingIndexes(prev => new Set(prev).add(index))
const uploadResponse = await singleUpload.mutateAsync(file)
const imageUrl = uploadResponse.results.url.url
uploadedUrls[id] = imageUrl
// بروزرسانی formik
if (isDownloadApp) {
const currentDetails = formik.values.downloadAppDetails || []
const updatedDetails = currentDetails.map((item, i) =>
i === index ? { ...item, pic: imageUrl } : item
)
formik.setFieldValue('downloadAppDetails', updatedDetails)
} else {
const currentLinks = formik.values.socialMediaLinks || []
const updatedLinks = currentLinks.map((item, i) =>
i === index ? { ...item, icon: imageUrl } : item
)
formik.setFieldValue('socialMediaLinks', updatedLinks)
}
} catch (error) {
const itemType = isDownloadApp ? 'تصویر دانلود اپ' : 'آیکون رسانه اجتماعی'
toast.error(`خطا در آپلود ${itemType} آیتم ${index + 1}`)
throw error
} finally {
setUploadingIndexes(prev => {
const newSet = new Set(prev)
newSet.delete(index)
return newSet
})
}
}
return uploadedUrls
}
if (isLoading || isShopLoading) {
return <div className="flex justify-center items-center h-64">در حال بارگذاری...</div>
}
@@ -237,24 +176,18 @@ const SiteSetting: FC = () => {
<div className='flex gap-6 xl:mt-8 mt-6'>
<MainInfoSection formik={formik} isDataLoaded={isDataLoaded} />
<FilesSection formik={formik} />
<FilesSection
formik={formik}
logoFile={logoFile}
setLogoFile={setLogoFile}
postalCodeFile={postalCodeFile}
setPostalCodeFile={setPostalCodeFile}
/>
</div>
<DownloadAppSection
formik={formik}
pendingFiles={pendingFiles}
setPendingFiles={setPendingFiles}
uploadingIndexes={uploadingIndexes}
setUploadingIndexes={setUploadingIndexes}
/>
<DownloadAppSection formik={formik} />
<SocialMediaSection
formik={formik}
pendingFiles={pendingFiles}
setPendingFiles={setPendingFiles}
uploadingIndexes={uploadingIndexes}
setUploadingIndexes={setUploadingIndexes}
/>
<SocialMediaSection formik={formik} />
</div>
)
}
@@ -1,9 +1,11 @@
import { type FC } from 'react'
import { type FC, useState } from 'react'
import type { FormikProps } from 'formik'
import { Add, Trash } from 'iconsax-react'
import Button from '../../../components/Button'
import Input from '../../../components/Input'
import UploadBox from '../../../components/UploadBox'
import { useSingleUpload } from '../../category/hooks/useCategoryData'
import { toast } from 'react-toastify'
type DownloadAppDetail = {
id: string
@@ -30,17 +32,13 @@ type FormValues = {
type Props = {
formik: FormikProps<FormValues>
pendingFiles: { [key: string]: File }
setPendingFiles: React.Dispatch<React.SetStateAction<{ [key: string]: File }>>
uploadingIndexes: Set<number>
setUploadingIndexes: React.Dispatch<React.SetStateAction<Set<number>>>
}
const DownloadAppSection: FC<Props> = ({
formik,
setPendingFiles,
uploadingIndexes,
}) => {
const singleUpload = useSingleUpload()
const [uploadingIndexes, setUploadingIndexes] = useState<Set<number>>(new Set())
const addDownloadAppDetail = () => {
const currentDetails = formik.values.downloadAppDetails || []
const newId = Date.now().toString()
@@ -49,18 +47,7 @@ const DownloadAppSection: FC<Props> = ({
const removeDownloadAppDetail = (index: number) => {
const currentDetails = formik.values.downloadAppDetails || []
const itemToRemove = currentDetails[index]
formik.setFieldValue('downloadAppDetails', currentDetails.filter((_, i) => i !== index))
// پاک کردن pending file مربوطه
if (itemToRemove) {
setPendingFiles(prev => {
const newPending = { ...prev }
delete newPending[itemToRemove.id]
return newPending
})
}
}
const updateDownloadAppDetail = (index: number, field: 'pic' | 'url', value: string) => {
@@ -71,20 +58,26 @@ const DownloadAppSection: FC<Props> = ({
formik.setFieldValue('downloadAppDetails', updatedDetails)
}
const handleDownloadAppImageUpload = (index: number, files: File[]) => {
const handleDownloadAppImageUpload = async (index: number, files: File[]) => {
if (files.length === 0) return
const item = formik.values.downloadAppDetails?.[index]
if (!item) return
try {
setUploadingIndexes(prev => new Set(prev).add(index))
// ذخیره فایل برای آپلود بعدی
setPendingFiles(prev => ({
...prev,
[item.id]: files[0]
}))
const uploadResponse = await singleUpload.mutateAsync(files[0])
const imageUrl = uploadResponse.results.url.url
// نمایش preview
updateDownloadAppDetail(index, 'pic', URL.createObjectURL(files[0]))
updateDownloadAppDetail(index, 'pic', imageUrl)
toast.success('تصویر با موفقیت آپلود شد')
} catch {
toast.error('خطا در آپلود تصویر')
} finally {
setUploadingIndexes(prev => {
const newSet = new Set(prev)
newSet.delete(index)
return newSet
})
}
}
return (
+25 -15
View File
@@ -21,9 +21,19 @@ type FormValues = {
type Props = {
formik: FormikProps<FormValues>
logoFile: File[]
setLogoFile: React.Dispatch<React.SetStateAction<File[]>>
postalCodeFile: File[]
setPostalCodeFile: React.Dispatch<React.SetStateAction<File[]>>
}
const FilesSection: FC<Props> = ({ formik }) => {
const FilesSection: FC<Props> = ({
formik,
logoFile,
setLogoFile,
postalCodeFile,
setPostalCodeFile
}) => {
return (
<div className='w-80 space-y-6'>
<div className='text-sm bg-white py-8 px-6 rounded-3xl'>
@@ -35,14 +45,14 @@ const FilesSection: FC<Props> = ({ formik }) => {
<UploadBox
label=""
isMultiple={false}
onChange={(files) => {
if (files.length > 0) {
formik.setFieldValue('siteLogo', URL.createObjectURL(files[0]))
}
}}
onChange={(files) => setLogoFile(files)}
/>
{formik.values.siteLogo && (
<img src={formik.values.siteLogo} alt="لوگو سایت" className="mt-2 w-20 h-20 object-contain rounded" />
{(logoFile.length > 0 || formik.values.siteLogo) && (
<img
src={logoFile.length > 0 ? URL.createObjectURL(logoFile[0]) : formik.values.siteLogo}
alt="لوگو سایت"
className="mt-2 w-20 h-20 object-contain rounded"
/>
)}
</div>
@@ -51,14 +61,14 @@ const FilesSection: FC<Props> = ({ formik }) => {
<UploadBox
label=""
isMultiple={false}
onChange={(files) => {
if (files.length > 0) {
formik.setFieldValue('shopPostalCode', URL.createObjectURL(files[0]))
}
}}
onChange={(files) => setPostalCodeFile(files)}
/>
{formik.values.shopPostalCode && (
<img src={formik.values.shopPostalCode} alt="کد پستی فروشگاه" className="mt-2 w-20 h-20 object-contain rounded" />
{(postalCodeFile.length > 0 || formik.values.shopPostalCode) && (
<img
src={postalCodeFile.length > 0 ? URL.createObjectURL(postalCodeFile[0]) : formik.values.shopPostalCode}
alt="کد پستی فروشگاه"
className="mt-2 w-20 h-20 object-contain rounded"
/>
)}
</div>
</div>
@@ -1,9 +1,11 @@
import { type FC } from 'react'
import { type FC, useState } from 'react'
import type { FormikProps } from 'formik'
import { Add, Trash } from 'iconsax-react'
import Button from '../../../components/Button'
import Input from '../../../components/Input'
import UploadBox from '../../../components/UploadBox'
import { useSingleUpload } from '../../category/hooks/useCategoryData'
import { toast } from 'react-toastify'
type SocialMediaLink = {
id: string
@@ -24,17 +26,13 @@ type FormValues = {
type Props = {
formik: FormikProps<FormValues>
pendingFiles: { [key: string]: File }
setPendingFiles: React.Dispatch<React.SetStateAction<{ [key: string]: File }>>
uploadingIndexes: Set<number>
setUploadingIndexes: React.Dispatch<React.SetStateAction<Set<number>>>
}
const SocialMediaSection: FC<Props> = ({
formik,
setPendingFiles,
uploadingIndexes,
}) => {
const singleUpload = useSingleUpload()
const [uploadingIndexes, setUploadingIndexes] = useState<Set<number>>(new Set())
const addSocialMediaLink = () => {
const currentLinks = formik.values.socialMediaLinks || []
const newId = Date.now().toString()
@@ -43,18 +41,7 @@ const SocialMediaSection: FC<Props> = ({
const removeSocialMediaLink = (index: number) => {
const currentLinks = formik.values.socialMediaLinks || []
const itemToRemove = currentLinks[index]
formik.setFieldValue('socialMediaLinks', currentLinks.filter((_, i) => i !== index))
// پاک کردن pending file مربوطه
if (itemToRemove) {
setPendingFiles(prev => {
const newPending = { ...prev }
delete newPending[itemToRemove.id]
return newPending
})
}
}
const updateSocialMediaLink = (index: number, field: 'url', value: string) => {
@@ -65,20 +52,26 @@ const SocialMediaSection: FC<Props> = ({
formik.setFieldValue('socialMediaLinks', updatedLinks)
}
const handleSocialMediaIconUpload = (index: number, files: File[]) => {
const handleSocialMediaIconUpload = async (index: number, files: File[]) => {
if (files.length === 0) return
const item = formik.values.socialMediaLinks?.[index]
if (!item) return
try {
setUploadingIndexes(prev => new Set(prev).add(index))
// ذخیره فایل برای آپلود بعدی
setPendingFiles(prev => ({
...prev,
[item.id]: files[0]
}))
const uploadResponse = await singleUpload.mutateAsync(files[0])
const imageUrl = uploadResponse.results.url.url
// نمایش preview
updateSocialMediaLinkIcon(index, URL.createObjectURL(files[0]))
updateSocialMediaLinkIcon(index, imageUrl)
toast.success('آیکون با موفقیت آپلود شد')
} catch {
toast.error('خطا در آپلود آیکون')
} finally {
setUploadingIndexes(prev => {
const newSet = new Set(prev)
newSet.delete(index)
return newSet
})
}
}
const updateSocialMediaLinkIcon = (index: number, iconUrl: string) => {