Files
dmail-admin/src/pages/setting/personality/components/SocialSidebar.tsx
T
hamid zarghami 0b79f870ee social networks
2025-07-31 10:47:14 +03:30

417 lines
16 KiB
TypeScript

import Button from '@/components/Button'
import ColorPicker from '@/components/ColorPicker'
import Input from '@/components/Input'
import Select from '@/components/Select'
import { AlignRight, AlignBottom, AlignHorizontally, AlignLeft, AlignTop, AlignVertically, Add, Edit, Trash, Link2 } from 'iconsax-react'
import React, { FC, useState, useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { usePersonalityStore } from '../store/Store'
import { SocialNetworkType, SocialIconSize, SocialIconStyle, HorizontalAlignment, VerticalAlignment, ElementType } from '../types/Types'
import { getSocialIconPath, hasPngIcon, getSocialSvgIcon } from '../utils/socialIcons'
const SocialSidebar: FC = () => {
const { t } = useTranslation()
const {
addSocialToActiveItem,
selectedElement,
setSelectedElement,
data,
updateSocial,
deleteSocial
} = usePersonalityStore()
const [networkType, setNetworkType] = useState<SocialNetworkType>(SocialNetworkType.INSTAGRAM)
const [link, setLink] = useState<string>('')
const [size, setSize] = useState<SocialIconSize>(SocialIconSize.MEDIUM)
const [style, setStyle] = useState<SocialIconStyle>(SocialIconStyle.CIRCLE)
const [color, setColor] = useState<string>('#000000')
const [horizontalAlignment, setHorizontalAlignment] = useState<HorizontalAlignment>(HorizontalAlignment.CENTER)
const [verticalAlignment, setVerticalAlignment] = useState<VerticalAlignment>(VerticalAlignment.MIDDLE)
const isEditMode = selectedElement?.type === ElementType.SOCIAL;
// Load selected social data when element is selected
useEffect(() => {
if (isEditMode && selectedElement) {
try {
const sectionData = data[selectedElement.sectionKey];
if (!sectionData || !sectionData.items) {
console.error('Section data or items not found');
return;
}
const item = sectionData.items.find(item => item.id === selectedElement.itemId);
if (!item) {
console.error('Item not found');
return;
}
const social = item.socials?.find(s => s.id === selectedElement.elementId);
if (social) {
setNetworkType(social.networkType);
setLink(social.link);
setSize(social.size);
setStyle(social.style);
setColor(social.color || '#000000');
setHorizontalAlignment(social.alignment || HorizontalAlignment.CENTER);
setVerticalAlignment(social.verticalAlignment || VerticalAlignment.MIDDLE);
} else {
console.error('Social element not found');
}
} catch (error) {
console.error('Error loading social data:', error);
}
} else {
// Reset form when not in edit mode
setNetworkType(SocialNetworkType.INSTAGRAM)
setLink('')
setSize(SocialIconSize.MEDIUM)
setStyle(SocialIconStyle.CIRCLE)
setColor('#000000')
setHorizontalAlignment(HorizontalAlignment.CENTER)
setVerticalAlignment(VerticalAlignment.MIDDLE)
}
}, [selectedElement, isEditMode, data]);
const networkOptions = [
{ label: 'Instagram', value: SocialNetworkType.INSTAGRAM },
{ label: 'Facebook', value: SocialNetworkType.FACEBOOK },
{ label: 'Twitter', value: SocialNetworkType.TWITTER },
{ label: 'LinkedIn', value: SocialNetworkType.LINKEDIN },
{ label: 'YouTube', value: SocialNetworkType.YOUTUBE },
{ label: 'Telegram', value: SocialNetworkType.TELEGRAM },
{ label: 'WhatsApp', value: SocialNetworkType.WHATSAPP },
{ label: 'TikTok', value: SocialNetworkType.TIKTOK },
{ label: 'Pinterest', value: SocialNetworkType.PINTEREST },
{ label: 'Snapchat', value: SocialNetworkType.SNAPCHAT },
{ label: 'GitHub', value: SocialNetworkType.GITHUB },
{ label: 'Discord', value: SocialNetworkType.DISCORD },
]
const sizeOptions = [
{ label: 'کوچک', value: SocialIconSize.SMALL },
{ label: 'متوسط', value: SocialIconSize.MEDIUM },
{ label: 'بزرگ', value: SocialIconSize.LARGE }
]
const styleOptions = [
{ label: 'دایره', value: SocialIconStyle.CIRCLE },
{ label: 'مربع', value: SocialIconStyle.SQUARE },
{ label: 'گرد', value: SocialIconStyle.ROUNDED }
]
const handleAddSocial = () => {
if (link.trim()) {
addSocialToActiveItem({
networkType,
link,
size,
style,
color,
alignment: horizontalAlignment,
verticalAlignment,
})
// Reset form after adding
setNetworkType(SocialNetworkType.INSTAGRAM)
setLink('')
setSize(SocialIconSize.MEDIUM)
setStyle(SocialIconStyle.CIRCLE)
setColor('#000000')
setHorizontalAlignment(HorizontalAlignment.CENTER)
setVerticalAlignment(VerticalAlignment.MIDDLE)
}
}
const handleUpdateSocial = () => {
if (selectedElement && link.trim()) {
updateSocial(
selectedElement.sectionKey,
selectedElement.itemId,
selectedElement.elementId,
{
networkType,
link,
size,
style,
color,
alignment: horizontalAlignment,
verticalAlignment,
}
);
// Exit edit mode
setSelectedElement(null);
}
}
const handleDeleteSocial = () => {
if (selectedElement) {
deleteSocial(
selectedElement.sectionKey,
selectedElement.itemId,
selectedElement.elementId
);
// Exit edit mode
setSelectedElement(null);
}
}
const handleCancelEdit = () => {
setSelectedElement(null);
}
// Preview component for selected social network
const renderPreview = () => {
const getIconSize = () => {
switch (size) {
case SocialIconSize.SMALL:
return { width: '24px', height: '24px' }
case SocialIconSize.MEDIUM:
return { width: '32px', height: '32px' }
case SocialIconSize.LARGE:
return { width: '40px', height: '40px' }
default:
return { width: '32px', height: '32px' }
}
}
const getIconStyle = () => {
const iconSize = getIconSize()
const baseStyle = {
...iconSize,
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
color: color,
transition: 'all 0.2s ease',
}
switch (style) {
case SocialIconStyle.CIRCLE:
return {
...baseStyle,
borderRadius: '50%',
}
case SocialIconStyle.SQUARE:
return {
...baseStyle,
borderRadius: '0',
}
case SocialIconStyle.ROUNDED:
return {
...baseStyle,
borderRadius: '8px',
}
default:
return baseStyle
}
}
const renderIcon = () => {
if (hasPngIcon(networkType)) {
const iconPath = getSocialIconPath(networkType)
return (
<img
src={iconPath}
alt={`${networkType} icon`}
style={{
width: '100%',
height: '100%',
objectFit: 'contain',
display: 'block'
}}
/>
)
}
// Use shared SVG function for fallback
return getSocialSvgIcon(networkType, true) as React.ReactElement
}
return (
<div className='mt-4 p-4 bg-gray-50 rounded-lg'>
<div className='text-sm text-gray-600 mb-2'>{t('setting.preview')}</div>
<div className='flex justify-center'>
<div style={getIconStyle()}>
{renderIcon()}
</div>
</div>
</div>
)
}
return (
<div>
<div className='text-lg flex items-center justify-between'>
{t('setting.social_networks')}
{isEditMode && (
<div className='flex items-center gap-2'>
<span className='text-sm text-blue-600'>{t('setting.edit_mode')}</span>
<Edit size={16} color='#0038FF' />
</div>
)}
</div>
<div className='mt-8'>
<Select
label={t('setting.social_network')}
items={networkOptions}
value={networkType}
onChange={(e) => setNetworkType(e.target.value as SocialNetworkType)}
placeholder={t('setting.select_network')}
/>
</div>
{/* Preview of selected social network */}
{renderPreview()}
<div className='mt-5'>
<Input
label={t('setting.social_link')}
value={link}
onChange={(e) => setLink(e.target.value)}
endIcon={<Link2 size={18} color='#888' />}
placeholder="https://..."
/>
</div>
<div className='mt-5'>
<Select
label={t('setting.size')}
items={sizeOptions}
value={size}
onChange={(e) => setSize(e.target.value as SocialIconSize)}
placeholder={t('setting.select')}
/>
</div>
<div className='mt-5'>
<Select
label={t('setting.icon_style')}
items={styleOptions}
value={style}
onChange={(e) => setStyle(e.target.value as SocialIconStyle)}
placeholder={t('setting.select')}
/>
</div>
<div className='mt-5'>
<ColorPicker
label={t('setting.icon_color')}
defaultColor={color}
changeColor={setColor}
/>
</div>
<div className='mt-5 flex justify-between'>
<div>
<div className='text-sm'>
{t('setting.horizontal_position')}
</div>
<div className='mt-3 flex gap-4'>
<div
onClick={() => setHorizontalAlignment(HorizontalAlignment.RIGHT)}
className='cursor-pointer'
>
<AlignRight size={20} color={horizontalAlignment === HorizontalAlignment.RIGHT ? '#0038FF' : '#000'} />
</div>
<div
onClick={() => setHorizontalAlignment(HorizontalAlignment.CENTER)}
className='cursor-pointer'
>
<AlignVertically size={20} color={horizontalAlignment === HorizontalAlignment.CENTER ? '#0038FF' : '#000'} />
</div>
<div
onClick={() => setHorizontalAlignment(HorizontalAlignment.LEFT)}
className='cursor-pointer'
>
<AlignLeft size={20} color={horizontalAlignment === HorizontalAlignment.LEFT ? '#0038FF' : '#000'} />
</div>
</div>
</div>
<div>
<div className='text-sm'>
{t('setting.vertical_position')}
</div>
<div className='mt-3 flex gap-4'>
<div
onClick={() => setVerticalAlignment(VerticalAlignment.TOP)}
className='cursor-pointer'
>
<AlignTop size={20} color={verticalAlignment === VerticalAlignment.TOP ? '#0038FF' : '#000'} />
</div>
<div
onClick={() => setVerticalAlignment(VerticalAlignment.MIDDLE)}
className='cursor-pointer'
>
<AlignHorizontally size={20} color={verticalAlignment === VerticalAlignment.MIDDLE ? '#0038FF' : '#000'} />
</div>
<div
onClick={() => setVerticalAlignment(VerticalAlignment.BOTTOM)}
className='cursor-pointer'
>
<AlignBottom size={20} color={verticalAlignment === VerticalAlignment.BOTTOM ? '#0038FF' : '#000'} />
</div>
</div>
</div>
</div>
<div className='mt-7'>
{isEditMode ? (
<div className='space-y-3'>
<Button
onClick={handleUpdateSocial}
className='bg-[#0038FF] text-white w-full'
disabled={!link.trim()}
>
<div className='flex justify-center items-center gap-2'>
<Edit size={18} color='white' />
<span>{t('setting.update_social')}</span>
</div>
</Button>
<div className='flex gap-2'>
<Button
onClick={handleDeleteSocial}
className='bg-red-500 text-white flex-1'
>
<div className='flex justify-center items-center gap-1'>
<Trash size={16} color='white' />
<span className='text-sm'>{t('setting.delete')}</span>
</div>
</Button>
<Button
onClick={handleCancelEdit}
className='bg-gray-500 text-white flex-1'
>
<span className='text-sm'>{t('setting.cancel')}</span>
</Button>
</div>
</div>
) : (
<Button
onClick={handleAddSocial}
className='bg-[#ECEEF5] text-black w-full'
disabled={!link.trim()}
>
<div className='flex justify-center items-center gap-2'>
<Add size={24} color='black' />
<span>{t('setting.add_social')}</span>
</div>
</Button>
)}
</div>
</div>
)
}
export default SocialSidebar