Remove some comments

This commit is contained in:
hamid zarghami
2025-08-04 16:37:00 +03:30
parent 4433c4ce1b
commit af935ba2b5
15 changed files with 656 additions and 129 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
VITE_TOKEN_NAME = 'dsc_token'
VITE_REFRESH_TOKEN_NAME = 'dsc_refresh_token'
VITE_DANAK_BASE_URL ='https://api.danakcorp.com'
# VITE_BASE_URL = 'http://192.168.1.113:4000'
# VITE_BASE_URL = 'http://192.168.1.117:4000'
VITE_BASE_URL = 'https://dmail-api.danakcorp.com'
VITE_SERVICE_ID = 'e51afdc3-ea0b-49cf-8f49-2a6f131b024e'
@@ -88,6 +88,7 @@ const AddressForm: FC<AddressFormProps> = ({ selectedAddress, isEditMode, onCanc
password: params.password || undefined,
quota: params.quotaMB ? params.quotaMB * 1024 * 1024 : undefined,
forwarders: forwarders,
templateId: params.templateId,
}
updateAddress({ id: selectedAddress.id, params: updateParams }, {
onSuccess: (data) => {
+1
View File
@@ -15,6 +15,7 @@ export type UpdateAddressType = {
aliases?: string[];
title?: string;
forwarders?: string[];
templateId?: string;
};
export interface AddressData extends Record<string, unknown> {
+65 -4
View File
@@ -2,13 +2,16 @@ import { FC, useEffect } from 'react'
import HeaderSection from './components/HeaderSection'
import ContentSection from './components/ContentSection'
import FooterSection from './components/FooterSection'
import GenericSection from './components/GenericSection'
import { useParams } from 'react-router-dom'
import { usePersonalityStore } from './store/Store'
import { useGetTemplate } from './hooks/usePersonalityData'
import { Add } from 'iconsax-react'
import { toast } from '@/components/Toast'
const Personality: FC = () => {
const { id } = useParams()
const { setData } = usePersonalityStore()
const { setData, data, addSection } = usePersonalityStore()
const { data: template } = useGetTemplate(id || '')
useEffect(() => {
@@ -17,13 +20,71 @@ const Personality: FC = () => {
}
}, [id, template, setData])
const handleAddSection = (afterSectionKey: string) => {
addSection(afterSectionKey)
toast('سکشن جدید با موفقیت اضافه شد', 'success')
}
const renderSection = (sectionKey: string) => {
if (sectionKey === 'header') {
return <HeaderSection key={sectionKey} />
} else if (sectionKey === 'content') {
return <ContentSection key={sectionKey} />
} else if (sectionKey === 'footer') {
return <FooterSection key={sectionKey} />
}
// For custom sections, use GenericSection with full functionality
return <GenericSection key={sectionKey} sectionKey={sectionKey} />
}
const renderPlusButton = (afterSectionKey: string) => {
return (
<tr key={`plus-${afterSectionKey}`} style={{ position: 'relative', height: '0px' }}>
<td style={{ position: 'relative', height: '0px' }}>
<div
className="absolute left-1/2 transform -translate-x-1/2 -translate-y-1/2"
style={{ top: '0px', zIndex: 9999 }}
>
<button
onClick={() => handleAddSection(afterSectionKey)}
className="group flex items-center justify-center w-10 h-10 rounded-full border-2 border-dashed border-gray-300 hover:border-blue-500 hover:bg-blue-50 bg-white transition-all duration-200 shadow-sm hover:shadow-md"
title="اضافه کردن سکشن جدید"
style={{ position: 'relative', zIndex: 9999 }}
>
<Add
size={20}
color='gray'
className="text-gray-400 group-hover:text-blue-500 transition-colors"
/>
</button>
</div>
</td>
</tr>
)
}
return (
<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 />
<ContentSection />
<FooterSection />
{Object.keys(data).map((sectionKey, index) => {
const sections = Object.keys(data);
const elements = [];
// Add the section
elements.push(renderSection(sectionKey));
// Add plus button after each section except the last one
if (index < sections.length - 1) {
elements.push(renderPlusButton(sectionKey));
}
return elements;
}).flat()}
{/* Plus button at the end */}
{Object.keys(data).length > 0 && renderPlusButton(Object.keys(data)[Object.keys(data).length - 1])}
</tbody>
</table>
</div>
+2 -2
View File
@@ -9,7 +9,7 @@ import ImageSideBar from './components/ImageSideBar'
import SocialSidebar from './components/SocialSidebar'
import { clx } from '@/helpers/utils'
import { usePersonalityStore } from './store/Store'
import { ElementType, SectionName } from './types/Types'
import { ElementType } from './types/Types'
const PersonalitySidebar: FC = () => {
@@ -94,7 +94,7 @@ const PersonalitySidebar: FC = () => {
className='cursor-pointer'
/>
{
activeSection !== SectionName.CONTENT && <>
activeSection !== 'content' && <>
<Text
size={22}
color={'black'}
@@ -1,5 +1,5 @@
import { FC } from 'react'
import { ButtonType, ButtonSize, HorizontalAlignment, VerticalAlignment, ElementType, PersonalityDataType } from '../types/Types'
import { ButtonType, ButtonSize, HorizontalAlignment, VerticalAlignment, ElementType, PersonalityDataType, SectionName } from '../types/Types'
import { usePersonalityStore } from '../store/Store'
interface ButtonRendererProps {
@@ -22,7 +22,7 @@ const ButtonRenderer: FC<ButtonRendererProps> = ({ buttons, itemId, sectionKey }
type: ElementType.BUTTON,
elementId: buttonId,
itemId,
sectionKey,
sectionKey: sectionKey as SectionName,
})
}
@@ -148,6 +148,14 @@ const ContentSection: FC = () => {
onClick={(e) => handleItemClick(index, e)}
>
{/* محتوای خالی برای columns خالی */}
<div style={{
margin: 0,
textAlign: 'right',
color: '#888',
fontSize: '14px'
}}>
{/* {'{{content}}'} */}
</div>
</td>
</tr>
) : (
@@ -164,6 +172,14 @@ const ContentSection: FC = () => {
padding: '8px'
}}
>
<div style={{
margin: '0 0 10px 0',
textAlign: 'right',
color: '#888',
fontSize: '14px'
}}>
{'{{content}}'}
</div>
<ButtonRenderer
buttons={item.buttons}
itemId={item?.id || ''}
@@ -202,6 +218,18 @@ const ContentSection: FC = () => {
itemId={item?.id || ''}
sectionKey="content"
/>
{(!item?.texts || item.texts.length === 0) &&
(!item?.images || item.images.length === 0) &&
(!item?.socials || item.socials.length === 0) && (
<div style={{
margin: 0,
textAlign: 'right',
color: '#888',
fontSize: '14px'
}}>
{'{{content}}'}
</div>
)}
</td>
</tr>
{/* ردیف دکمه‌ها */}
@@ -0,0 +1,271 @@
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()
// 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',
border: activeSection === sectionKey ? '1px dashed #0038FF' : '1px dashed #E5E7EB',
borderRadius: '24px',
cursor: 'pointer'
}}>
<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: '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: '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="123"
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="123"
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 ? "60" : "87"}
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="27"
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: '123px',
border: activeSection === sectionKey ? '1px dashed #0038FF' : '1px dashed #E5E7EB',
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
@@ -1,5 +1,5 @@
import { FC } from 'react'
import { ImageType, ImageSize, HorizontalAlignment, VerticalAlignment, ElementType, PersonalityDataType } from '../types/Types'
import { ImageType, ImageSize, HorizontalAlignment, VerticalAlignment, ElementType, PersonalityDataType, SectionName } from '../types/Types'
import { usePersonalityStore } from '../store/Store'
interface ImageRendererProps {
@@ -21,7 +21,7 @@ const ImageRenderer: FC<ImageRendererProps> = ({ images, itemId, sectionKey }) =
type: ElementType.IMAGE,
elementId: imageId,
itemId,
sectionKey,
sectionKey: sectionKey as SectionName,
})
}
@@ -3,14 +3,16 @@ import { useTranslation } from 'react-i18next'
import UploadBoxDraggble from '@/components/UploadBoxDraggble';
import ColorPicker from '@/components/ColorPicker';
import { usePersonalityStore } from '../store/Store';
import { SectionItemType, BackgroundSize, BackgroundPosition, SectionName } from '../types/Types';
import { SectionItemType, BackgroundSize, BackgroundPosition } from '../types/Types';
import Select from '@/components/Select';
import { useSingleUpload } from '../hooks/usePersonalityData';
import { toast } from '@/components/Toast';
import Button from '@/components/Button';
import { Trash } from 'iconsax-react';
const SettingSideBar: FC = () => {
const { t } = useTranslation()
const { activeSection, setColumnsCount, updateActiveItem, data, activeItemIndex } = usePersonalityStore()
const { activeSection, setColumnsCount, updateActiveItem, data, activeItemIndex, removeSection } = usePersonalityStore()
const [color, setColor] = useState("#aabbcc");
const [backgroundSize, setBackgroundSize] = useState<BackgroundSize>(BackgroundSize.COVER)
const [backgroundPosition, setBackgroundPosition] = useState<BackgroundPosition>(BackgroundPosition.CENTER)
@@ -54,11 +56,22 @@ const SettingSideBar: FC = () => {
const handleColumnClick = (columns: number) => {
if (activeSection) {
if (activeSection === SectionName.CONTENT && columns > 1) {
if (data[activeSection]?.isContent && columns > 1) {
toast('این بخش فقط می تواند یک ستون داشته باشد', 'error')
return
}
setColumnsCount(activeSection as SectionName, columns);
setColumnsCount(activeSection, columns);
}
}
const handleRemoveSection = () => {
if (activeSection && !data[activeSection]?.isContent) {
removeSection(activeSection)
toast('سکشن با موفقیت حذف شد', 'success')
} else {
toast('نمی‌توانید سکشن محتوا را حذف کنید', 'error')
}
}
@@ -167,6 +180,20 @@ const SettingSideBar: FC = () => {
<div>
<div className='text-lg'>{t('setting.setting')}</div>
{/* Section Delete */}
{activeSection && !data[activeSection]?.isContent && (
<div className='mt-6 border-b border-gray-200 pb-6'>
<Button
onClick={handleRemoveSection}
variant='secondary'
className='w-full flex items-center justify-center gap-2 !text-red-600 !border-red-600 hover:!bg-red-50'
>
<Trash size={16} />
حذف سکشن فعلی
</Button>
</div>
)}
<div className='flex h-[51px] gap-5 mt-6'>
<div
className={`w-[102px] h-full bg-[#EAECF4] transition-colors ${isUploading ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer hover:bg-[#d1d5db]'}`}
@@ -1,5 +1,5 @@
import React, { FC } from 'react'
import { SocialType, SocialNetworkType, SocialIconSize, SocialIconStyle, ElementType, PersonalityDataType, HorizontalAlignment, VerticalAlignment } from '../types/Types'
import { SocialType, SocialNetworkType, SocialIconSize, SocialIconStyle, ElementType, PersonalityDataType, HorizontalAlignment, VerticalAlignment, SectionName } from '../types/Types'
import { usePersonalityStore } from '../store/Store'
import { getSocialIconPath, hasPngIcon, getSocialColor as getSocialColorFromUtils, getSocialSvgIcon } from '../utils/socialIcons'
@@ -22,7 +22,7 @@ const SocialRenderer: FC<SocialRendererProps> = ({ socials, itemId, sectionKey }
type: ElementType.SOCIAL,
elementId: socialId,
itemId,
sectionKey,
sectionKey: sectionKey as SectionName,
})
}
@@ -1,5 +1,5 @@
import { FC } from 'react'
import { TextType, ElementType, PersonalityDataType } from '../types/Types'
import { TextType, ElementType, PersonalityDataType, SectionName } from '../types/Types'
import { usePersonalityStore } from '../store/Store'
interface TextRendererProps {
@@ -20,7 +20,7 @@ const TextRenderer: FC<TextRendererProps> = ({ texts, itemId, sectionKey }) => {
type: ElementType.TEXT,
elementId: textId,
itemId,
sectionKey,
sectionKey: sectionKey as SectionName,
})
}
+136 -57
View File
@@ -8,7 +8,6 @@ import {
ButtonType,
ImageType,
SocialType,
SectionName,
HorizontalAlignment,
VerticalAlignment,
SelectedElement,
@@ -16,9 +15,9 @@ import {
export const usePersonalityStore = create<PersonalityStore>((set) => ({
data: {
header: { columnsCount: 1, items: [] },
content: { columnsCount: 1, items: [] },
footer: { columnsCount: 1, items: [] },
header: { name: "هدر", columnsCount: 1, items: [] },
content: { name: "محتوا", columnsCount: 1, items: [], isContent: true },
footer: { name: "فوتر", columnsCount: 1, items: [] },
},
activeSection: "",
@@ -27,13 +26,35 @@ export const usePersonalityStore = create<PersonalityStore>((set) => ({
isEditMode: false,
setData: (data: PersonalityDataType) =>
set(() => ({
data,
})),
set(() => {
// Normalize data to ensure all sections have names
const normalizedData: PersonalityDataType = {};
setActiveSection: (section: SectionName) =>
Object.entries(data).forEach(([key, section]) => {
normalizedData[key] = {
...section,
name:
section.name ||
(key === "header"
? "هدر"
: key === "content"
? "محتوا"
: key === "footer"
? "فوتر"
: `سکشن ${key}`),
};
});
return { data: normalizedData };
}),
setActiveSection: (section: string) =>
set((state) => {
const sectionData = state.data[section as keyof PersonalityDataType];
const sectionData = state.data[section];
if (!sectionData) {
return state;
}
// اگر بخش آیتم ندارد و تعداد ستون‌ها بیشتر از صفر است، یک آیتم پیش‌فرض بساز
if (sectionData.items.length === 0 && sectionData.columnsCount > 0) {
@@ -85,10 +106,90 @@ export const usePersonalityStore = create<PersonalityStore>((set) => ({
setEditMode: (isEdit: boolean) => set({ isEditMode: isEdit }),
setColumnsCount: (
sectionKey: keyof PersonalityDataType,
columnsCount: number
) =>
addSection: (afterSectionKey?: string) =>
set((state) => {
// Generate automatic section name
const existingSectionNumbers = Object.values(state.data)
.map((section) => {
if (!section.name) return 0;
const match = section.name.match(/سکشن (\d+)/);
return match ? parseInt(match[1]) : 0;
})
.filter((num) => num > 0);
const nextNumber =
existingSectionNumbers.length > 0
? Math.max(...existingSectionNumbers) + 1
: 1;
const sectionName = `سکشن ${nextNumber}`;
const sectionKey = `section_${nextNumber}`;
if (state.data[sectionKey]) {
return state; // Section already exists
}
// Create new section data
const newSection = {
name: sectionName,
columnsCount: 1,
items: [],
};
// If afterSectionKey is provided, insert after that section
if (afterSectionKey) {
const keys = Object.keys(state.data);
const afterIndex = keys.indexOf(afterSectionKey);
if (afterIndex !== -1) {
const newData: typeof state.data = {};
// Add sections before the target
for (let i = 0; i <= afterIndex; i++) {
newData[keys[i]] = state.data[keys[i]];
}
// Add new section
newData[sectionKey] = newSection;
// Add remaining sections
for (let i = afterIndex + 1; i < keys.length; i++) {
newData[keys[i]] = state.data[keys[i]];
}
return { data: newData };
}
}
// Default: add at the end
return {
data: {
...state.data,
[sectionKey]: newSection,
},
};
}),
removeSection: (sectionKey: string) =>
set((state) => {
// Cannot remove content section
if (state.data[sectionKey]?.isContent) {
return state;
}
const newData = { ...state.data };
delete newData[sectionKey];
return {
data: newData,
activeSection:
state.activeSection === sectionKey ? "" : state.activeSection,
selectedElement: null,
isEditMode: false,
};
}),
setColumnsCount: (sectionKey: string, columnsCount: number) =>
set((state) => {
const currentItems = state.data[sectionKey].items;
const currentActiveItem = currentItems[state.activeItemIndex];
@@ -124,6 +225,7 @@ export const usePersonalityStore = create<PersonalityStore>((set) => ({
data: {
...state.data,
[sectionKey]: {
...state.data[sectionKey],
columnsCount,
items: newItems,
},
@@ -132,7 +234,7 @@ export const usePersonalityStore = create<PersonalityStore>((set) => ({
};
}),
setItems: (sectionKey: keyof PersonalityDataType, items: SectionItemType[]) =>
setItems: (sectionKey: string, items: SectionItemType[]) =>
set((state) => ({
data: {
...state.data,
@@ -143,10 +245,7 @@ export const usePersonalityStore = create<PersonalityStore>((set) => ({
},
})),
addItem: (
sectionKey: keyof PersonalityDataType,
item: Omit<SectionItemType, "id">
) =>
addItem: (sectionKey: string, item: Omit<SectionItemType, "id">) =>
set((state) => ({
data: {
...state.data,
@@ -161,7 +260,7 @@ export const usePersonalityStore = create<PersonalityStore>((set) => ({
set((state) => {
if (!state.activeSection) return state;
const sectionKey = state.activeSection as keyof PersonalityDataType;
const sectionKey = state.activeSection;
const items = state.data[sectionKey].items;
const activeIndex = state.activeItemIndex;
@@ -209,7 +308,7 @@ export const usePersonalityStore = create<PersonalityStore>((set) => ({
}),
updateItem: (
sectionKey: keyof PersonalityDataType,
sectionKey: string,
id: string,
updates: Partial<SectionItemType>
) =>
@@ -225,7 +324,7 @@ export const usePersonalityStore = create<PersonalityStore>((set) => ({
},
})),
removeItem: (sectionKey: keyof PersonalityDataType, id: string) =>
removeItem: (sectionKey: string, id: string) =>
set((state) => ({
data: {
...state.data,
@@ -236,11 +335,7 @@ export const usePersonalityStore = create<PersonalityStore>((set) => ({
},
})),
addText: (
sectionKey: keyof PersonalityDataType,
itemId: string,
text: Omit<TextType, "id">
) =>
addText: (sectionKey: string, itemId: string, text: Omit<TextType, "id">) =>
set((state) => ({
data: {
...state.data,
@@ -262,7 +357,7 @@ export const usePersonalityStore = create<PersonalityStore>((set) => ({
set((state) => {
if (!state.activeSection) return state;
const sectionKey = state.activeSection as keyof PersonalityDataType;
const sectionKey = state.activeSection as string;
const items = state.data[sectionKey].items;
const activeIndex = state.activeItemIndex;
@@ -289,7 +384,7 @@ export const usePersonalityStore = create<PersonalityStore>((set) => ({
}),
addButton: (
sectionKey: keyof PersonalityDataType,
sectionKey: string,
itemId: string,
button: Omit<ButtonType, "id">
) =>
@@ -317,7 +412,7 @@ export const usePersonalityStore = create<PersonalityStore>((set) => ({
set((state) => {
if (!state.activeSection) return state;
const sectionKey = state.activeSection as keyof PersonalityDataType;
const sectionKey = state.activeSection as string;
const items = state.data[sectionKey].items;
const activeIndex = state.activeItemIndex;
@@ -344,7 +439,7 @@ export const usePersonalityStore = create<PersonalityStore>((set) => ({
}),
updateText: (
sectionKey: keyof PersonalityDataType,
sectionKey: string,
itemId: string,
textId: string,
updates: Partial<TextType>
@@ -369,7 +464,7 @@ export const usePersonalityStore = create<PersonalityStore>((set) => ({
})),
updateButton: (
sectionKey: keyof PersonalityDataType,
sectionKey: string,
itemId: string,
buttonId: string,
updates: Partial<ButtonType>
@@ -394,7 +489,7 @@ export const usePersonalityStore = create<PersonalityStore>((set) => ({
})),
addImage: (
sectionKey: keyof PersonalityDataType,
sectionKey: string,
itemId: string,
image: Omit<ImageType, "id">
) =>
@@ -419,7 +514,7 @@ export const usePersonalityStore = create<PersonalityStore>((set) => ({
set((state) => {
if (!state.activeSection) return state;
const sectionKey = state.activeSection as keyof PersonalityDataType;
const sectionKey = state.activeSection as string;
const items = state.data[sectionKey].items;
const activeIndex = state.activeItemIndex;
@@ -446,7 +541,7 @@ export const usePersonalityStore = create<PersonalityStore>((set) => ({
}),
updateImage: (
sectionKey: keyof PersonalityDataType,
sectionKey: string,
itemId: string,
imageId: string,
updates: Partial<ImageType>
@@ -470,11 +565,7 @@ export const usePersonalityStore = create<PersonalityStore>((set) => ({
},
})),
deleteText: (
sectionKey: keyof PersonalityDataType,
itemId: string,
textId: string
) =>
deleteText: (sectionKey: string, itemId: string, textId: string) =>
set((state) => ({
data: {
...state.data,
@@ -496,11 +587,7 @@ export const usePersonalityStore = create<PersonalityStore>((set) => ({
isEditMode: false,
})),
deleteButton: (
sectionKey: keyof PersonalityDataType,
itemId: string,
buttonId: string
) =>
deleteButton: (sectionKey: string, itemId: string, buttonId: string) =>
set((state) => ({
data: {
...state.data,
@@ -522,11 +609,7 @@ export const usePersonalityStore = create<PersonalityStore>((set) => ({
isEditMode: false,
})),
deleteImage: (
sectionKey: keyof PersonalityDataType,
itemId: string,
imageId: string
) =>
deleteImage: (sectionKey: string, itemId: string, imageId: string) =>
set((state) => ({
data: {
...state.data,
@@ -549,7 +632,7 @@ export const usePersonalityStore = create<PersonalityStore>((set) => ({
})),
addSocial: (
sectionKey: keyof PersonalityDataType,
sectionKey: string,
itemId: string,
social: Omit<SocialType, "id">
) =>
@@ -577,7 +660,7 @@ export const usePersonalityStore = create<PersonalityStore>((set) => ({
set((state) => {
if (!state.activeSection) return state;
const sectionKey = state.activeSection as keyof PersonalityDataType;
const sectionKey = state.activeSection as string;
const items = state.data[sectionKey].items;
const activeIndex = state.activeItemIndex;
@@ -604,7 +687,7 @@ export const usePersonalityStore = create<PersonalityStore>((set) => ({
}),
updateSocial: (
sectionKey: keyof PersonalityDataType,
sectionKey: string,
itemId: string,
socialId: string,
updates: Partial<SocialType>
@@ -628,11 +711,7 @@ export const usePersonalityStore = create<PersonalityStore>((set) => ({
},
})),
deleteSocial: (
sectionKey: keyof PersonalityDataType,
itemId: string,
socialId: string
) =>
deleteSocial: (sectionKey: string, itemId: string, socialId: string) =>
set((state) => ({
data: {
...state.data,
+25 -48
View File
@@ -92,13 +92,11 @@ export type SelectedElement = {
type: ElementType;
elementId: string;
itemId: string;
sectionKey: keyof PersonalityDataType;
sectionKey: string;
} | null;
export type PersonalityDataType = {
header: SectionType;
content: SectionType;
footer: SectionType;
[key: string]: SectionType & { name: string; isContent?: boolean };
};
export type SectionType = {
@@ -175,106 +173,85 @@ export type SocialType = {
export type PersonalityStore = {
data: PersonalityDataType;
activeSection: SectionName | "";
activeSection: string;
activeItemIndex: number;
selectedElement: SelectedElement; // New field for element selection
isEditMode: boolean; // New field for edit mode
setData: (data: PersonalityDataType) => void;
setActiveSection: (section: SectionName) => void;
setActiveSection: (section: string) => void;
setActiveItemIndex: (index: number) => void;
setSelectedElement: (element: SelectedElement) => void; // New method
setEditMode: (isEdit: boolean) => void; // New method
setItems: (
sectionKey: keyof PersonalityDataType,
items: SectionItemType[]
) => void;
addItem: (
sectionKey: keyof PersonalityDataType,
item: Omit<SectionItemType, "id">
) => void;
// Section management methods
addSection: (afterSectionKey?: string) => void;
removeSection: (sectionKey: string) => void;
setItems: (sectionKey: string, items: SectionItemType[]) => void;
addItem: (sectionKey: string, item: Omit<SectionItemType, "id">) => void;
updateItem: (
sectionKey: keyof PersonalityDataType,
sectionKey: string,
id: string,
updates: Partial<SectionItemType>
) => void;
updateActiveItem: (updates: Partial<SectionItemType>) => void;
removeItem: (sectionKey: keyof PersonalityDataType, id: string) => void;
setColumnsCount: (
sectionKey: keyof PersonalityDataType,
columnsCount: number
) => void;
removeItem: (sectionKey: string, id: string) => void;
setColumnsCount: (sectionKey: string, columnsCount: number) => void;
addText: (
sectionKey: keyof PersonalityDataType,
sectionKey: string,
itemId: string,
text: Omit<TextType, "id">
) => void;
addTextToActiveItem: (text: Omit<TextType, "id">) => void;
addButton: (
sectionKey: keyof PersonalityDataType,
sectionKey: string,
itemId: string,
button: Omit<ButtonType, "id">
) => void;
addButtonToActiveItem: (button: Omit<ButtonType, "id">) => void;
updateText: (
sectionKey: keyof PersonalityDataType,
sectionKey: string,
itemId: string,
textId: string,
updates: Partial<TextType>
) => void;
updateButton: (
sectionKey: keyof PersonalityDataType,
sectionKey: string,
itemId: string,
buttonId: string,
updates: Partial<ButtonType>
) => void;
addImage: (
sectionKey: keyof PersonalityDataType,
sectionKey: string,
itemId: string,
image: Omit<ImageType, "id">
) => void;
addImageToActiveItem: (image: Omit<ImageType, "id">) => void;
updateImage: (
sectionKey: keyof PersonalityDataType,
sectionKey: string,
itemId: string,
imageId: string,
updates: Partial<ImageType>
) => void;
// Social network methods
addSocial: (
sectionKey: keyof PersonalityDataType,
sectionKey: string,
itemId: string,
social: Omit<SocialType, "id">
) => void;
addSocialToActiveItem: (social: Omit<SocialType, "id">) => void;
updateSocial: (
sectionKey: keyof PersonalityDataType,
sectionKey: string,
itemId: string,
socialId: string,
updates: Partial<SocialType>
) => void;
deleteSocial: (
sectionKey: keyof PersonalityDataType,
itemId: string,
socialId: string
) => void;
deleteSocial: (sectionKey: string, itemId: string, socialId: string) => void;
// New methods for deleting elements
deleteText: (
sectionKey: keyof PersonalityDataType,
itemId: string,
textId: string
) => void;
deleteButton: (
sectionKey: keyof PersonalityDataType,
itemId: string,
buttonId: string
) => void;
deleteImage: (
sectionKey: keyof PersonalityDataType,
itemId: string,
imageId: string
) => void;
deleteText: (sectionKey: string, itemId: string, textId: string) => void;
deleteButton: (sectionKey: string, itemId: string, buttonId: string) => void;
deleteImage: (sectionKey: string, itemId: string, imageId: string) => void;
};
export type TemplateType = {
@@ -505,14 +505,22 @@ export const exportPersonalityToHTML = async (
} else {
// اگر هیچ محتوایی نداریم و content section است
console.log(
"🔥 [ExportHTML] Empty content section detected, adding {{content}} placeholder"
"🔥 [ExportHTML] Empty content section detected, sectionHeight:",
sectionHeight
);
if (sectionHeight === "246px") {
console.log(
"🔥 [ExportHTML] Adding {{content}} placeholder to empty content"
);
result += `<tr>
<td width="100%" height="230" style="padding: 20px; vertical-align: top; font-size: 15px; line-height: 1.8;">
<div style="margin: 0; text-align: right; color: #888; font-size: 14px;">{{content}}</div>
</td>
</tr>`;
} else {
console.log(
"🔥 [ExportHTML] Not content section, skipping {{content}}"
);
}
}
@@ -567,18 +575,92 @@ export const exportPersonalityToHTML = async (
}
};
// Helper function to determine section height
const getSectionHeight = (
sectionKey: string,
sectionData: PersonalityDataType[string]
) => {
console.log(`🔥 [ExportHTML] Getting height for section: ${sectionKey}`, {
isContent: sectionData?.isContent,
});
if (sectionKey === "content" || sectionData?.isContent) {
return "246px";
}
// header, footer و سکشن‌های جدید همه 123px ارتفاع دارند
return "123px";
};
// Generate complete HTML exactly like Personality.tsx structure
console.log("🔥 [ExportHTML] Available sections:", Object.keys(data));
console.log(
"🔥 [ExportHTML] Full data structure:",
JSON.stringify(data, null, 2)
);
// Render all sections in the same order as they appear in the object
const sectionsHtml = Object.keys(data)
.map((sectionKey) => {
const sectionData = data[sectionKey];
const height = getSectionHeight(sectionKey, sectionData);
console.log(`🔥 [ExportHTML] Processing section: ${sectionKey}`, {
name: sectionData.name,
columnsCount: sectionData.columnsCount,
itemsLength: sectionData.items?.length || 0,
height: height,
isContent: sectionData.isContent,
});
// Pass only the section structure (without name and isContent)
const sectionStructure = {
columnsCount: sectionData.columnsCount,
items: sectionData.items || [],
};
const renderedSection = renderSection(sectionStructure, height, "");
console.log(
`🔥 [ExportHTML] Section ${sectionKey} rendered, HTML length:`,
renderedSection.length
);
return renderedSection;
})
.join("");
console.log(
"🔥 [ExportHTML] Total sections rendered:",
Object.keys(data).length
);
console.log(
"🔥 [ExportHTML] Combined sections HTML length:",
sectionsHtml.length
);
const html = `<div style="max-width: 600px; margin: 0 auto; direction: rtl;">
<table width="100%" cellpadding="0" cellspacing="0" border="0" style="border-collapse: collapse;">
<tbody>
${renderSection(data.header, "123px")}
${renderSection(data.content, "246px", "")}
${renderSection(data.footer, "123px", "")}
${sectionsHtml}
</tbody>
</table>
</div>`;
console.log("🔥 [ExportHTML] Final HTML generated:", html);
console.log("🔥 [ExportHTML] Final HTML generated (length):", html.length);
console.log(
"🔥 [ExportHTML] HTML contains {{content}}:",
html.includes("{{content}}")
);
// نمایش قسمت‌هایی از HTML که شامل content است
const contentMatch = html.match(/<div[^>]*>{{content}}<\/div>/g);
if (contentMatch) {
console.log(
"🔥 [ExportHTML] Found {{content}} placeholders:",
contentMatch
);
} else {
console.log("🔥 [ExportHTML] NO {{content}} placeholders found in HTML!");
}
return html;
};