set select fix + screen shot fix
This commit is contained in:
@@ -8,7 +8,7 @@ type Props = {
|
|||||||
|
|
||||||
const Radio: FC<Props> = (props: Props) => {
|
const Radio: FC<Props> = (props: Props) => {
|
||||||
return (
|
return (
|
||||||
<div onClick={() => props.onChange(props.value)} className='size-4 cursor-pointer rounded-full bg-[#EAEDF5] flex justify-center items-center'>
|
<div onClick={() => props.onChange(props.value)} className='size-4 min-w-4 cursor-pointer rounded-full bg-[#EAEDF5] flex justify-center items-center'>
|
||||||
{
|
{
|
||||||
props.isActive &&
|
props.isActive &&
|
||||||
<div className='size-2 bg-black rounded-full'></div>
|
<div className='size-2 bg-black rounded-full'></div>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import Button from '@/components/Button'
|
import Button from '@/components/Button'
|
||||||
import { AddCircle } from 'iconsax-react'
|
import { AddCircle } from 'iconsax-react'
|
||||||
import { FC } from 'react'
|
import { FC, useState, useEffect } from 'react'
|
||||||
import { useGetTemplates } from './hooks/usePersonalityData'
|
import { useGetTemplates } from './hooks/usePersonalityData'
|
||||||
import { Link } from 'react-router-dom'
|
import { Link } from 'react-router-dom'
|
||||||
import { Paths } from '@/utils/Paths'
|
import { Paths } from '@/utils/Paths'
|
||||||
@@ -9,12 +9,23 @@ import Templete from './components/Templete'
|
|||||||
|
|
||||||
const List: FC = () => {
|
const List: FC = () => {
|
||||||
const { data } = useGetTemplates()
|
const { data } = useGetTemplates()
|
||||||
|
const [optimisticSelectedId, setOptimisticSelectedId] = useState<string | null>(null)
|
||||||
|
|
||||||
// پیدا کردن template انتخاب شده
|
// پیدا کردن template انتخاب شده از سرور
|
||||||
const selectedTemplateId = data?.data?.templates?.find(
|
const serverSelectedTemplateId = data?.data?.templates?.find(
|
||||||
(template: TemplateResponseType) => template.selected
|
(template: TemplateResponseType) => template.selected
|
||||||
)?.id
|
)?.id
|
||||||
|
|
||||||
|
// استفاده از optimistic state یا server state
|
||||||
|
const selectedTemplateId = optimisticSelectedId || serverSelectedTemplateId
|
||||||
|
|
||||||
|
// وقتی دادههای جدید از سرور میآید، optimistic state را پاک کن
|
||||||
|
useEffect(() => {
|
||||||
|
if (data?.data?.templates) {
|
||||||
|
setOptimisticSelectedId(null)
|
||||||
|
}
|
||||||
|
}, [data])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='mt-2 md:mt-4 px-2 md:px-0'>
|
<div className='mt-2 md:mt-4 px-2 md:px-0'>
|
||||||
<div className='flex justify-between items-center'>
|
<div className='flex justify-between items-center'>
|
||||||
@@ -41,10 +52,14 @@ const List: FC = () => {
|
|||||||
key={item.id}
|
key={item.id}
|
||||||
item={item}
|
item={item}
|
||||||
selectedTemplateId={selectedTemplateId}
|
selectedTemplateId={selectedTemplateId}
|
||||||
|
onOptimisticSelect={setOptimisticSelectedId}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
<div className='flex-1 rounded-3xl px-4'></div>
|
||||||
|
<div className='flex-1 rounded-3xl px-4'></div>
|
||||||
|
<div className='flex-1 rounded-3xl px-4'></div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ import ModalConfrim from '@/components/ModalConfrim'
|
|||||||
const Templete: FC<{
|
const Templete: FC<{
|
||||||
item: TemplateResponseType
|
item: TemplateResponseType
|
||||||
selectedTemplateId?: string
|
selectedTemplateId?: string
|
||||||
}> = ({ item, selectedTemplateId }) => {
|
onOptimisticSelect?: (id: string | null) => void
|
||||||
|
}> = ({ item, selectedTemplateId, onOptimisticSelect }) => {
|
||||||
|
|
||||||
const [isActive, setIsActive] = useState(item.selected)
|
const [isActive, setIsActive] = useState(item.selected)
|
||||||
const [isShowDeleteModal, setIsShowDeleteModal] = useState(false)
|
const [isShowDeleteModal, setIsShowDeleteModal] = useState(false)
|
||||||
@@ -19,7 +20,16 @@ const Templete: FC<{
|
|||||||
const { mutate: deleteTemplate, isPending: isDeleting } = useDeleteTemplate()
|
const { mutate: deleteTemplate, isPending: isDeleting } = useDeleteTemplate()
|
||||||
|
|
||||||
const handleChangeSelectedTemplate = (id: string) => {
|
const handleChangeSelectedTemplate = (id: string) => {
|
||||||
|
// Optimistic update - بلافاصله UI را بهروزرسانی کن
|
||||||
|
setIsActive(true)
|
||||||
|
onOptimisticSelect?.(id)
|
||||||
|
|
||||||
setSelectedTemplate(id, {
|
setSelectedTemplate(id, {
|
||||||
|
onError: () => {
|
||||||
|
// اگر خطا رخ داد، state را به حالت قبلی برگردان
|
||||||
|
setIsActive(item.selected)
|
||||||
|
onOptimisticSelect?.(null)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,7 +52,9 @@ const Templete: FC<{
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div key={item.id} className='flex-1 bg-white rounded-3xl p-4'>
|
<div key={item.id} className='flex-1 bg-white rounded-3xl p-4'>
|
||||||
<div className='h-[140px] bg-gray-200 rounded-3xl'></div>
|
<div className='h-[140px] bg-gray-200 rounded-3xl'>
|
||||||
|
<img src={item.thumbnailUrl} alt={item.name} className='w-full h-full object-contain' />
|
||||||
|
</div>
|
||||||
<div className='mt-4 flex justify-between items-center'>
|
<div className='mt-4 flex justify-between items-center'>
|
||||||
<div className='flex gap-2 items-center'>
|
<div className='flex gap-2 items-center'>
|
||||||
<Radio isActive={isActive} onChange={handleChangeSelectedTemplate} value={item.id} />
|
<Radio isActive={isActive} onChange={handleChangeSelectedTemplate} value={item.id} />
|
||||||
|
|||||||
@@ -20,8 +20,13 @@ export const useGetTemplates = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const useSetSelectedTemplate = () => {
|
export const useSetSelectedTemplate = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: (id: string) => api.setSelectedTemplate(id),
|
mutationFn: (id: string) => api.setSelectedTemplate(id),
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["templates"] });
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -316,6 +316,7 @@ export type TemplateResponseType = {
|
|||||||
currentVersion: string;
|
currentVersion: string;
|
||||||
selected: boolean;
|
selected: boolean;
|
||||||
business: BusinessType;
|
business: BusinessType;
|
||||||
|
thumbnailUrl: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PagerType = {
|
export type PagerType = {
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ export const captureScreenshotAsFile = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
const dataUrl = await domtoimage.toPng(element, options);
|
const dataUrl = await domtoimage.toPng(element, options);
|
||||||
|
|
||||||
// بازگردانی استایلهای اصلی
|
// بازگردانی استایلهای اصلی
|
||||||
htmlElement.style.transform = originalTransform;
|
htmlElement.style.transform = originalTransform;
|
||||||
htmlElement.style.position = originalPosition;
|
htmlElement.style.position = originalPosition;
|
||||||
|
|||||||
Reference in New Issue
Block a user