screen shot test

This commit is contained in:
hamid zarghami
2025-07-16 12:24:51 +03:30
parent 9cdd5de94c
commit fe5b7de9b1
8 changed files with 309 additions and 47 deletions
+47 -46
View File
@@ -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,
};
};