delete a template + update template
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import { FC, useState, useEffect } from 'react'
|
import { FC, useState, useEffect } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useLocation } from 'react-router-dom'
|
import { useLocation, useParams } from 'react-router-dom'
|
||||||
import { SettingTabEnum } from './enum/SettingEnum'
|
import { SettingTabEnum } from './enum/SettingEnum'
|
||||||
import Personality from './personality/Personality'
|
import Personality from './personality/Personality'
|
||||||
import Domain from './domain/Domain'
|
import Domain from './domain/Domain'
|
||||||
@@ -14,17 +14,30 @@ import { TickCircle } from 'iconsax-react'
|
|||||||
import { usePersonalityStore } from './personality/store/Store'
|
import { usePersonalityStore } from './personality/store/Store'
|
||||||
import { exportPersonalityToHTML } from './personality/utils/ExportHTML'
|
import { exportPersonalityToHTML } from './personality/utils/ExportHTML'
|
||||||
import Input from '@/components/Input'
|
import Input from '@/components/Input'
|
||||||
import { useSaveTemplate } from './personality/hooks/usePersonalityData'
|
import { useSaveTemplate, useUpdateTemplate, useGetTemplate } from './personality/hooks/usePersonalityData'
|
||||||
import { toast } from '@/components/Toast'
|
import { toast } from '@/components/Toast'
|
||||||
|
|
||||||
const Setting: FC = () => {
|
const Setting: FC = () => {
|
||||||
|
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
const { id } = useParams()
|
||||||
const { data } = usePersonalityStore()
|
const { data } = usePersonalityStore()
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const [activeTab, setActiveTab] = useState<SettingTabEnum>(SettingTabEnum.SETTING_DOMAIN)
|
const [activeTab, setActiveTab] = useState<SettingTabEnum>(SettingTabEnum.SETTING_DOMAIN)
|
||||||
const [templateName, setTemplateName] = useState('')
|
const [templateName, setTemplateName] = useState('')
|
||||||
const { mutate: saveTemplate, isPending } = useSaveTemplate()
|
const { mutate: saveTemplate, isPending: isSaving } = useSaveTemplate()
|
||||||
|
const { mutate: updateTemplate, isPending: isUpdating } = useUpdateTemplate()
|
||||||
|
const { data: template } = useGetTemplate(id || '')
|
||||||
|
|
||||||
|
const isEditMode = !!id
|
||||||
|
const isLoading = isSaving || isUpdating
|
||||||
|
|
||||||
|
// پر کردن نام قالب در حالت edit
|
||||||
|
useEffect(() => {
|
||||||
|
if (isEditMode && template?.data?.template?.name) {
|
||||||
|
setTemplateName(template.data.template.name)
|
||||||
|
}
|
||||||
|
}, [isEditMode, template])
|
||||||
|
|
||||||
const getTabFromPath = (pathname: string): SettingTabEnum => {
|
const getTabFromPath = (pathname: string): SettingTabEnum => {
|
||||||
switch (pathname) {
|
switch (pathname) {
|
||||||
@@ -39,6 +52,10 @@ const Setting: FC = () => {
|
|||||||
case Paths.settingSignature:
|
case Paths.settingSignature:
|
||||||
return SettingTabEnum.SETTING_SIGNATURE
|
return SettingTabEnum.SETTING_SIGNATURE
|
||||||
default:
|
default:
|
||||||
|
// بررسی personality با id
|
||||||
|
if (pathname.startsWith(Paths.settingPersonality + '/')) {
|
||||||
|
return SettingTabEnum.SETTING_PERSONALITY
|
||||||
|
}
|
||||||
return SettingTabEnum.SETTING_DOMAIN
|
return SettingTabEnum.SETTING_DOMAIN
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -84,16 +101,40 @@ const Setting: FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const html = await exportPersonalityToHTML(data)
|
const html = await exportPersonalityToHTML(data)
|
||||||
|
|
||||||
|
if (isEditMode) {
|
||||||
|
// حالت edit - استفاده از updateTemplate
|
||||||
|
updateTemplate({
|
||||||
|
id: id!,
|
||||||
|
params: {
|
||||||
|
name: templateName,
|
||||||
|
rawHtml: html,
|
||||||
|
structure: data
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
onSuccess: (data) => {
|
||||||
|
toast(data?.data?.message || 'قالب با موفقیت بروزرسانی شد', 'success')
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
toast('خطا در بروزرسانی قالب', 'error')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// حالت جدید - استفاده از saveTemplate
|
||||||
saveTemplate({
|
saveTemplate({
|
||||||
name: templateName,
|
name: templateName,
|
||||||
rawHtml: html,
|
rawHtml: html,
|
||||||
structure: data
|
structure: data
|
||||||
}, {
|
}, {
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
toast(data?.data?.message, 'success')
|
toast(data?.data?.message || 'قالب با موفقیت ایجاد شد', 'success')
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
toast('خطا در ایجاد قالب', 'error')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// const handleExportHTML = async () => {
|
// const handleExportHTML = async () => {
|
||||||
// try {
|
// try {
|
||||||
@@ -124,11 +165,11 @@ const Setting: FC = () => {
|
|||||||
variant='secondary'
|
variant='secondary'
|
||||||
className='border border-black w-fit px-7'
|
className='border border-black w-fit px-7'
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
loading={isPending}
|
loading={isLoading}
|
||||||
>
|
>
|
||||||
<div className='flex items-center gap-2'>
|
<div className='flex items-center gap-2'>
|
||||||
<TickCircle size={18} color='black' />
|
<TickCircle size={18} color='black' />
|
||||||
{t('save')}
|
{isEditMode ? 'بروزرسانی' : t('save')}
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,9 +1,22 @@
|
|||||||
import { FC } from 'react'
|
import { FC, useEffect } from 'react'
|
||||||
import HeaderSection from './components/HeaderSection'
|
import HeaderSection from './components/HeaderSection'
|
||||||
import ContentSection from './components/ContentSection'
|
import ContentSection from './components/ContentSection'
|
||||||
import FooterSection from './components/FooterSection'
|
import FooterSection from './components/FooterSection'
|
||||||
|
import { useParams } from 'react-router-dom'
|
||||||
|
import { usePersonalityStore } from './store/Store'
|
||||||
|
import { useGetTemplate } from './hooks/usePersonalityData'
|
||||||
|
|
||||||
const Personality: FC = () => {
|
const Personality: FC = () => {
|
||||||
|
const { id } = useParams()
|
||||||
|
const { setData } = usePersonalityStore()
|
||||||
|
const { data: template } = useGetTemplate(id || '')
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (id && template?.data?.template?.structure) {
|
||||||
|
setData(template.data.template.structure)
|
||||||
|
}
|
||||||
|
}, [id, template, setData])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex-1 bg-white rounded-4xl p-8'>
|
<div className='flex-1 bg-white rounded-4xl p-8'>
|
||||||
<table width="100%" cellPadding="0" cellSpacing="0" border={0} style={{ borderCollapse: 'collapse' }}>
|
<table width="100%" cellPadding="0" cellSpacing="0" border={0} style={{ borderCollapse: 'collapse' }}>
|
||||||
|
|||||||
@@ -98,28 +98,28 @@ const PersonalitySidebar: FC = () => {
|
|||||||
/>
|
/>
|
||||||
<Text
|
<Text
|
||||||
size={22}
|
size={22}
|
||||||
color={selectedElement?.type === ElementType.TEXT ? '#0038FF' : 'black'}
|
color={'black'}
|
||||||
variant={SideBarTab.TEXT === active ? 'Bold' : 'Outline'}
|
variant={SideBarTab.TEXT === active ? 'Bold' : 'Outline'}
|
||||||
onClick={() => setActive(SideBarTab.TEXT)}
|
onClick={() => setActive(SideBarTab.TEXT)}
|
||||||
className='cursor-pointer'
|
className='cursor-pointer'
|
||||||
/>
|
/>
|
||||||
<LinkSquare
|
<LinkSquare
|
||||||
size={22}
|
size={22}
|
||||||
color={selectedElement?.type === ElementType.BUTTON ? '#0038FF' : 'black'}
|
color={'black'}
|
||||||
variant={SideBarTab.BUTTON === active ? 'Bold' : 'Outline'}
|
variant={SideBarTab.BUTTON === active ? 'Bold' : 'Outline'}
|
||||||
onClick={() => setActive(SideBarTab.BUTTON)}
|
onClick={() => setActive(SideBarTab.BUTTON)}
|
||||||
className='cursor-pointer'
|
className='cursor-pointer'
|
||||||
/>
|
/>
|
||||||
<Gallery
|
<Gallery
|
||||||
size={22}
|
size={22}
|
||||||
color={selectedElement?.type === ElementType.IMAGE ? '#0038FF' : 'black'}
|
color={'black'}
|
||||||
variant={SideBarTab.IMAGE === active ? 'Bold' : 'Outline'}
|
variant={SideBarTab.IMAGE === active ? 'Bold' : 'Outline'}
|
||||||
onClick={() => setActive(SideBarTab.IMAGE)}
|
onClick={() => setActive(SideBarTab.IMAGE)}
|
||||||
className='cursor-pointer'
|
className='cursor-pointer'
|
||||||
/>
|
/>
|
||||||
<Profile2User
|
<Profile2User
|
||||||
size={22}
|
size={22}
|
||||||
color={selectedElement?.type === ElementType.SOCIAL ? '#0038FF' : 'black'}
|
color={'black'}
|
||||||
variant={SideBarTab.SOCIAL === active ? 'Bold' : 'Outline'}
|
variant={SideBarTab.SOCIAL === active ? 'Bold' : 'Outline'}
|
||||||
onClick={() => setActive(SideBarTab.SOCIAL)}
|
onClick={() => setActive(SideBarTab.SOCIAL)}
|
||||||
className='cursor-pointer'
|
className='cursor-pointer'
|
||||||
|
|||||||
@@ -13,80 +13,47 @@ const ContentSection: FC = () => {
|
|||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { activeSection, activeItemIndex, data, setActiveSection, setActiveItemIndex } = usePersonalityStore()
|
const { activeSection, activeItemIndex, data, setActiveSection, setActiveItemIndex } = usePersonalityStore()
|
||||||
|
|
||||||
console.log('🟢 ContentSection - Current state:', {
|
// Early return if data is not available
|
||||||
activeSection,
|
if (!data || !data.content) {
|
||||||
activeItemIndex,
|
return (
|
||||||
contentColumnsCount: data.content.columnsCount,
|
<tr>
|
||||||
contentItemsLength: data.content.items.length
|
<td style={{ height: '246px', textAlign: 'center', verticalAlign: 'middle', paddingTop: '16px' }}>
|
||||||
});
|
<div>در حال بارگذاری...</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const handleSectionClick = (section: SectionName, e: React.MouseEvent) => {
|
const handleSectionClick = (section: SectionName, e: React.MouseEvent) => {
|
||||||
console.log('🟢 ContentSection - handleSectionClick called:', {
|
|
||||||
section,
|
|
||||||
event: e,
|
|
||||||
target: e.target,
|
|
||||||
currentTarget: e.currentTarget
|
|
||||||
});
|
|
||||||
|
|
||||||
// اگر کلیک روی فضای خالی باشد (نه روی content elements)
|
// اگر کلیک روی فضای خالی باشد (نه روی content elements)
|
||||||
const target = e.target as HTMLElement;
|
const target = e.target as HTMLElement;
|
||||||
const isClickOnContent = target.closest('[data-element-type]') !== null;
|
const isClickOnContent = target.closest('[data-element-type]') !== null;
|
||||||
|
|
||||||
console.log('🟢 ContentSection - Section click analysis:', {
|
|
||||||
isClickOnContent,
|
|
||||||
targetTagName: target.tagName,
|
|
||||||
targetClasses: target.className
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!isClickOnContent) {
|
if (!isClickOnContent) {
|
||||||
console.log('🟢 ContentSection - Setting active section:', section);
|
|
||||||
setActiveSection(section)
|
setActiveSection(section)
|
||||||
} else {
|
|
||||||
console.log('🟢 ContentSection - Click on content element, not setting section');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleItemClick = (index: number, e: React.MouseEvent) => {
|
const handleItemClick = (index: number, e: React.MouseEvent) => {
|
||||||
console.log('🟢 ContentSection - handleItemClick called:', {
|
|
||||||
index,
|
|
||||||
event: e,
|
|
||||||
target: e.target,
|
|
||||||
currentTarget: e.currentTarget
|
|
||||||
});
|
|
||||||
|
|
||||||
// Check if click is on a content element (text, button, image, social)
|
// Check if click is on a content element (text, button, image, social)
|
||||||
const target = e.target as HTMLElement;
|
const target = e.target as HTMLElement;
|
||||||
const isClickOnContent = target.closest('[data-element-type]') !== null;
|
const isClickOnContent = target.closest('[data-element-type]') !== null;
|
||||||
|
|
||||||
console.log('🟢 ContentSection - Click analysis:', {
|
|
||||||
isClickOnContent,
|
|
||||||
targetTagName: target.tagName,
|
|
||||||
targetClasses: target.className,
|
|
||||||
closestDataElement: target.closest('[data-element-type]')
|
|
||||||
});
|
|
||||||
|
|
||||||
// Only select column if not clicking on content elements
|
// Only select column if not clicking on content elements
|
||||||
if (!isClickOnContent) {
|
if (!isClickOnContent) {
|
||||||
console.log('🟢 ContentSection - Selecting column:', {
|
|
||||||
index,
|
|
||||||
section: SectionName.CONTENT,
|
|
||||||
currentActiveSection: activeSection,
|
|
||||||
currentActiveItemIndex: activeItemIndex
|
|
||||||
});
|
|
||||||
|
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
|
|
||||||
// Only set active section if we're not already in content section
|
// Only set active section if we're not already in content section
|
||||||
if (activeSection !== SectionName.CONTENT) {
|
if (activeSection !== SectionName.CONTENT) {
|
||||||
console.log('🟢 ContentSection - Switching to content section');
|
|
||||||
setActiveSection(SectionName.CONTENT)
|
setActiveSection(SectionName.CONTENT)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always set the active item index
|
// Always set the active item index
|
||||||
console.log('🟢 ContentSection - Setting active item index to:', index);
|
|
||||||
setActiveItemIndex(index)
|
setActiveItemIndex(index)
|
||||||
} else {
|
|
||||||
console.log('🟢 ContentSection - Click on content element, not selecting column');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,17 @@ const FooterSection: FC = () => {
|
|||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { activeSection, activeItemIndex, data, setActiveSection, setActiveItemIndex } = usePersonalityStore()
|
const { activeSection, activeItemIndex, data, setActiveSection, setActiveItemIndex } = usePersonalityStore()
|
||||||
|
|
||||||
|
// Early return if data is not available
|
||||||
|
if (!data || !data.footer) {
|
||||||
|
return (
|
||||||
|
<tr>
|
||||||
|
<td style={{ height: '123px', textAlign: 'center', verticalAlign: 'middle', paddingTop: '16px' }}>
|
||||||
|
<div>در حال بارگذاری...</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
console.log('🔴 FooterSection - Current state:', {
|
console.log('🔴 FooterSection - Current state:', {
|
||||||
activeSection,
|
activeSection,
|
||||||
activeItemIndex,
|
activeItemIndex,
|
||||||
|
|||||||
@@ -13,80 +13,50 @@ const HeaderSection: FC = () => {
|
|||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { activeSection, activeItemIndex, data, setActiveSection, setActiveItemIndex } = usePersonalityStore()
|
const { activeSection, activeItemIndex, data, setActiveSection, setActiveItemIndex } = usePersonalityStore()
|
||||||
|
|
||||||
console.log('🔵 HeaderSection - Current state:', {
|
// Early return if data is not available
|
||||||
activeSection,
|
if (!data || !data.header) {
|
||||||
activeItemIndex,
|
return (
|
||||||
headerColumnsCount: data.header.columnsCount,
|
<tr>
|
||||||
headerItemsLength: data.header.items.length
|
<td style={{ height: '123px', textAlign: 'center', verticalAlign: 'middle' }}>
|
||||||
});
|
<div>در حال بارگذاری...</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleSectionClick = (section: SectionName, e: React.MouseEvent) => {
|
const handleSectionClick = (section: SectionName, e: React.MouseEvent) => {
|
||||||
console.log('🔵 HeaderSection - handleSectionClick called:', {
|
|
||||||
section,
|
|
||||||
event: e,
|
|
||||||
target: e.target,
|
|
||||||
currentTarget: e.currentTarget
|
|
||||||
});
|
|
||||||
|
|
||||||
// اگر کلیک روی فضای خالی باشد (نه روی content elements)
|
// اگر کلیک روی فضای خالی باشد (نه روی content elements)
|
||||||
const target = e.target as HTMLElement;
|
const target = e.target as HTMLElement;
|
||||||
const isClickOnContent = target.closest('[data-element-type]') !== null;
|
const isClickOnContent = target.closest('[data-element-type]') !== null;
|
||||||
|
|
||||||
console.log('🔵 HeaderSection - Section click analysis:', {
|
|
||||||
isClickOnContent,
|
|
||||||
targetTagName: target.tagName,
|
|
||||||
targetClasses: target.className
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!isClickOnContent) {
|
if (!isClickOnContent) {
|
||||||
console.log('🔵 HeaderSection - Setting active section:', section);
|
|
||||||
setActiveSection(section)
|
setActiveSection(section)
|
||||||
} else {
|
|
||||||
console.log('🔵 HeaderSection - Click on content element, not setting section');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleItemClick = (index: number, e: React.MouseEvent) => {
|
const handleItemClick = (index: number, e: React.MouseEvent) => {
|
||||||
console.log('🔵 HeaderSection - handleItemClick called:', {
|
|
||||||
index,
|
|
||||||
event: e,
|
|
||||||
target: e.target,
|
|
||||||
currentTarget: e.currentTarget
|
|
||||||
});
|
|
||||||
|
|
||||||
// Check if click is on a content element (text, button, image, social)
|
// Check if click is on a content element (text, button, image, social)
|
||||||
const target = e.target as HTMLElement;
|
const target = e.target as HTMLElement;
|
||||||
const isClickOnContent = target.closest('[data-element-type]') !== null;
|
const isClickOnContent = target.closest('[data-element-type]') !== null;
|
||||||
|
|
||||||
console.log('🔵 HeaderSection - Click analysis:', {
|
|
||||||
isClickOnContent,
|
|
||||||
targetTagName: target.tagName,
|
|
||||||
targetClasses: target.className,
|
|
||||||
closestDataElement: target.closest('[data-element-type]')
|
|
||||||
});
|
|
||||||
|
|
||||||
// Only select column if not clicking on content elements
|
// Only select column if not clicking on content elements
|
||||||
if (!isClickOnContent) {
|
if (!isClickOnContent) {
|
||||||
console.log('🔵 HeaderSection - Selecting column:', {
|
|
||||||
index,
|
|
||||||
section: SectionName.HEADER,
|
|
||||||
currentActiveSection: activeSection,
|
|
||||||
currentActiveItemIndex: activeItemIndex
|
|
||||||
});
|
|
||||||
|
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
|
|
||||||
// Only set active section if we're not already in header section
|
// Only set active section if we're not already in header section
|
||||||
if (activeSection !== SectionName.HEADER) {
|
if (activeSection !== SectionName.HEADER) {
|
||||||
console.log('🔵 HeaderSection - Switching to header section');
|
|
||||||
setActiveSection(SectionName.HEADER)
|
setActiveSection(SectionName.HEADER)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always set the active item index
|
// Always set the active item index
|
||||||
console.log('🔵 HeaderSection - Setting active item index to:', index);
|
|
||||||
setActiveItemIndex(index)
|
setActiveItemIndex(index)
|
||||||
} else {
|
|
||||||
console.log('🔵 HeaderSection - Click on content element, not selecting column');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,10 @@ import { FC, useEffect, useState } from 'react'
|
|||||||
import { TemplateResponseType } from '../types/Types'
|
import { TemplateResponseType } from '../types/Types'
|
||||||
import Radio from '@/components/Radio'
|
import Radio from '@/components/Radio'
|
||||||
import { Brush2, Trash } from 'iconsax-react'
|
import { Brush2, Trash } from 'iconsax-react'
|
||||||
import { useSetSelectedTemplate } from '../hooks/usePersonalityData'
|
import { useSetSelectedTemplate, useDeleteTemplate } from '../hooks/usePersonalityData'
|
||||||
|
import { Link } from 'react-router-dom'
|
||||||
|
import { Paths } from '@/utils/Paths'
|
||||||
|
import ModalConfrim from '@/components/ModalConfrim'
|
||||||
|
|
||||||
const Templete: FC<{
|
const Templete: FC<{
|
||||||
item: TemplateResponseType
|
item: TemplateResponseType
|
||||||
@@ -10,14 +13,24 @@ const Templete: FC<{
|
|||||||
}> = ({ item, selectedTemplateId }) => {
|
}> = ({ item, selectedTemplateId }) => {
|
||||||
|
|
||||||
const [isActive, setIsActive] = useState(item.selected)
|
const [isActive, setIsActive] = useState(item.selected)
|
||||||
|
const [isShowDeleteModal, setIsShowDeleteModal] = useState(false)
|
||||||
|
|
||||||
const { mutate: setSelectedTemplate } = useSetSelectedTemplate()
|
const { mutate: setSelectedTemplate } = useSetSelectedTemplate()
|
||||||
|
const { mutate: deleteTemplate, isPending: isDeleting } = useDeleteTemplate()
|
||||||
|
|
||||||
const handleChangeSelectedTemplate = (id: string) => {
|
const handleChangeSelectedTemplate = (id: string) => {
|
||||||
setSelectedTemplate(id, {
|
setSelectedTemplate(id, {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleDeleteTemplate = () => {
|
||||||
|
deleteTemplate(item.id, {
|
||||||
|
onSuccess: () => {
|
||||||
|
setIsShowDeleteModal(false)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedTemplateId) {
|
if (selectedTemplateId) {
|
||||||
setIsActive(selectedTemplateId === item.id)
|
setIsActive(selectedTemplateId === item.id)
|
||||||
@@ -27,6 +40,7 @@ const Templete: FC<{
|
|||||||
}, [item.selected, selectedTemplateId, item.id])
|
}, [item.selected, selectedTemplateId, item.id])
|
||||||
|
|
||||||
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'></div>
|
||||||
<div className='mt-4 flex justify-between items-center'>
|
<div className='mt-4 flex justify-between items-center'>
|
||||||
@@ -35,11 +49,25 @@ const Templete: FC<{
|
|||||||
<div className='text-sm'>{item.name}</div>
|
<div className='text-sm'>{item.name}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='flex gap-2 items-center'>
|
<div className='flex gap-2 items-center'>
|
||||||
|
<Link to={`${Paths.settingPersonality}/${item.id}`}>
|
||||||
<Brush2 size={20} color='black' />
|
<Brush2 size={20} color='black' />
|
||||||
|
</Link>
|
||||||
|
<button onClick={() => setIsShowDeleteModal(true)} className='p-1'>
|
||||||
<Trash size={20} color='red' />
|
<Trash size={20} color='red' />
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<ModalConfrim
|
||||||
|
isOpen={isShowDeleteModal}
|
||||||
|
close={() => setIsShowDeleteModal(false)}
|
||||||
|
onConfrim={handleDeleteTemplate}
|
||||||
|
isLoading={isDeleting}
|
||||||
|
title="حذف قالب"
|
||||||
|
label={`آیا از حذف قالب "${item.name}" اطمینان دارید؟`}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -228,7 +228,6 @@ const TextSidebar: FC = () => {
|
|||||||
<div className='space-y-3'>
|
<div className='space-y-3'>
|
||||||
<Button
|
<Button
|
||||||
onClick={handleUpdateText}
|
onClick={handleUpdateText}
|
||||||
className='bg-[#0038FF] text-white w-full'
|
|
||||||
disabled={!value.trim()}
|
disabled={!value.trim()}
|
||||||
>
|
>
|
||||||
<div className='flex justify-center items-center gap-2'>
|
<div className='flex justify-center items-center gap-2'>
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
import * as api from "../service/Service";
|
import * as api from "../service/Service";
|
||||||
import { TemplateType, TemplatesResponseType } from "../types/Types";
|
import {
|
||||||
|
TemplateType,
|
||||||
|
TemplatesResponseType,
|
||||||
|
GetTemplateResponse,
|
||||||
|
} from "../types/Types";
|
||||||
|
|
||||||
export const useSaveTemplate = () => {
|
export const useSaveTemplate = () => {
|
||||||
return useMutation({
|
return useMutation({
|
||||||
@@ -20,3 +24,29 @@ export const useSetSelectedTemplate = () => {
|
|||||||
mutationFn: (id: string) => api.setSelectedTemplate(id),
|
mutationFn: (id: string) => api.setSelectedTemplate(id),
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useGetTemplate = (id: string) => {
|
||||||
|
return useQuery<GetTemplateResponse>({
|
||||||
|
queryKey: ["template", id],
|
||||||
|
queryFn: () => api.getTemplate(id),
|
||||||
|
enabled: !!id,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useUpdateTemplate = () => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: ({ id, params }: { id: string; params: TemplateType }) =>
|
||||||
|
api.updateTemplate({ id, params }),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useDeleteTemplate = () => {
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: (id: string) => api.deleteTemplate(id),
|
||||||
|
onSuccess: () => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["templates"] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -15,3 +15,24 @@ export const setSelectedTemplate = async (id: string) => {
|
|||||||
const { data } = await axios.patch(`/templates/${id}/set-selected`);
|
const { data } = await axios.patch(`/templates/${id}/set-selected`);
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getTemplate = async (id: string) => {
|
||||||
|
const { data } = await axios.get(`/templates/${id}`);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateTemplate = async ({
|
||||||
|
id,
|
||||||
|
params,
|
||||||
|
}: {
|
||||||
|
id: string;
|
||||||
|
params: TemplateType;
|
||||||
|
}) => {
|
||||||
|
const { data } = await axios.patch(`/templates/${id}`, params);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const deleteTemplate = async (id: string) => {
|
||||||
|
const { data } = await axios.delete(`/templates/${id}`);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|||||||
@@ -26,6 +26,11 @@ export const usePersonalityStore = create<PersonalityStore>((set) => ({
|
|||||||
selectedElement: null,
|
selectedElement: null,
|
||||||
isEditMode: false,
|
isEditMode: false,
|
||||||
|
|
||||||
|
setData: (data: PersonalityDataType) =>
|
||||||
|
set(() => ({
|
||||||
|
data,
|
||||||
|
})),
|
||||||
|
|
||||||
setActiveSection: (section: SectionName) =>
|
setActiveSection: (section: SectionName) =>
|
||||||
set((state) => {
|
set((state) => {
|
||||||
console.log("🏪 Store - setActiveSection called:", {
|
console.log("🏪 Store - setActiveSection called:", {
|
||||||
|
|||||||
@@ -168,6 +168,7 @@ export type PersonalityStore = {
|
|||||||
selectedElement: SelectedElement; // New field for element selection
|
selectedElement: SelectedElement; // New field for element selection
|
||||||
isEditMode: boolean; // New field for edit mode
|
isEditMode: boolean; // New field for edit mode
|
||||||
|
|
||||||
|
setData: (data: PersonalityDataType) => void;
|
||||||
setActiveSection: (section: SectionName) => void;
|
setActiveSection: (section: SectionName) => void;
|
||||||
setActiveItemIndex: (index: number) => void;
|
setActiveItemIndex: (index: number) => void;
|
||||||
setSelectedElement: (element: SelectedElement) => void; // New method
|
setSelectedElement: (element: SelectedElement) => void; // New method
|
||||||
@@ -270,6 +271,24 @@ export type TemplateType = {
|
|||||||
structure: PersonalityDataType;
|
structure: PersonalityDataType;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type GetTemplateResponse = {
|
||||||
|
statusCode: number;
|
||||||
|
success: boolean;
|
||||||
|
data: {
|
||||||
|
template: {
|
||||||
|
id: string;
|
||||||
|
createdAt: string;
|
||||||
|
updatedAt: string;
|
||||||
|
deletedAt: string | null;
|
||||||
|
name: string;
|
||||||
|
structure: PersonalityDataType;
|
||||||
|
currentVersion: string;
|
||||||
|
selected: boolean;
|
||||||
|
business: BusinessType;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
// API Response Types
|
// API Response Types
|
||||||
export type BusinessType = {
|
export type BusinessType = {
|
||||||
id: string;
|
id: string;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ const AppRouter: FC = () => {
|
|||||||
<Route path={Paths.settingPersonality} element={<Setting />} />
|
<Route path={Paths.settingPersonality} element={<Setting />} />
|
||||||
<Route path={Paths.settingSignature} element={<Setting />} />
|
<Route path={Paths.settingSignature} element={<Setting />} />
|
||||||
<Route path={Paths.settingPersonalityList} element={<List />} />
|
<Route path={Paths.settingPersonalityList} element={<List />} />
|
||||||
|
<Route path={`${Paths.settingPersonality}/:id`} element={<Setting />} />
|
||||||
<Route path={Paths.emailBuilder} element={<EmailBuilder />} />
|
<Route path={Paths.emailBuilder} element={<EmailBuilder />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user