import React, { useEffect, useState } from 'react' import { Helmet } from 'react-helmet' import navigationIcon from '@/proxy/menus/navigation-icon.config' import { AboutDto } from '@/proxy/about/models' import { getAbout, saveAboutPage } from '@/services/about' import { useLocalization } from '@/utils/hooks/useLocalization' import Loading from '@/components/shared/Loading' import { APP_NAME } from '@/constants/app.constant' import { ROUTES_ENUM } from '@/routes/route.constant' import { useStoreState } from '@/store' import { useStoreActions } from '@/store' import { useNavigate } from 'react-router-dom' import DesignerDrawer from './designer/DesignerDrawer' import SelectableBlock from './designer/SelectableBlock' import { DesignerSelection } from './designer/types' import { useDesignerState } from './designer/useDesignerState' interface AboutStatContent { icon: string value: string label: string labelKey: string useCounter?: boolean counterEnd?: string counterSuffix?: string counterDuration?: number } interface AboutDescriptionContent { key: string text: string } interface AboutSectionContent { title: string description: string titleKey: string descriptionKey: string } interface AboutContent { heroTitle: string heroTitleKey: string heroSubtitle: string heroSubtitleKey: string heroImage: string heroImageKey: string stats: AboutStatContent[] descriptions: AboutDescriptionContent[] sections: AboutSectionContent[] } const ABOUT_HERO_IMAGE = 'https://images.pexels.com/photos/3183183/pexels-photo-3183183.jpeg?auto=compress&cs=tinysrgb&w=1920' const ABOUT_HERO_TITLE_KEY = 'App.About' const ABOUT_HERO_SUBTITLE_KEY = 'Public.about.subtitle' const ABOUT_HERO_IMAGE_KEY = 'Public.about.heroImage' function isLikelyLocalizationKey(value?: string) { return Boolean(value && /^[A-Za-z0-9_.-]+$/.test(value) && value.includes('.')) } function resolveLocalizedValue( translate: (key: string) => string, keyOrValue: string | undefined, fallback = '', ) { if (!keyOrValue) { return fallback } if (!isLikelyLocalizationKey(keyOrValue)) { return keyOrValue } const translatedValue = translate('::' + keyOrValue) return translatedValue === keyOrValue ? fallback || keyOrValue : translatedValue } function buildAboutContent( about: AboutDto | undefined, translate: (key: string) => string, ): AboutContent { return { heroTitle: resolveLocalizedValue(translate, ABOUT_HERO_TITLE_KEY, 'About'), heroTitleKey: ABOUT_HERO_TITLE_KEY, heroSubtitle: resolveLocalizedValue(translate, ABOUT_HERO_SUBTITLE_KEY), heroSubtitleKey: ABOUT_HERO_SUBTITLE_KEY, heroImage: resolveLocalizedValue(translate, ABOUT_HERO_IMAGE_KEY, ABOUT_HERO_IMAGE), heroImageKey: ABOUT_HERO_IMAGE_KEY, stats: about?.statsDto.map((stat) => ({ icon: stat.icon || '', value: stat.value, label: resolveLocalizedValue(translate, stat.labelKey, stat.labelKey), labelKey: (isLikelyLocalizationKey(stat.labelKey) ? stat.labelKey : undefined) || `Public.about.dynamic.stat.${stat.value}.label`, useCounter: stat.useCounter, counterEnd: stat.counterEnd, counterSuffix: stat.counterSuffix, counterDuration: stat.counterDuration, })) ?? [], descriptions: about?.descriptionsDto.map((item, index) => ({ key: (isLikelyLocalizationKey(item) ? item : undefined) || `Public.about.dynamic.description.${index + 1}`, text: resolveLocalizedValue(translate, item, item), })) ?? [], sections: about?.sectionsDto.map((section) => ({ title: resolveLocalizedValue(translate, section.key, section.key), description: resolveLocalizedValue(translate, section.descKey, section.descKey), titleKey: (isLikelyLocalizationKey(section.key) ? section.key : undefined) || `Public.about.dynamic.section.${section.key}.title`, descriptionKey: (isLikelyLocalizationKey(section.descKey) ? section.descKey : undefined) || `Public.about.dynamic.section.${section.key}.description`, })) ?? [], } } const About: React.FC = () => { const { translate } = useLocalization() const navigate = useNavigate() const { setLang } = useStoreActions((actions) => actions.locale) const { getConfig } = useStoreActions((actions) => actions.abpConfig) const configCultureName = useStoreState( (state) => state.abpConfig.config?.localization.currentCulture.cultureName, ) const localeCurrentLang = useStoreState((state) => state.locale?.currentLang) const currentLanguage = configCultureName || localeCurrentLang || 'tr' const abpLanguages = useStoreState((state) => state.abpConfig.config?.localization.languages) || [] const languageOptions = abpLanguages .filter((language) => Boolean(language.cultureName)) .map((language) => { const cultureName = language.cultureName || 'tr' return { key: cultureName.toLowerCase().split('-')[0], cultureName, displayName: language.displayName || cultureName, } }) const languagesFromConfig = languageOptions.map((language) => language.key) const editorLanguages = Array.from( new Set((languagesFromConfig.length > 0 ? languagesFromConfig : [currentLanguage]).filter(Boolean)), ) const [loading, setLoading] = useState(true) const [isSaving, setIsSaving] = useState(false) const [isPanelVisible, setIsPanelVisible] = useState(true) const [about, setAbout] = useState() const iconColors = [ 'text-blue-600', 'text-red-600', 'text-green-600', 'text-purple-600', 'text-yellow-600', 'text-indigo-600', ] function getIconColor(index: number) { return iconColors[index % iconColors.length] } const initialContent = !loading ? buildAboutContent(about, translate) : null const { content, isDesignMode, selectedBlockId, selectedLanguage, supportedLanguages, setContent, setSelectedBlockId, resetContent, } = useDesignerState('about', initialContent, { currentLanguage, supportedLanguages: editorLanguages, }) useEffect(() => { setLoading(true) const fetchServices = async () => { try { const result = await getAbout() setAbout(result.data) } catch (error) { console.error('About alınırken hata oluştu:', error) } finally { setLoading(false) } } fetchServices() }, []) const updateContent = (updater: (current: AboutContent) => AboutContent) => { setContent((current) => { if (!current) { return current } return updater(current) }) } const handleFieldChange = (fieldKey: string, value: string | string[]) => { updateContent((current) => { if (fieldKey === 'heroTitle' || fieldKey === 'heroSubtitle' || fieldKey === 'heroImage') { return { ...current, [fieldKey]: value as string, } } if (fieldKey.startsWith('description-')) { const index = Number(fieldKey.replace('description-', '')) const descriptions = [...current.descriptions] descriptions[index] = { ...descriptions[index], text: value as string, } return { ...current, descriptions, } } if (selectedBlockId?.startsWith('stat-')) { const index = Number(selectedBlockId.replace('stat-', '')) const stats = [...current.stats] stats[index] = { ...stats[index], [fieldKey]: value as string, } return { ...current, stats, } } if (selectedBlockId?.startsWith('section-')) { const index = Number(selectedBlockId.replace('section-', '')) const sections = [...current.sections] sections[index] = { ...sections[index], [fieldKey]: value as string, } return { ...current, sections, } } return current }) } const selectedSelection: DesignerSelection | null = React.useMemo(() => { if (!content || !selectedBlockId) { return null } if (selectedBlockId === 'hero') { return { id: 'hero', title: content.heroTitleKey, description: 'Baslik, alt baslik ve arka plan gorselini guncelleyin.', fields: [ { key: 'heroTitle', label: content.heroTitleKey, type: 'text', value: content.heroTitle, }, { key: 'heroSubtitle', label: content.heroSubtitleKey, type: 'textarea', value: content.heroSubtitle, }, { key: 'heroImage', label: content.heroImageKey, type: 'image', value: content.heroImage, }, ], } } if (selectedBlockId === 'descriptions') { return { id: 'descriptions', title: 'Public.about.description.*', description: 'Orta bolumdeki aciklama metinlerini duzenleyin.', fields: content.descriptions.map((item, index) => ({ key: `description-${index}`, label: item.key || `Public.about.dynamic.description.${index + 1}`, type: 'textarea', value: item.text, rows: index % 2 === 0 ? 4 : 3, })), } } if (selectedBlockId.startsWith('stat-')) { const index = Number(selectedBlockId.replace('stat-', '')) const stat = content.stats[index] if (!stat) { return null } return { id: selectedBlockId, title: stat.labelKey, description: translate('::Public.designer.desc1'), fields: [ { key: 'icon', label: translate('::Public.designer.ikonAnahtari'), type: 'icon', value: stat.icon, placeholder: 'Ornek: FaUsers', }, { key: 'value', label: translate('::Public.designer.value'), type: 'text', value: stat.value }, { key: 'label', label: translate('::' + stat.labelKey), type: 'text', value: stat.label, }, ], } } if (selectedBlockId.startsWith('section-')) { const index = Number(selectedBlockId.replace('section-', '')) const section = content.sections[index] if (!section) { return null } return { id: selectedBlockId, title: section.titleKey, description: 'Kart basligi ve aciklama metnini duzenleyin.', fields: [ { key: 'title', label: section.titleKey, type: 'text', value: section.title, }, { key: 'description', label: section.descriptionKey, type: 'textarea', value: section.description, }, ], } } return null }, [content, selectedBlockId]) const handleSaveAndExit = async () => { if (!content || isSaving) { return } setIsSaving(true) try { await saveAboutPage({ cultureName: selectedLanguage, heroTitleKey: content.heroTitleKey, heroTitleValue: content.heroTitle, heroSubtitleKey: content.heroSubtitleKey, heroSubtitleValue: content.heroSubtitle, heroImageKey: content.heroImageKey, heroImageValue: content.heroImage, stats: content.stats.map((stat, index) => ({ icon: stat.icon, value: stat.value, labelKey: stat.labelKey || `Public.about.dynamic.stat.${index + 1}.label`, labelValue: stat.label, useCounter: stat.useCounter, counterEnd: stat.counterEnd, counterSuffix: stat.counterSuffix, counterDuration: stat.counterDuration, })), descriptions: content.descriptions.map((item, index) => ({ key: item.key || `Public.about.dynamic.description.${index + 1}`, value: item.text, })), sections: content.sections.map((section, index) => ({ titleKey: section.titleKey || `Public.about.dynamic.section.${index + 1}.title`, titleValue: section.title, descriptionKey: section.descriptionKey || `Public.about.dynamic.section.${index + 1}.description`, descriptionValue: section.description, })), }) await getConfig(false) setSelectedBlockId(null) navigate(ROUTES_ENUM.public.about, { replace: true }) } catch (error) { console.error('About tasarimi kaydedilemedi:', error) } finally { setIsSaving(false) } } const handleLanguageChange = (language: string) => { // Global locale changes asynchronously fetch fresh localization texts. // Keep designer language synced from store after that refresh. setLang(language) } const handleSelectBlock = (blockId: string) => { setSelectedBlockId(blockId) if (!isPanelVisible) { setIsPanelVisible(true) } } if (loading) { return (
) } return ( <>
{isDesignMode && !isPanelVisible && ( )} {/* Hero Section */}

{content?.heroTitle}

{content?.heroSubtitle}

{/* Stats Section */}
{content?.stats.map((stat, index) => { const IconComponent = navigationIcon[stat.icon || ''] return (
{IconComponent && ( )}
{stat.value}
{stat.label}
) })}
{/* Main Content */}

{content?.descriptions[0]?.text}

{content?.descriptions[1]?.text}

{content?.descriptions[2]?.text}

{content?.descriptions[3]?.text}

{content?.sections.map((section, index) => (

{section.title}

{section.description}

))}
0 ? languageOptions : supportedLanguages.map((language) => ({ key: language, cultureName: language, displayName: language.toUpperCase(), })) } onClose={() => setIsPanelVisible(false)} onSave={handleSaveAndExit} onLanguageChange={handleLanguageChange} onReset={resetContent} onFieldChange={handleFieldChange} />
) } export default About