Files
dmail-admin/src/pages/setting/personality/components/GenericSection.tsx
T
2025-08-06 08:49:11 +03:30

345 lines
22 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { FC } from 'react'
import { usePersonalityStore } from '../store/Store'
import { AddCircle } from 'iconsax-react'
import TextRenderer from './TextRenderer'
import ButtonRenderer from './ButtonRenderer'
import ImageRenderer from './ImageRenderer'
import SocialRenderer from './SocialRenderer'
import { VerticalAlignment, HorizontalAlignment } from '../types/Types'
interface GenericSectionProps {
sectionKey: string
}
const GenericSection: FC<GenericSectionProps> = ({ sectionKey }) => {
const { activeSection, activeItemIndex, data, setActiveSection, setActiveItemIndex } = usePersonalityStore()
// Helper function to get numeric height
const getNumericHeight = (height?: string) => {
return parseInt(height?.replace(/px|em|rem|%/g, '') || "123")
}
// Helper function to generate border style object based on selected sides
const generateBorderStyle = (border?: { width?: string; color?: string; style?: string; sides?: string[] }) => {
if (!border || border.style === 'none') {
return {}
}
const { width = '0px', color = '#E5E7EB', style = 'solid', sides = [] } = border
// اگر width صفر باشد یا تعریف نشده باشد، border نمایش نده
if (!width || width === '0px') {
return {}
}
if (sides.length === 0 || sides.length === 4) {
return { border: `${width} ${style} ${color}` }
}
// Generate individual border sides
const borderStyle: any = {}
sides.forEach(side => {
borderStyle[`border${side.charAt(0).toUpperCase() + side.slice(1)}`] = `${width} ${style} ${color}`
})
return borderStyle
}
// Early return if data is not available
if (!data || !data[sectionKey]) {
return (
<tr>
<td style={{ height: '123px', textAlign: 'center', verticalAlign: 'middle' }}>
<div>در حال بارگذاری...</div>
</td>
</tr>
)
}
const sectionData = data[sectionKey]
const handleSectionClick = (section: string, e: React.MouseEvent) => {
// اگر کلیک روی فضای خالی باشد (نه روی content elements)
const target = e.target as HTMLElement;
const isClickOnContent = target.closest('[data-element-type]') !== null;
if (!isClickOnContent) {
setActiveSection(section)
}
}
const handleItemClick = (index: number, e: React.MouseEvent) => {
// Check if click is on a content element (text, button, image, social)
const target = e.target as HTMLElement;
const isClickOnContent = target.closest('[data-element-type]') !== null;
// Only select column if not clicking on content elements
if (!isClickOnContent) {
e.stopPropagation()
// Only set active section if we're not already in this section
if (activeSection !== sectionKey) {
setActiveSection(sectionKey)
}
// Always set the active item index
setActiveItemIndex(index)
}
}
// تابع‌های helper برای تعیین alignment دکمه‌ها
const getButtonHorizontalAlign = (button?: { alignment?: HorizontalAlignment }) => {
if (!button || !button.alignment) return 'center'
switch (button.alignment) {
case HorizontalAlignment.LEFT:
return 'left'
case HorizontalAlignment.RIGHT:
return 'right'
case HorizontalAlignment.CENTER:
default:
return 'center'
}
}
const getButtonVerticalAlign = (button?: { verticalAlignment?: VerticalAlignment }) => {
if (!button || !button.verticalAlignment) return 'middle'
switch (button.verticalAlignment) {
case VerticalAlignment.TOP:
return 'top'
case VerticalAlignment.BOTTOM:
return 'bottom'
case VerticalAlignment.MIDDLE:
default:
return 'middle'
}
}
return (
<tr onClick={(e) => handleSectionClick(sectionKey, e)}>
{
sectionData.columnsCount > 0 ?
<td style={{
padding: '10px',
...(() => {
const borderStyle = generateBorderStyle(sectionData.border)
if (activeSection === sectionKey) {
// اگر سکشن انتخاب شده است، border آبی + border سکشن را نمایش بده
if (Object.keys(borderStyle).length === 0) {
return { border: '1px dashed #0038FF' }
} else {
return {
...borderStyle,
outline: '1px dashed #0038FF',
outlineOffset: '2px'
}
}
} else {
// اگر سکشن انتخاب نشده، فقط border سکشن یا border پیش‌فرض
if (Object.keys(borderStyle).length === 0) {
return { border: '1px dashed #E5E7EB' }
}
return borderStyle
}
})(),
borderRadius: '24px',
cursor: 'pointer',
height: sectionData.height || '123px'
}}>
<table width="100%" cellPadding="0" cellSpacing="0" border={0} style={{ tableLayout: 'fixed' }}>
<tbody>
<tr>
{
Array.from({ length: sectionData.columnsCount }, (_, index) => {
const item = sectionData.items[index];
const isActive = activeSection === sectionKey && activeItemIndex === index;
return (
<>
<td
key={index}
onClick={(e) => handleItemClick(index, e)}
style={{
width: `${100 / sectionData.columnsCount}%`,
height: sectionData.height || '123px',
border: isActive ? '1px dashed #0038FF' : '1px dashed #EAECF4',
backgroundColor: item?.backgroundColor || '#ffffff',
backgroundImage: item?.backgroundImage ? `url(${item.backgroundImage})` : 'none',
backgroundSize: item?.style?.backgroundSize || 'cover',
backgroundRepeat: item?.style?.backgroundRepeat || 'no-repeat',
backgroundPosition: item?.style?.backgroundPosition || 'center',
cursor: 'pointer',
// padding: '8px',
verticalAlign: 'top',
borderRadius: '8px',
position: 'relative'
}}
>
<table width="100%" cellPadding="0" cellSpacing="0" border={0} style={{
borderCollapse: 'collapse',
height: sectionData.height || '123px'
}}>
<tbody>
{/* اگر column خالی است */}
{(!item?.texts || item.texts.length === 0) &&
(!item?.buttons || item.buttons.length === 0) &&
(!item?.images || item.images.length === 0) &&
(!item?.socials || item.socials.length === 0) ? (
<tr>
<td
width="100%"
height={getNumericHeight(sectionData.height).toString()}
style={{
padding: '8px',
cursor: 'pointer'
}}
onClick={(e) => handleItemClick(index, e)}
>
{/* محتوای خالی برای columns خالی */}
</td>
</tr>
) : (
<>
{/* اگر فقط دکمه داریم و متن نداریم */}
{(!item?.texts || item.texts.length === 0) && item?.buttons && item.buttons.length > 0 ? (
<tr>
<td
width="100%"
height={getNumericHeight(sectionData.height).toString()}
align={getButtonHorizontalAlign(item.buttons[0])}
valign={getButtonVerticalAlign(item.buttons[0])}
style={{
padding: '8px'
}}
>
<ButtonRenderer
buttons={item.buttons}
itemId={item?.id || ''}
sectionKey={sectionKey}
/>
</td>
</tr>
) : (
<>
{/* ردیف اصلی برای متن */}
<tr>
<td
width="100%"
height={item?.buttons && item.buttons.length > 0 ?
Math.floor(getNumericHeight(sectionData.height) * 0.6).toString() :
Math.floor(getNumericHeight(sectionData.height) * 0.8).toString()}
align={item?.texts?.[0]?.alignment || 'center'}
valign={item?.texts?.[0]?.verticalAlignment === VerticalAlignment.TOP ? 'top' :
item?.texts?.[0]?.verticalAlignment === VerticalAlignment.BOTTOM ? 'bottom' : 'middle'}
style={{
padding: '8px',
fontSize: '14px',
lineHeight: '1.4'
}}
>
<TextRenderer
texts={item?.texts || []}
itemId={item?.id || ''}
sectionKey={sectionKey}
/>
<ImageRenderer
images={item?.images || []}
itemId={item?.id || ''}
sectionKey={sectionKey}
/>
<SocialRenderer
socials={item?.socials || []}
itemId={item?.id || ''}
sectionKey={sectionKey}
/>
</td>
</tr>
{/* ردیف دکمه‌ها */}
{item?.buttons && item.buttons.length > 0 && (
<tr>
<td
width="100%"
height={Math.floor(getNumericHeight(sectionData.height) * 0.2).toString()}
align={getButtonHorizontalAlign(item.buttons[0])}
valign={getButtonVerticalAlign(item.buttons[0])}
style={{
padding: '0 8px 8px 8px'
}}
>
<ButtonRenderer
buttons={item.buttons}
itemId={item?.id || ''}
sectionKey={sectionKey}
/>
</td>
</tr>
)}
</>
)}
</>
)}
</tbody>
</table>
</td>
{index < sectionData.columnsCount - 1 && (
<td style={{ width: '16px' }}></td>
)}
</>
)
})
}
</tr>
</tbody>
</table>
</td>
:
<td
onClick={(e) => handleSectionClick(sectionKey, e)}
style={{
height: sectionData.height || '123px',
...(() => {
const borderStyle = generateBorderStyle(sectionData.border)
if (activeSection === sectionKey) {
// اگر سکشن انتخاب شده است، border آبی + border سکشن را نمایش بده
if (Object.keys(borderStyle).length === 0) {
return { border: '1px dashed #0038FF' }
} else {
return {
...borderStyle,
outline: '1px dashed #0038FF',
outlineOffset: '2px'
}
}
} else {
// اگر سکشن انتخاب نشده، فقط border سکشن یا border پیش‌فرض
if (Object.keys(borderStyle).length === 0) {
return { border: '1px dashed #E5E7EB' }
}
return borderStyle
}
})(),
borderRadius: '24px',
textAlign: 'center',
verticalAlign: 'middle',
padding: '10px',
cursor: 'pointer'
}}
>
<div style={{ color: '#888', fontSize: '14px', marginBottom: '8px' }}>
{data[sectionKey]?.name || sectionKey}
</div>
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
<AddCircle size={24} color='#888' />
</div>
</td>
}
</tr>
)
}
export default GenericSection