site setting

This commit is contained in:
hamid zarghami
2025-10-04 08:43:16 +03:30
parent 2d52076ac0
commit 7db97eed6d
5 changed files with 160 additions and 32 deletions
+69 -17
View File
@@ -2,7 +2,6 @@ import { type FC, useEffect, useState } from 'react'
import { useFormik } from 'formik'
import * as Yup from 'yup'
import { useGetSiteSetting, useUpdateSiteSetting, useGetShop } from './hooks/useSettingData'
import { uploadSingle } from '../uploader/service/UploaderService'
type DownloadAppDetail = {
id: string
@@ -10,6 +9,12 @@ type DownloadAppDetail = {
url: string
}
type SocialMediaLink = {
id: string
icon: string
url: string
}
type FormValues = {
siteLogo?: string
footerPhone: string
@@ -18,7 +23,7 @@ type FormValues = {
footerDescription: string
shopPostalCode?: string
downloadAppDetails: DownloadAppDetail[]
socialMediaLinks: { icon: string; url: string }[]
socialMediaLinks: SocialMediaLink[]
}
import Button from '../../components/Button'
import { TickCircle } from 'iconsax-react'
@@ -28,6 +33,7 @@ import DownloadAppSection from './components/DownloadAppSection'
import SocialMediaSection from './components/SocialMediaSection'
import { toast } from 'react-toastify'
import { extractErrorMessage } from '../../helpers/utils'
import { useSingleUpload } from '../category/hooks/useCategoryData'
const validationSchema = Yup.object().shape({
footerPhone: Yup.string().trim().required('تلفن فوتر الزامی است'),
@@ -41,12 +47,14 @@ const validationSchema = Yup.object().shape({
})
const SiteSetting: FC = () => {
const { data, isLoading } = useGetSiteSetting()
const { data: shopData } = 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 formik = useFormik<FormValues>({
initialValues: {
@@ -57,15 +65,28 @@ const SiteSetting: FC = () => {
footerDescription: '',
shopPostalCode: '',
downloadAppDetails: [],
socialMediaLinks: []
socialMediaLinks: [] as SocialMediaLink[]
},
validationSchema,
validateOnChange: false,
validateOnBlur: false,
onSubmit: async (values) => {
let finalDownloadAppDetails = values.downloadAppDetails
let finalSocialMediaLinks = values.socialMediaLinks
// اول همه فایل‌های pending رو آپلود کن
if (Object.keys(pendingFiles).length > 0) {
await uploadAllPendingFiles()
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({})
}
@@ -73,9 +94,13 @@ const SiteSetting: FC = () => {
// transform data برای ارسال به API (حذف id)
const transformedValues = {
...values,
downloadAppDetails: values.downloadAppDetails.map((item) => ({
downloadAppDetails: finalDownloadAppDetails.map((item) => ({
pic: item.pic,
url: item.url
})),
socialMediaLinks: finalSocialMediaLinks.map((item) => ({
icon: item.icon,
url: item.url
}))
}
@@ -107,7 +132,11 @@ const SiteSetting: FC = () => {
pic: item.pic,
url: item.url
})),
socialMediaLinks: data.results.siteSetting.socialMediaLinks || []
socialMediaLinks: (data.results.siteSetting.socialMediaLinks || []).map((item: { icon: string; url: string }, index: number) => ({
id: `existing-${index}-${Date.now()}`,
icon: item.icon,
url: item.url
}))
}, false)
setIsDataLoaded(true)
}
@@ -119,27 +148,44 @@ const SiteSetting: FC = () => {
const uploadedUrls: { [key: string]: string } = {}
for (const [id, file] of Object.entries(pendingFiles)) {
// پیدا کردن index آیتم بر اساس id
const index = formik.values.downloadAppDetails?.findIndex(item => item.id === id)
// پیدا کردن 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 uploadSingle(file)
const imageUrl = uploadResponse.results.url
const uploadResponse = await singleUpload.mutateAsync(file)
const imageUrl = uploadResponse.results.url.url
uploadedUrls[id] = imageUrl
// بروزرسانی formik
const currentDetails = formik.values.downloadAppDetails || []
const updatedDetails = currentDetails.map((item, i) =>
i === index ? { ...item, pic: imageUrl } : item
)
formik.setFieldValue('downloadAppDetails', updatedDetails)
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) {
toast.error(`خطا در آپلود تصویر آیتم ${index + 1}`)
const itemType = isDownloadApp ? 'تصویر دانلود اپ' : 'آیکون رسانه اجتماعی'
toast.error(`خطا در آپلود ${itemType} آیتم ${index + 1}`)
throw error
} finally {
setUploadingIndexes(prev => {
@@ -192,7 +238,13 @@ const SiteSetting: FC = () => {
setUploadingIndexes={setUploadingIndexes}
/>
<SocialMediaSection formik={formik} />
<SocialMediaSection
formik={formik}
pendingFiles={pendingFiles}
setPendingFiles={setPendingFiles}
uploadingIndexes={uploadingIndexes}
setUploadingIndexes={setUploadingIndexes}
/>
</div>
)
}
@@ -11,6 +11,12 @@ type DownloadAppDetail = {
url: string
}
type SocialMediaLink = {
id: string
icon: string
url: string
}
type FormValues = {
siteLogo?: string
footerPhone: string
@@ -19,7 +25,7 @@ type FormValues = {
footerDescription: string
shopPostalCode?: string
downloadAppDetails: DownloadAppDetail[]
socialMediaLinks: { icon: string; url: string }[]
socialMediaLinks: SocialMediaLink[]
}
type Props = {
@@ -32,10 +38,8 @@ type Props = {
const DownloadAppSection: FC<Props> = ({
formik,
pendingFiles,
setPendingFiles,
uploadingIndexes,
setUploadingIndexes
}) => {
const addDownloadAppDetail = () => {
const currentDetails = formik.values.downloadAppDetails || []
@@ -2,6 +2,12 @@ import { type FC } from 'react'
import type { FormikProps } from 'formik'
import UploadBox from '../../../components/UploadBox'
type SocialMediaLink = {
id: string
icon: string
url: string
}
type FormValues = {
siteLogo?: string
footerPhone: string
@@ -10,7 +16,7 @@ type FormValues = {
footerDescription: string
shopPostalCode?: string
downloadAppDetails: { id: string; pic: string; url: string }[]
socialMediaLinks: { icon: string; url: string }[]
socialMediaLinks: SocialMediaLink[]
}
type Props = {
@@ -4,6 +4,12 @@ import Input from '../../../components/Input'
import Textarea from '../../../components/Textarea'
import TextEditor from '../../../components/TextEditor'
type SocialMediaLink = {
id: string
icon: string
url: string
}
type FormValues = {
siteLogo?: string
footerPhone: string
@@ -12,7 +18,7 @@ type FormValues = {
footerDescription: string
shopPostalCode?: string
downloadAppDetails: { id: string; pic: string; url: string }[]
socialMediaLinks: { icon: string; url: string }[]
socialMediaLinks: SocialMediaLink[]
}
type Props = {
@@ -3,6 +3,13 @@ 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'
type SocialMediaLink = {
id: string
icon: string
url: string
}
type FormValues = {
siteLogo?: string
@@ -12,25 +19,45 @@ type FormValues = {
footerDescription: string
shopPostalCode?: string
downloadAppDetails: { id: string; pic: string; url: string }[]
socialMediaLinks: { icon: string; url: string }[]
socialMediaLinks: SocialMediaLink[]
}
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 }) => {
const SocialMediaSection: FC<Props> = ({
formik,
setPendingFiles,
uploadingIndexes,
}) => {
const addSocialMediaLink = () => {
const currentLinks = formik.values.socialMediaLinks || []
formik.setFieldValue('socialMediaLinks', [...currentLinks, { icon: '', url: '' }])
const newId = Date.now().toString()
formik.setFieldValue('socialMediaLinks', [...currentLinks, { id: newId, icon: '', url: '' }])
}
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: 'icon' | 'url', value: string) => {
const updateSocialMediaLink = (index: number, field: 'url', value: string) => {
const currentLinks = formik.values.socialMediaLinks || []
const updatedLinks = currentLinks.map((item, i) =>
i === index ? { ...item, [field]: value } : item
@@ -38,6 +65,30 @@ const SocialMediaSection: FC<Props> = ({ formik }) => {
formik.setFieldValue('socialMediaLinks', updatedLinks)
}
const handleSocialMediaIconUpload = (index: number, files: File[]) => {
if (files.length === 0) return
const item = formik.values.socialMediaLinks?.[index]
if (!item) return
// ذخیره فایل برای آپلود بعدی
setPendingFiles(prev => ({
...prev,
[item.id]: files[0]
}))
// نمایش preview
updateSocialMediaLinkIcon(index, URL.createObjectURL(files[0]))
}
const updateSocialMediaLinkIcon = (index: number, iconUrl: string) => {
const currentLinks = formik.values.socialMediaLinks || []
const updatedLinks = currentLinks.map((item, i) =>
i === index ? { ...item, icon: iconUrl } : item
)
formik.setFieldValue('socialMediaLinks', updatedLinks)
}
return (
<div className='mt-6 bg-white py-8 xl:px-10 px-6 rounded-3xl'>
<div className='flex justify-between items-center mb-6'>
@@ -63,12 +114,21 @@ const SocialMediaSection: FC<Props> = ({ formik }) => {
</Button>
</div>
<div className="space-y-4">
<Input
label="آیکون"
value={item.icon}
onChange={(e) => updateSocialMediaLink(index, 'icon', e.target.value)}
placeholder="مثال: telegram"
/>
<div>
<label className="block text-sm font-medium mb-2">آیکون</label>
<UploadBox
label=""
isMultiple={false}
onChange={(files) => handleSocialMediaIconUpload(index, files)}
isReset={false}
/>
{item.icon && (
<img src={item.icon} alt={`آیکون ${index + 1}`} className="mt-2 w-10 h-10 object-contain rounded" />
)}
{uploadingIndexes.has(index) && (
<div className="mt-2 text-sm text-blue-600">در حال آپلود...</div>
)}
</div>
<Input
label="لینک"
value={item.url}