screen shot test
This commit is contained in:
@@ -13,26 +13,28 @@ import Button from '@/components/Button'
|
||||
import { TickCircle } from 'iconsax-react'
|
||||
import { usePersonalityStore } from './personality/store/Store'
|
||||
import { exportPersonalityToHTML } from './personality/utils/ExportHTML'
|
||||
import { capturePersonalityScreenshot } from './personality/utils/screenshotHelper'
|
||||
import Input from '@/components/Input'
|
||||
import { useSaveTemplate, useUpdateTemplate, useGetTemplate } from './personality/hooks/usePersonalityData'
|
||||
import { useSaveTemplate, useUpdateTemplate, useGetTemplate, useSingleUpload } from './personality/hooks/usePersonalityData'
|
||||
import { toast } from '@/components/Toast'
|
||||
|
||||
const Setting: FC = () => {
|
||||
|
||||
const { t } = useTranslation()
|
||||
const { id } = useParams()
|
||||
const { data } = usePersonalityStore()
|
||||
const location = useLocation()
|
||||
const [activeTab, setActiveTab] = useState<SettingTabEnum>(SettingTabEnum.SETTING_DOMAIN)
|
||||
const [templateName, setTemplateName] = useState('')
|
||||
const [isCapturingScreenshot, setIsCapturingScreenshot] = useState(false)
|
||||
|
||||
const { mutate: saveTemplate, isPending: isSaving } = useSaveTemplate()
|
||||
const { mutate: updateTemplate, isPending: isUpdating } = useUpdateTemplate()
|
||||
const { data: template } = useGetTemplate(id || '')
|
||||
const { mutateAsync: singleUpload, isPending: isUploading } = useSingleUpload()
|
||||
|
||||
const isEditMode = !!id
|
||||
const isLoading = isSaving || isUpdating
|
||||
const isLoading = isSaving || isUpdating || isUploading || isCapturingScreenshot
|
||||
|
||||
// پر کردن نام قالب در حالت edit
|
||||
useEffect(() => {
|
||||
if (isEditMode && template?.data?.template?.name) {
|
||||
setTemplateName(template.data.template.name)
|
||||
@@ -52,7 +54,6 @@ const Setting: FC = () => {
|
||||
case Paths.settingSignature:
|
||||
return SettingTabEnum.SETTING_SIGNATURE
|
||||
default:
|
||||
// بررسی personality با id
|
||||
if (pathname.startsWith(Paths.settingPersonality + '/')) {
|
||||
return SettingTabEnum.SETTING_PERSONALITY
|
||||
}
|
||||
@@ -60,9 +61,6 @@ const Setting: FC = () => {
|
||||
}
|
||||
}
|
||||
|
||||
// Map setting tabs to URL paths
|
||||
|
||||
// Update active tab when URL changes
|
||||
useEffect(() => {
|
||||
const tabFromPath = getTabFromPath(location.pathname)
|
||||
setActiveTab(tabFromPath)
|
||||
@@ -80,7 +78,7 @@ const Setting: FC = () => {
|
||||
return <Signture />;
|
||||
case SettingTabEnum.SETTING_PERSONALITY:
|
||||
return (
|
||||
<div className='flex xl:flex-row-reverse gap-2 md:gap-6 mt-6 md:mt-8'>
|
||||
<div className='flex xl:flex-row-reverse gap-2 md:gap-6 mt-6 md:mt-8'>
|
||||
<div className="xl:w-auto">
|
||||
<PersonalitySidebar />
|
||||
</div>
|
||||
@@ -100,16 +98,31 @@ const Setting: FC = () => {
|
||||
return
|
||||
}
|
||||
|
||||
if (activeTab !== SettingTabEnum.SETTING_PERSONALITY) {
|
||||
return
|
||||
}
|
||||
|
||||
const html = await exportPersonalityToHTML(data)
|
||||
|
||||
setIsCapturingScreenshot(true)
|
||||
const screenshotFile = await capturePersonalityScreenshot(templateName)
|
||||
setIsCapturingScreenshot(false)
|
||||
|
||||
if (!screenshotFile) {
|
||||
toast('خطا در گرفتن اسکرینشات', 'error')
|
||||
return
|
||||
}
|
||||
|
||||
const thumbnailUrl = await singleUpload(screenshotFile)
|
||||
|
||||
if (isEditMode) {
|
||||
// حالت edit - استفاده از updateTemplate
|
||||
updateTemplate({
|
||||
id: id!,
|
||||
params: {
|
||||
name: templateName,
|
||||
rawHtml: html,
|
||||
structure: data
|
||||
structure: data,
|
||||
thumbnailUrl: thumbnailUrl?.data?.url
|
||||
}
|
||||
}, {
|
||||
onSuccess: (data) => {
|
||||
@@ -120,11 +133,11 @@ const Setting: FC = () => {
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// حالت جدید - استفاده از saveTemplate
|
||||
saveTemplate({
|
||||
name: templateName,
|
||||
rawHtml: html,
|
||||
structure: data
|
||||
structure: data,
|
||||
thumbnailUrl: thumbnailUrl?.data?.url
|
||||
}, {
|
||||
onSuccess: (data) => {
|
||||
toast(data?.data?.message || 'قالب با موفقیت ایجاد شد', 'success')
|
||||
@@ -136,43 +149,31 @@ const Setting: FC = () => {
|
||||
}
|
||||
}
|
||||
|
||||
// const handleExportHTML = async () => {
|
||||
// try {
|
||||
// const html = await exportPersonalityToHTML(data)
|
||||
// const success = await copyHTMLToClipboard(html)
|
||||
|
||||
// if (success) {
|
||||
// toast(t('export_success'), 'success')
|
||||
// } else {
|
||||
// toast(t('export_error'), 'error')
|
||||
// }
|
||||
// } catch {
|
||||
// toast(t('export_error'), 'error')
|
||||
// }
|
||||
// }
|
||||
|
||||
return (
|
||||
<div className='mt-2 md:mt-4 px-2 md:px-0'>
|
||||
<div className='flex justify-between items-center'>
|
||||
<h1 className='text-lg mb-4 md:mb-0'>{t('setting.title')}</h1>
|
||||
<div className='flex gap-2'>
|
||||
<Input
|
||||
placeholder='نام قالب'
|
||||
value={templateName}
|
||||
onChange={(e) => setTemplateName(e.target.value)}
|
||||
/>
|
||||
<Button
|
||||
variant='secondary'
|
||||
className='border border-black w-fit px-7'
|
||||
onClick={handleSave}
|
||||
loading={isLoading}
|
||||
>
|
||||
<div className='flex items-center gap-2'>
|
||||
<TickCircle size={18} color='black' />
|
||||
{isEditMode ? 'بروزرسانی' : t('save')}
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
{
|
||||
activeTab === SettingTabEnum.SETTING_PERSONALITY &&
|
||||
<div className='flex gap-2'>
|
||||
<Input
|
||||
placeholder='نام قالب'
|
||||
value={templateName}
|
||||
onChange={(e) => setTemplateName(e.target.value)}
|
||||
/>
|
||||
<Button
|
||||
variant='secondary'
|
||||
className='border border-black w-fit px-7'
|
||||
onClick={handleSave}
|
||||
loading={isLoading}
|
||||
>
|
||||
<div className='flex items-center gap-2'>
|
||||
<TickCircle size={18} color='black' />
|
||||
{isEditMode ? 'بروزرسانی' : t('save')}
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
|
||||
@@ -18,7 +18,7 @@ const Personality: FC = () => {
|
||||
}, [id, template, setData])
|
||||
|
||||
return (
|
||||
<div className='flex-1 bg-white rounded-4xl p-8'>
|
||||
<div className='flex-1 bg-white rounded-4xl p-8 personality-template'>
|
||||
<table width="100%" cellPadding="0" cellSpacing="0" border={0} style={{ borderCollapse: 'collapse' }}>
|
||||
<tbody>
|
||||
<HeaderSection />
|
||||
|
||||
@@ -50,3 +50,9 @@ export const useDeleteTemplate = () => {
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const useSingleUpload = () => {
|
||||
return useMutation({
|
||||
mutationFn: (file: File) => api.singleUpload(file),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import axios from "@/config/axios";
|
||||
import { TemplateType, TemplatesResponseType } from "../types/Types";
|
||||
import axiosDanak from "@/config/axiosDanak";
|
||||
|
||||
export const saveTemplate = async (params: TemplateType) => {
|
||||
const { data } = await axios.post("/templates", params);
|
||||
@@ -36,3 +37,12 @@ export const deleteTemplate = async (id: string) => {
|
||||
const { data } = await axios.delete(`/templates/${id}`);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const singleUpload = async (file: File) => {
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
|
||||
const { data } = await axiosDanak.post("/uploader/single-file", formData);
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
@@ -269,6 +269,7 @@ export type TemplateType = {
|
||||
name: string;
|
||||
rawHtml: string;
|
||||
structure: PersonalityDataType;
|
||||
thumbnailUrl: string;
|
||||
};
|
||||
|
||||
export type GetTemplateResponse = {
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
import domtoimage from "dom-to-image";
|
||||
|
||||
/**
|
||||
* گرفتن اسکرینشات با dom-to-image و برگرداندن فایل برای آپلود
|
||||
*/
|
||||
export const captureScreenshotAsFile = async (
|
||||
templateName: string
|
||||
): Promise<File | null> => {
|
||||
try {
|
||||
console.log("📸 Starting screenshot capture for upload...");
|
||||
|
||||
const element = await findAndValidateElement();
|
||||
if (!element) {
|
||||
console.error("❌ Element not found");
|
||||
return null;
|
||||
}
|
||||
|
||||
// آمادهسازی element
|
||||
const htmlElement = element as HTMLElement;
|
||||
const originalTransform = htmlElement.style.transform;
|
||||
const originalPosition = htmlElement.style.position;
|
||||
|
||||
htmlElement.style.transform = "scale(1)";
|
||||
htmlElement.style.position = "relative";
|
||||
|
||||
// صبر برای render کامل
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
|
||||
const options = {
|
||||
quality: 1,
|
||||
bgcolor: "#ffffff",
|
||||
width: element.offsetWidth,
|
||||
height: element.offsetHeight,
|
||||
style: {
|
||||
transform: "scale(1)",
|
||||
transformOrigin: "top left",
|
||||
},
|
||||
};
|
||||
|
||||
const dataUrl = await domtoimage.toPng(element, options);
|
||||
|
||||
// بازگردانی استایلهای اصلی
|
||||
htmlElement.style.transform = originalTransform;
|
||||
htmlElement.style.position = originalPosition;
|
||||
|
||||
// تبدیل dataURL به File
|
||||
const response = await fetch(dataUrl);
|
||||
const blob = await response.blob();
|
||||
const file = new File([blob], `${templateName}-template.png`, {
|
||||
type: "image/png",
|
||||
});
|
||||
|
||||
console.log("✅ Screenshot file created successfully:", file);
|
||||
return file;
|
||||
} catch (error) {
|
||||
console.error("❌ Screenshot capture failed:", error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* تابع اصلی - استفاده از dom-to-image و برگرداندن فایل
|
||||
*/
|
||||
export const capturePersonalityScreenshot = async (
|
||||
templateName: string
|
||||
): Promise<File | null> => {
|
||||
console.log("🎯 Starting screenshot capture...");
|
||||
|
||||
try {
|
||||
const screenshotFile = await captureScreenshotAsFile(templateName);
|
||||
if (screenshotFile) {
|
||||
console.log("✅ Screenshot captured successfully");
|
||||
return screenshotFile;
|
||||
} else {
|
||||
console.error("❌ Screenshot capture failed");
|
||||
return null;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("❌ Screenshot error:", error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* پیدا کردن و بررسی element
|
||||
*/
|
||||
const findAndValidateElement = async (): Promise<HTMLElement | null> => {
|
||||
console.log("🔍 Looking for personality element...");
|
||||
|
||||
const selectors = [
|
||||
".personality-template",
|
||||
'[class*="personality-template"]',
|
||||
".flex-1.bg-white.rounded-4xl.p-8",
|
||||
".bg-white.rounded-4xl.p-8",
|
||||
'div[class*="bg-white"][class*="rounded-4xl"]',
|
||||
];
|
||||
|
||||
for (let i = 0; i < selectors.length; i++) {
|
||||
const selector = selectors[i];
|
||||
console.log(`🔍 Trying selector ${i + 1}/${selectors.length}: ${selector}`);
|
||||
|
||||
const elements = document.querySelectorAll(selector);
|
||||
console.log(` Found ${elements.length} elements`);
|
||||
|
||||
for (let j = 0; j < elements.length; j++) {
|
||||
const element = elements[j] as HTMLElement;
|
||||
|
||||
const rect = element.getBoundingClientRect();
|
||||
const isVisible = rect.width > 0 && rect.height > 0;
|
||||
const hasContent = element.innerHTML.trim().length > 0;
|
||||
|
||||
console.log(` Element ${j + 1}:`, {
|
||||
visible: isVisible,
|
||||
hasContent,
|
||||
dimensions: `${rect.width}x${rect.height}`,
|
||||
className: element.className.slice(0, 50) + "...",
|
||||
});
|
||||
|
||||
if (isVisible && hasContent) {
|
||||
console.log(`✅ Found valid element with selector: ${selector}`);
|
||||
return element;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log("❌ No valid elements found");
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* تابع debug برای بررسی element
|
||||
*/
|
||||
export const debugPersonalityElement = () => {
|
||||
console.log("🔍 === DEBUG: Personality Element Detection ===");
|
||||
|
||||
const selectors = [
|
||||
".personality-template",
|
||||
'[class*="personality-template"]',
|
||||
".flex-1.bg-white.rounded-4xl.p-8",
|
||||
".bg-white.rounded-4xl",
|
||||
"table",
|
||||
];
|
||||
|
||||
let foundElements = 0;
|
||||
|
||||
selectors.forEach((selector, index) => {
|
||||
const elements = document.querySelectorAll(selector);
|
||||
console.log(`${index + 1}. "${selector}": ${elements.length} elements`);
|
||||
|
||||
elements.forEach((el, elIndex) => {
|
||||
const htmlEl = el as HTMLElement;
|
||||
const rect = htmlEl.getBoundingClientRect();
|
||||
|
||||
console.log(` [${elIndex + 1}]`, {
|
||||
visible: rect.width > 0 && rect.height > 0,
|
||||
size: `${rect.width}x${rect.height}`,
|
||||
hasContent: htmlEl.innerHTML.trim().length > 0,
|
||||
className: htmlEl.className.slice(0, 40) + "...",
|
||||
});
|
||||
|
||||
if (rect.width > 0 && rect.height > 0) {
|
||||
foundElements++;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
console.log(`📊 Summary: ${foundElements} visible elements found`);
|
||||
console.log("=========================================");
|
||||
|
||||
return {
|
||||
found: foundElements > 0,
|
||||
totalElements: foundElements,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user