import React, { useEffect, useMemo, useState } from 'react' import { Link } from 'react-router-dom' import { FaArrowRight, FaChevronLeft, FaChevronRight } from 'react-icons/fa' import { Helmet } from 'react-helmet' import { useLocalization } from '@/utils/hooks/useLocalization' import { ROUTES_ENUM } from '@/routes/route.constant' import { APP_NAME } from '@/constants/app.constant' import { PUBLIC_PAGE_DESIGN } from '@/constants/permission.constant' import { Button } from '@/components/ui' import { useOptionalDemo } from '@/contexts/DemoContext' import { getHome, HomeDto, saveHomePage } from '@/services/home.service' import { DesignerLayer, DesignerField, DesignerSelection, SelectableBlock, TranslateFn, iconField, imageField, resolveLocalizedValue, styleField, textAreaField, textField, translateLabel, usePageDesigner, } from './designer' import { designerPageClass } from './pageStyles' import { PageIcon, PublicPageLoader } from './shared' interface HomeSlideServiceContent { icon: string title: string titleKey: string description: string descriptionKey: string styleClass: string } interface HomeSlideContent { title: string titleKey: string subtitle: string subtitleKey: string styleClass: string services: HomeSlideServiceContent[] } interface HomeCardContent { icon: string title: string titleKey: string description: string descriptionKey: string styleClass: string } interface HomeSolutionContent extends HomeCardContent { colorClass: string } interface HomeContent { heroBackgroundImage: string heroBackgroundImageKey: string heroPrimaryCtaLabel: string heroPrimaryCtaKey: string heroPrimaryCtaStyle: string heroPrimaryCtaStyleKey: string heroSecondaryCtaLabel: string heroSecondaryCtaKey: string heroSecondaryCtaStyle: string heroSecondaryCtaStyleKey: string featuresTitle: string featuresTitleKey: string featuresTitleStyle: string featuresTitleStyleKey: string featuresSubtitle: string featuresSubtitleKey: string featuresSubtitleStyle: string featuresSubtitleStyleKey: string solutionsTitle: string solutionsTitleKey: string solutionsTitleStyle: string solutionsTitleStyleKey: string solutionsSubtitle: string solutionsSubtitleKey: string solutionsSubtitleStyle: string solutionsSubtitleStyleKey: string ctaTitle: string ctaTitleKey: string ctaTitleStyle: string ctaTitleStyleKey: string ctaSubtitle: string ctaSubtitleKey: string ctaSubtitleStyle: string ctaSubtitleStyleKey: string ctaButtonLabel: string ctaButtonLabelKey: string ctaButtonStyle: string ctaButtonStyleKey: string slides: HomeSlideContent[] features: HomeCardContent[] solutions: HomeSolutionContent[] } const SLIDE_INTERVAL_MS = 10000 const HOME_HERO_DEFAULT_IMAGE = 'https://images.pexels.com/photos/3183150/pexels-photo-3183150.jpeg?auto=compress&cs=tinysrgb&w=1920' const HOME_HERO_PRIMARY_CTA_STYLE_KEY = 'Public.home.hero.primaryCta.style' const HOME_HERO_SECONDARY_CTA_STYLE_KEY = 'Public.home.hero.secondaryCta.style' const HOME_FEATURES_TITLE_STYLE_KEY = 'Public.home.features.title.style' const HOME_FEATURES_SUBTITLE_STYLE_KEY = 'Public.home.features.subtitle.style' const HOME_SOLUTIONS_TITLE_STYLE_KEY = 'Public.home.solutions.title.style' const HOME_SOLUTIONS_SUBTITLE_STYLE_KEY = 'Public.home.solutions.subtitle.style' const HOME_CTA_TITLE_STYLE_KEY = 'Public.home.cta.title.style' const HOME_CTA_SUBTITLE_STYLE_KEY = 'Public.home.cta.subtitle.style' const HOME_CTA_BUTTON_STYLE_KEY = 'Public.home.cta.button.style' const HERO_PRIMARY_CTA_STYLE_DEFAULT = 'inline-flex items-center justify-center px-8 py-4 bg-gradient-to-r from-blue-500 to-purple-500 hover:from-blue-600 hover:to-purple-600 text-white rounded-lg font-semibold transition-all transform hover:scale-105' const HERO_SECONDARY_CTA_STYLE_DEFAULT = 'inline-flex items-center justify-center px-8 py-4 bg-white/10 hover:bg-white/20 text-white rounded-lg font-semibold backdrop-blur-sm transition-all transform hover:scale-105' const FEATURES_TITLE_STYLE_DEFAULT = 'text-4xl font-bold text-gray-900 dark:text-gray-100 mb-4' const FEATURES_SUBTITLE_STYLE_DEFAULT = 'text-xl text-gray-600 dark:text-gray-300 max-w-2xl mx-auto' const SOLUTIONS_TITLE_STYLE_DEFAULT = 'text-4xl font-bold text-gray-900 dark:text-gray-100 mb-4' const SOLUTIONS_SUBTITLE_STYLE_DEFAULT = 'text-xl text-gray-600 dark:text-gray-300 max-w-2xl mx-auto' const CTA_TITLE_STYLE_DEFAULT = 'text-3xl font-bold text-white mb-4' const CTA_SUBTITLE_STYLE_DEFAULT = 'text-white text-lg mb-8' const CTA_BUTTON_STYLE_DEFAULT = 'inline-block bg-white text-blue-600 px-8 py-3 rounded-lg font-semibold hover:bg-blue-50 transition-colors' const SLIDE_STYLE_DEFAULT = 'max-w-4xl mx-auto text-center' const SLIDE_SERVICE_STYLE_DEFAULT = 'bg-white/5 backdrop-blur-sm rounded-2xl p-8 text-center hover:scale-105 hover:bg-white/10 transition-all' const FEATURE_CARD_STYLE_DEFAULT = 'p-8 bg-white dark:bg-gray-900 rounded-xl shadow-lg hover:shadow-xl transition-shadow dark:shadow-gray-950/40' const SOLUTION_CARD_STYLE_DEFAULT = 'p-8 h-full rounded-2xl' function buildHomeContent(home: HomeDto | undefined, translate: TranslateFn): HomeContent { return { heroBackgroundImage: resolveLocalizedValue( translate, home?.heroBackgroundImageKey, HOME_HERO_DEFAULT_IMAGE, ), heroBackgroundImageKey: home?.heroBackgroundImageKey || '', heroPrimaryCtaLabel: resolveLocalizedValue(translate, home?.heroPrimaryCtaKey, ''), heroPrimaryCtaKey: home?.heroPrimaryCtaKey || '', heroPrimaryCtaStyle: resolveLocalizedValue( translate, HOME_HERO_PRIMARY_CTA_STYLE_KEY, HERO_PRIMARY_CTA_STYLE_DEFAULT, ), heroPrimaryCtaStyleKey: HOME_HERO_PRIMARY_CTA_STYLE_KEY, heroSecondaryCtaLabel: resolveLocalizedValue(translate, home?.heroSecondaryCtaKey, ''), heroSecondaryCtaKey: home?.heroSecondaryCtaKey || '', heroSecondaryCtaStyle: resolveLocalizedValue( translate, HOME_HERO_SECONDARY_CTA_STYLE_KEY, HERO_SECONDARY_CTA_STYLE_DEFAULT, ), heroSecondaryCtaStyleKey: HOME_HERO_SECONDARY_CTA_STYLE_KEY, featuresTitle: resolveLocalizedValue(translate, home?.featuresTitleKey, ''), featuresTitleKey: home?.featuresTitleKey || '', featuresTitleStyle: resolveLocalizedValue( translate, HOME_FEATURES_TITLE_STYLE_KEY, FEATURES_TITLE_STYLE_DEFAULT, ), featuresTitleStyleKey: HOME_FEATURES_TITLE_STYLE_KEY, featuresSubtitle: resolveLocalizedValue(translate, home?.featuresSubtitleKey, ''), featuresSubtitleKey: home?.featuresSubtitleKey || '', featuresSubtitleStyle: resolveLocalizedValue( translate, HOME_FEATURES_SUBTITLE_STYLE_KEY, FEATURES_SUBTITLE_STYLE_DEFAULT, ), featuresSubtitleStyleKey: HOME_FEATURES_SUBTITLE_STYLE_KEY, solutionsTitle: resolveLocalizedValue(translate, home?.solutionsTitleKey, ''), solutionsTitleKey: home?.solutionsTitleKey || '', solutionsTitleStyle: resolveLocalizedValue( translate, HOME_SOLUTIONS_TITLE_STYLE_KEY, SOLUTIONS_TITLE_STYLE_DEFAULT, ), solutionsTitleStyleKey: HOME_SOLUTIONS_TITLE_STYLE_KEY, solutionsSubtitle: resolveLocalizedValue(translate, home?.solutionsSubtitleKey, ''), solutionsSubtitleKey: home?.solutionsSubtitleKey || '', solutionsSubtitleStyle: resolveLocalizedValue( translate, HOME_SOLUTIONS_SUBTITLE_STYLE_KEY, SOLUTIONS_SUBTITLE_STYLE_DEFAULT, ), solutionsSubtitleStyleKey: HOME_SOLUTIONS_SUBTITLE_STYLE_KEY, ctaTitle: resolveLocalizedValue(translate, home?.ctaTitleKey, ''), ctaTitleKey: home?.ctaTitleKey || '', ctaTitleStyle: resolveLocalizedValue(translate, HOME_CTA_TITLE_STYLE_KEY, CTA_TITLE_STYLE_DEFAULT), ctaTitleStyleKey: HOME_CTA_TITLE_STYLE_KEY, ctaSubtitle: resolveLocalizedValue(translate, home?.ctaSubtitleKey, ''), ctaSubtitleKey: home?.ctaSubtitleKey || '', ctaSubtitleStyle: resolveLocalizedValue( translate, HOME_CTA_SUBTITLE_STYLE_KEY, CTA_SUBTITLE_STYLE_DEFAULT, ), ctaSubtitleStyleKey: HOME_CTA_SUBTITLE_STYLE_KEY, ctaButtonLabel: resolveLocalizedValue(translate, home?.ctaButtonLabelKey, ''), ctaButtonLabelKey: home?.ctaButtonLabelKey || '', ctaButtonStyle: resolveLocalizedValue( translate, HOME_CTA_BUTTON_STYLE_KEY, CTA_BUTTON_STYLE_DEFAULT, ), ctaButtonStyleKey: HOME_CTA_BUTTON_STYLE_KEY, slides: (home?.slidesDto ?? []).map((slide) => ({ title: resolveLocalizedValue(translate, slide.titleKey, slide.titleKey), titleKey: slide.titleKey || '', subtitle: resolveLocalizedValue(translate, slide.subtitleKey, slide.subtitleKey), subtitleKey: slide.subtitleKey || '', styleClass: slide.styleClass || SLIDE_STYLE_DEFAULT, services: (slide.services ?? []).map((service) => ({ icon: service.icon || '', title: resolveLocalizedValue(translate, service.titleKey, service.titleKey), titleKey: service.titleKey || '', description: resolveLocalizedValue(translate, service.descriptionKey, service.descriptionKey), descriptionKey: service.descriptionKey || '', styleClass: service.styleClass || SLIDE_SERVICE_STYLE_DEFAULT, })), })), features: (home?.featuresDto ?? []).map((feature) => ({ icon: feature.icon || '', title: resolveLocalizedValue(translate, feature.titleKey, feature.titleKey), titleKey: feature.titleKey || '', description: resolveLocalizedValue(translate, feature.descriptionKey, feature.descriptionKey), descriptionKey: feature.descriptionKey || '', styleClass: feature.styleClass || FEATURE_CARD_STYLE_DEFAULT, })), solutions: (home?.solutionsDto ?? []).map((solution) => ({ icon: solution.icon || '', colorClass: solution.colorClass || '', title: resolveLocalizedValue(translate, solution.titleKey, solution.titleKey), titleKey: solution.titleKey || '', description: resolveLocalizedValue(translate, solution.descriptionKey, solution.descriptionKey), descriptionKey: solution.descriptionKey || '', styleClass: solution.styleClass || SOLUTION_CARD_STYLE_DEFAULT, })), } } function toSaveInput(content: HomeContent, cultureName: string) { return { cultureName, heroBackgroundImageKey: content.heroBackgroundImageKey, heroBackgroundImageValue: content.heroBackgroundImage, heroPrimaryCtaKey: content.heroPrimaryCtaKey, heroPrimaryCtaValue: content.heroPrimaryCtaLabel, heroPrimaryCtaStyleKey: content.heroPrimaryCtaStyleKey, heroPrimaryCtaStyleValue: content.heroPrimaryCtaStyle, heroSecondaryCtaKey: content.heroSecondaryCtaKey, heroSecondaryCtaValue: content.heroSecondaryCtaLabel, heroSecondaryCtaStyleKey: content.heroSecondaryCtaStyleKey, heroSecondaryCtaStyleValue: content.heroSecondaryCtaStyle, featuresTitleKey: content.featuresTitleKey, featuresTitleValue: content.featuresTitle, featuresTitleStyleKey: content.featuresTitleStyleKey, featuresTitleStyleValue: content.featuresTitleStyle, featuresSubtitleKey: content.featuresSubtitleKey, featuresSubtitleValue: content.featuresSubtitle, featuresSubtitleStyleKey: content.featuresSubtitleStyleKey, featuresSubtitleStyleValue: content.featuresSubtitleStyle, solutionsTitleKey: content.solutionsTitleKey, solutionsTitleValue: content.solutionsTitle, solutionsTitleStyleKey: content.solutionsTitleStyleKey, solutionsTitleStyleValue: content.solutionsTitleStyle, solutionsSubtitleKey: content.solutionsSubtitleKey, solutionsSubtitleValue: content.solutionsSubtitle, solutionsSubtitleStyleKey: content.solutionsSubtitleStyleKey, solutionsSubtitleStyleValue: content.solutionsSubtitleStyle, ctaTitleKey: content.ctaTitleKey, ctaTitleValue: content.ctaTitle, ctaTitleStyleKey: content.ctaTitleStyleKey, ctaTitleStyleValue: content.ctaTitleStyle, ctaSubtitleKey: content.ctaSubtitleKey, ctaSubtitleValue: content.ctaSubtitle, ctaSubtitleStyleKey: content.ctaSubtitleStyleKey, ctaSubtitleStyleValue: content.ctaSubtitleStyle, ctaButtonLabelKey: content.ctaButtonLabelKey, ctaButtonLabelValue: content.ctaButtonLabel, ctaButtonStyleKey: content.ctaButtonStyleKey, ctaButtonStyleValue: content.ctaButtonStyle, slides: content.slides.map((slide) => ({ titleKey: slide.titleKey, titleValue: slide.title, subtitleKey: slide.subtitleKey, subtitleValue: slide.subtitle, styleClass: slide.styleClass, services: slide.services.map((service) => ({ icon: service.icon, titleKey: service.titleKey, titleValue: service.title, descriptionKey: service.descriptionKey, descriptionValue: service.description, styleClass: service.styleClass, })), })), features: content.features.map((feature) => ({ icon: feature.icon, titleKey: feature.titleKey, titleValue: feature.title, descriptionKey: feature.descriptionKey, descriptionValue: feature.description, styleClass: feature.styleClass, })), solutions: content.solutions.map((solution) => ({ icon: solution.icon, colorClass: solution.colorClass, titleKey: solution.titleKey, titleValue: solution.title, descriptionKey: solution.descriptionKey, descriptionValue: solution.description, styleClass: solution.styleClass, })), } } function cardFields(path: string, item: HomeCardContent, iconLabel: string): DesignerField[] { return [ iconField(`${path}.icon`, iconLabel, item.icon, { placeholder: 'Ornek: FaChartBar' }), textField(`${path}.title`, item.titleKey, item.title), textAreaField(`${path}.description`, item.descriptionKey, item.description), styleField(`${path}.styleClass`, `${path}.styleClass`, item.styleClass), ] } const Home: React.FC = () => { const { openDemo } = useOptionalDemo() const { translate } = useLocalization() const [home, setHome] = useState() const [loading, setLoading] = useState(true) const [currentSlide, setCurrentSlide] = useState(0) useEffect(() => { const fetchHome = async () => { setLoading(true) try { const result = await getHome() setHome(result.data) } catch (error) { console.error('Home alinirken hata olustu:', error) } finally { setLoading(false) } } fetchHome() }, []) const initialContent = useMemo( () => (loading ? null : buildHomeContent(home, translate)), [home, loading, translate], ) const designer = usePageDesigner({ pageKey: 'Home', permission: PUBLIC_PAGE_DESIGN.HOME, initialContent, onSave: (content, cultureName) => saveHomePage(toSaveInput(content, cultureName)), }) const { content, isDesignMode, isPanelVisible, selectedBlockId, selectBlock } = designer const slides = useMemo(() => content?.slides ?? [], [content?.slides]) const slideCount = slides.length useEffect(() => { if (slideCount > 0 && currentSlide >= slideCount) { setCurrentSlide(0) } }, [currentSlide, slideCount]) // Tasarim modunda otomatik gecis kapalidir; aksi halde duzenlenen slayt kayiyor. useEffect(() => { if (slideCount < 2 || isDesignMode) { return } const timer = setInterval( () => setCurrentSlide((previous) => (previous + 1) % slideCount), SLIDE_INTERVAL_MS, ) return () => clearInterval(timer) }, [isDesignMode, slideCount]) const goToSlide = (offset: number) => setCurrentSlide((previous) => (previous + offset + Math.max(slideCount, 1)) % Math.max(slideCount, 1)) const selection: DesignerSelection | null = useMemo(() => { if (!content || !selectedBlockId) { return null } const iconLabel = translateLabel(translate, 'Public.designer.ikonAnahtari', 'Ikon anahtari') if (selectedBlockId === 'hero') { return { id: 'hero', title: 'Public.home.hero.*', description: translateLabel( translate, 'Public.designer.heroHint', 'Hero arka planini ve CTA metinlerini duzenleyin.', ), fields: [ textField('heroPrimaryCtaLabel', content.heroPrimaryCtaKey, content.heroPrimaryCtaLabel), textField('heroSecondaryCtaLabel', content.heroSecondaryCtaKey, content.heroSecondaryCtaLabel), imageField('heroBackgroundImage', content.heroBackgroundImageKey, content.heroBackgroundImage, { group: 'media', }), styleField('heroPrimaryCtaStyle', content.heroPrimaryCtaStyleKey, content.heroPrimaryCtaStyle), styleField( 'heroSecondaryCtaStyle', content.heroSecondaryCtaStyleKey, content.heroSecondaryCtaStyle, ), ], } } if (selectedBlockId.startsWith('slide-')) { const index = Number(selectedBlockId.replace('slide-', '')) const slide = content.slides[index] if (!slide) { return null } return { id: selectedBlockId, title: slide.titleKey || `Slide ${index + 1}`, description: translateLabel( translate, 'Public.designer.slideHint', 'Slayt baslik, alt baslik ve servis kartlarini duzenleyin.', ), fields: [ textField(`slides.${index}.title`, slide.titleKey, slide.title), textAreaField(`slides.${index}.subtitle`, slide.subtitleKey, slide.subtitle), styleField(`slides.${index}.styleClass`, `Slide${index + 1}.StyleClass`, slide.styleClass), ...slide.services.flatMap((service, serviceIndex) => { const path = `slides.${index}.services.${serviceIndex}` return [ iconField(`${path}.icon`, `Slide${index + 1}.Service${serviceIndex + 1}.Icon`, service.icon, { placeholder: 'Ornek: FaUsers', }), textField(`${path}.title`, service.titleKey, service.title), textAreaField(`${path}.description`, service.descriptionKey, service.description), styleField( `${path}.styleClass`, `Slide${index + 1}.Service${serviceIndex + 1}.StyleClass`, service.styleClass, ), ] }), ], } } if (selectedBlockId === 'features-heading') { return { id: selectedBlockId, title: content.featuresTitleKey, description: translateLabel( translate, 'Public.designer.headingHint', 'Bolum basliklarini duzenleyin.', ), fields: [ textField('featuresTitle', content.featuresTitleKey, content.featuresTitle), textAreaField('featuresSubtitle', content.featuresSubtitleKey, content.featuresSubtitle), styleField('featuresTitleStyle', content.featuresTitleStyleKey, content.featuresTitleStyle), styleField( 'featuresSubtitleStyle', content.featuresSubtitleStyleKey, content.featuresSubtitleStyle, ), ], } } if (selectedBlockId === 'solutions-heading') { return { id: selectedBlockId, title: content.solutionsTitleKey, description: translateLabel( translate, 'Public.designer.headingHint', 'Bolum basliklarini duzenleyin.', ), fields: [ textField('solutionsTitle', content.solutionsTitleKey, content.solutionsTitle), textAreaField('solutionsSubtitle', content.solutionsSubtitleKey, content.solutionsSubtitle), styleField('solutionsTitleStyle', content.solutionsTitleStyleKey, content.solutionsTitleStyle), styleField( 'solutionsSubtitleStyle', content.solutionsSubtitleStyleKey, content.solutionsSubtitleStyle, ), ], } } if (selectedBlockId.startsWith('feature-')) { const index = Number(selectedBlockId.replace('feature-', '')) const item = content.features[index] return item ? { id: selectedBlockId, title: item.titleKey || `Feature ${index + 1}`, description: translateLabel(translate, 'Public.designer.desc1', 'Kart icerigini duzenleyin.'), fields: cardFields(`features.${index}`, item, iconLabel), } : null } if (selectedBlockId.startsWith('solution-')) { const index = Number(selectedBlockId.replace('solution-', '')) const item = content.solutions[index] return item ? { id: selectedBlockId, title: item.titleKey || `Solution ${index + 1}`, description: translateLabel(translate, 'Public.designer.desc1', 'Kart icerigini duzenleyin.'), fields: [ ...cardFields(`solutions.${index}`, item, iconLabel), styleField(`solutions.${index}.colorClass`, `Solution${index + 1}.ColorClass`, item.colorClass), ], } : null } if (selectedBlockId === 'cta') { return { id: selectedBlockId, title: content.ctaTitleKey, description: translateLabel( translate, 'Public.designer.desc2', 'Sayfa alti cagri bolumunu duzenleyin.', ), fields: [ textField('ctaTitle', content.ctaTitleKey, content.ctaTitle), textAreaField('ctaSubtitle', content.ctaSubtitleKey, content.ctaSubtitle), textField('ctaButtonLabel', content.ctaButtonLabelKey, content.ctaButtonLabel), styleField('ctaTitleStyle', content.ctaTitleStyleKey, content.ctaTitleStyle), styleField('ctaSubtitleStyle', content.ctaSubtitleStyleKey, content.ctaSubtitleStyle), styleField('ctaButtonStyle', content.ctaButtonStyleKey, content.ctaButtonStyle), ], } } return null }, [content, selectedBlockId, translate]) if (loading) { return } return (
{/* Hero carousel */}
{slides.map((slide, index) => (
{ setCurrentSlide(index) selectBlock(id) }} >

{slide.title}

{slide.subtitle}

{content?.heroSecondaryCtaLabel}
{slide.services.map((service, serviceIndex) => (

{service.title}

{service.description}

))}
))}
{slideCount > 1 && ( <>
)}
{/* Tasarim modunda slayt secici; secilebilir blogun disinda durur ki tiklamalar yutulmasin. */} {isDesignMode && slideCount > 0 && (
{slides.map((slide, index) => ( ))}
)}
{/* Features */}

{content?.featuresTitle}

{content?.featuresSubtitle}

{content?.features.map((feature, index) => (

{feature.title}

{feature.description}

))}
{/* Solutions */}

{content?.solutionsTitle}

{content?.solutionsSubtitle}

{content?.solutions.map((solution, index) => (

{solution.title}

{solution.description}

))}
{/* CTA */}

{content?.ctaTitle}

{content?.ctaSubtitle}

{content?.ctaButtonLabel}
) } export default Home