site setting
This commit is contained in:
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user