2026-02-24 20:44:16 +00:00
|
|
|
import React, { useEffect, useState } from 'react'
|
|
|
|
|
import {
|
|
|
|
|
FaPhone,
|
|
|
|
|
FaBuilding,
|
|
|
|
|
FaCalendarAlt,
|
|
|
|
|
FaCalendarCheck,
|
|
|
|
|
FaRegComment,
|
|
|
|
|
FaMapMarkerAlt,
|
|
|
|
|
FaEnvelope,
|
|
|
|
|
FaIdCard,
|
|
|
|
|
} from 'react-icons/fa'
|
|
|
|
|
import { useLocalization } from '@/utils/hooks/useLocalization'
|
|
|
|
|
import { Helmet } from 'react-helmet'
|
|
|
|
|
import { ContactDto } from '@/proxy/contact/models'
|
2026-03-17 11:27:30 +00:00
|
|
|
import Loading from '@/components/shared/Loading'
|
|
|
|
|
import { getContact, saveContactPage } from '@/services/contact.service'
|
2026-02-24 20:44:16 +00:00
|
|
|
import { APP_NAME } from '@/constants/app.constant'
|
2026-03-17 11:27:30 +00:00
|
|
|
import { useStoreActions, useStoreState } from '@/store'
|
|
|
|
|
import DesignerDrawer from './designer/DesignerDrawer'
|
|
|
|
|
import SelectableBlock from './designer/SelectableBlock'
|
|
|
|
|
import { DesignerSelection } from './designer/types'
|
|
|
|
|
import { useDesignerState } from './designer/useDesignerState'
|
|
|
|
|
|
|
|
|
|
interface ContactContent {
|
|
|
|
|
heroTitle: string
|
|
|
|
|
heroTitleKey: string
|
|
|
|
|
heroSubtitle: string
|
|
|
|
|
heroSubtitleKey: string
|
|
|
|
|
heroImage: string
|
|
|
|
|
heroImageKey: string
|
|
|
|
|
contactInfoTitle: string
|
|
|
|
|
contactInfoTitleKey: string
|
|
|
|
|
address: string
|
|
|
|
|
addressKey: string
|
|
|
|
|
phoneNumber: string
|
|
|
|
|
email: string
|
|
|
|
|
location: string
|
|
|
|
|
taxNumber: string
|
|
|
|
|
bankTitle: string
|
|
|
|
|
bankTitleKey: string
|
|
|
|
|
bankAccountHolder: string
|
|
|
|
|
bankBranch: string
|
|
|
|
|
bankAccountNumber: string
|
|
|
|
|
bankIban: string
|
|
|
|
|
workHoursTitle: string
|
|
|
|
|
workHoursTitleKey: string
|
|
|
|
|
workWeekday: string
|
|
|
|
|
workWeekdayKey: string
|
|
|
|
|
workWeekend: string
|
|
|
|
|
workWeekendKey: string
|
|
|
|
|
workWhatsapp: string
|
|
|
|
|
workWhatsappKey: string
|
|
|
|
|
mapTitle: string
|
|
|
|
|
mapTitleKey: string
|
|
|
|
|
mapSrc: string
|
|
|
|
|
mapWidth: string
|
|
|
|
|
mapHeight: string
|
|
|
|
|
mapAllowFullScreen: string
|
|
|
|
|
mapLoading: string
|
|
|
|
|
mapReferrerPolicy: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const CONTACT_HERO_TITLE_KEY = 'App.Contact'
|
|
|
|
|
const CONTACT_HERO_SUBTITLE_KEY = 'Public.contact.subtitle'
|
|
|
|
|
const CONTACT_HERO_IMAGE_KEY = 'Public.contact.heroImage'
|
|
|
|
|
const CONTACT_HERO_IMAGE_DEFAULT =
|
|
|
|
|
'https://images.pexels.com/photos/3183171/pexels-photo-3183171.jpeg?auto=compress&cs=tinysrgb&w=1920'
|
|
|
|
|
const CONTACT_INFO_TITLE_KEY = 'Abp.Identity.User.UserInformation.ContactInformation'
|
|
|
|
|
const CONTACT_ADDRESS_KEY = 'Public.contact.address.full'
|
|
|
|
|
const CONTACT_BANK_TITLE_KEY = 'Public.contact.bank.title'
|
|
|
|
|
const CONTACT_WORK_HOURS_TITLE_KEY = 'App.Definitions.WorkHour'
|
|
|
|
|
const CONTACT_WORK_WEEKDAY_KEY = 'Public.contact.workHours.weekday'
|
|
|
|
|
const CONTACT_WORK_WEEKEND_KEY = 'Public.contact.workHours.weekend'
|
|
|
|
|
const CONTACT_WORK_WHATSAPP_KEY = 'Public.contact.workHours.whatsapp'
|
|
|
|
|
const CONTACT_MAP_TITLE_KEY = 'Public.contact.location'
|
|
|
|
|
|
|
|
|
|
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 buildContactContent(
|
|
|
|
|
contact: ContactDto | undefined,
|
|
|
|
|
translate: (key: string) => string,
|
|
|
|
|
): ContactContent {
|
|
|
|
|
return {
|
|
|
|
|
heroTitle: resolveLocalizedValue(translate, CONTACT_HERO_TITLE_KEY, 'Contact'),
|
|
|
|
|
heroTitleKey: CONTACT_HERO_TITLE_KEY,
|
|
|
|
|
heroSubtitle: resolveLocalizedValue(translate, CONTACT_HERO_SUBTITLE_KEY),
|
|
|
|
|
heroSubtitleKey: CONTACT_HERO_SUBTITLE_KEY,
|
|
|
|
|
heroImage: resolveLocalizedValue(translate, CONTACT_HERO_IMAGE_KEY, CONTACT_HERO_IMAGE_DEFAULT),
|
|
|
|
|
heroImageKey: CONTACT_HERO_IMAGE_KEY,
|
|
|
|
|
contactInfoTitle: resolveLocalizedValue(translate, CONTACT_INFO_TITLE_KEY),
|
|
|
|
|
contactInfoTitleKey: CONTACT_INFO_TITLE_KEY,
|
|
|
|
|
address: resolveLocalizedValue(translate, contact?.address || CONTACT_ADDRESS_KEY),
|
|
|
|
|
addressKey: contact?.address || CONTACT_ADDRESS_KEY,
|
|
|
|
|
phoneNumber: contact?.phoneNumber || '',
|
|
|
|
|
email: contact?.email || '',
|
|
|
|
|
location: contact?.location || '',
|
|
|
|
|
taxNumber: contact?.taxNumber || '',
|
|
|
|
|
bankTitle: resolveLocalizedValue(translate, CONTACT_BANK_TITLE_KEY),
|
|
|
|
|
bankTitleKey: CONTACT_BANK_TITLE_KEY,
|
|
|
|
|
bankAccountHolder: contact?.bankDto?.accountHolder || '',
|
|
|
|
|
bankBranch: contact?.bankDto?.branch || '',
|
|
|
|
|
bankAccountNumber: contact?.bankDto?.accountNumber || '',
|
|
|
|
|
bankIban: contact?.bankDto?.iban || '',
|
|
|
|
|
workHoursTitle: resolveLocalizedValue(translate, CONTACT_WORK_HOURS_TITLE_KEY),
|
|
|
|
|
workHoursTitleKey: CONTACT_WORK_HOURS_TITLE_KEY,
|
|
|
|
|
workWeekday: resolveLocalizedValue(translate, contact?.workHoursDto?.weekday || CONTACT_WORK_WEEKDAY_KEY),
|
|
|
|
|
workWeekdayKey: contact?.workHoursDto?.weekday || CONTACT_WORK_WEEKDAY_KEY,
|
|
|
|
|
workWeekend: resolveLocalizedValue(translate, contact?.workHoursDto?.weekend || CONTACT_WORK_WEEKEND_KEY),
|
|
|
|
|
workWeekendKey: contact?.workHoursDto?.weekend || CONTACT_WORK_WEEKEND_KEY,
|
|
|
|
|
workWhatsapp: resolveLocalizedValue(
|
|
|
|
|
translate,
|
|
|
|
|
contact?.workHoursDto?.whatsapp || CONTACT_WORK_WHATSAPP_KEY,
|
|
|
|
|
),
|
|
|
|
|
workWhatsappKey: contact?.workHoursDto?.whatsapp || CONTACT_WORK_WHATSAPP_KEY,
|
|
|
|
|
mapTitle: resolveLocalizedValue(translate, contact?.mapDto?.title || CONTACT_MAP_TITLE_KEY),
|
|
|
|
|
mapTitleKey: contact?.mapDto?.title || CONTACT_MAP_TITLE_KEY,
|
|
|
|
|
mapSrc: contact?.mapDto?.src || '',
|
|
|
|
|
mapWidth: contact?.mapDto?.width || '100%',
|
|
|
|
|
mapHeight: contact?.mapDto?.height || '700',
|
|
|
|
|
mapAllowFullScreen: String(contact?.mapDto?.allowFullScreen ?? true),
|
|
|
|
|
mapLoading: contact?.mapDto?.loading || 'lazy',
|
|
|
|
|
mapReferrerPolicy: contact?.mapDto?.referrerPolicy || 'no-referrer-when-downgrade',
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
|
|
|
const Contact: React.FC = () => {
|
|
|
|
|
const { translate } = useLocalization()
|
2026-03-17 11:27:30 +00:00
|
|
|
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)),
|
|
|
|
|
)
|
|
|
|
|
|
2026-02-24 20:44:16 +00:00
|
|
|
const [loading, setLoading] = useState(true)
|
2026-03-17 11:27:30 +00:00
|
|
|
const [isSaving, setIsSaving] = useState(false)
|
|
|
|
|
const [isPanelVisible, setIsPanelVisible] = useState(true)
|
2026-02-24 20:44:16 +00:00
|
|
|
const [contact, setContact] = useState<ContactDto>()
|
|
|
|
|
|
2026-03-17 11:27:30 +00:00
|
|
|
const initialContent = !loading ? buildContactContent(contact, translate) : null
|
|
|
|
|
const {
|
|
|
|
|
content,
|
|
|
|
|
isDesignMode,
|
|
|
|
|
selectedBlockId,
|
|
|
|
|
selectedLanguage,
|
|
|
|
|
supportedLanguages,
|
|
|
|
|
setContent,
|
|
|
|
|
setSelectedBlockId,
|
|
|
|
|
resetContent,
|
|
|
|
|
} = useDesignerState<ContactContent>('contact', initialContent, {
|
|
|
|
|
currentLanguage,
|
|
|
|
|
supportedLanguages: editorLanguages,
|
|
|
|
|
})
|
|
|
|
|
|
2026-02-24 20:44:16 +00:00
|
|
|
useEffect(() => {
|
|
|
|
|
setLoading(true)
|
2026-03-17 11:27:30 +00:00
|
|
|
const fetchContact = async () => {
|
2026-02-24 20:44:16 +00:00
|
|
|
try {
|
|
|
|
|
const result = await getContact()
|
|
|
|
|
setContact(result.data)
|
|
|
|
|
} catch (error) {
|
2026-03-17 11:27:30 +00:00
|
|
|
console.error('Contact alinirken hata olustu:', error)
|
2026-02-24 20:44:16 +00:00
|
|
|
} finally {
|
|
|
|
|
setLoading(false)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-17 11:27:30 +00:00
|
|
|
fetchContact()
|
2026-02-24 20:44:16 +00:00
|
|
|
}, [])
|
|
|
|
|
|
2026-03-17 11:27:30 +00:00
|
|
|
const updateContent = (updater: (current: ContactContent) => ContactContent) => {
|
|
|
|
|
setContent((current) => {
|
|
|
|
|
if (!current) {
|
|
|
|
|
return current
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return updater(current)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleFieldChange = (fieldKey: string, value: string | string[]) => {
|
|
|
|
|
updateContent((current) => ({
|
|
|
|
|
...current,
|
|
|
|
|
[fieldKey]: value as string,
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const selectedSelection: DesignerSelection | null = React.useMemo(() => {
|
|
|
|
|
if (!content || !selectedBlockId) {
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (selectedBlockId === 'hero') {
|
|
|
|
|
return {
|
|
|
|
|
id: 'hero',
|
|
|
|
|
title: 'Public.contact.hero.*',
|
|
|
|
|
description: 'Hero basligi, alt basligi ve arka plan gorselini duzenleyin.',
|
|
|
|
|
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 === 'contact-info') {
|
|
|
|
|
return {
|
|
|
|
|
id: selectedBlockId,
|
|
|
|
|
title: content.contactInfoTitleKey,
|
|
|
|
|
description: 'Iletisim bilgilerini duzenleyin.',
|
|
|
|
|
fields: [
|
|
|
|
|
{
|
|
|
|
|
key: 'contactInfoTitle',
|
|
|
|
|
label: content.contactInfoTitleKey,
|
|
|
|
|
type: 'text',
|
|
|
|
|
value: content.contactInfoTitle,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'address',
|
|
|
|
|
label: content.addressKey,
|
|
|
|
|
type: 'textarea',
|
|
|
|
|
value: content.address,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'phoneNumber',
|
|
|
|
|
label: 'Public.contact.phone',
|
|
|
|
|
type: 'text',
|
|
|
|
|
value: content.phoneNumber,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'email',
|
|
|
|
|
label: 'Public.contact.email',
|
|
|
|
|
type: 'text',
|
|
|
|
|
value: content.email,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'location',
|
|
|
|
|
label: 'Public.contact.location.plain',
|
|
|
|
|
type: 'text',
|
|
|
|
|
value: content.location,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'taxNumber',
|
|
|
|
|
label: 'Public.contact.taxNumber',
|
|
|
|
|
type: 'text',
|
|
|
|
|
value: content.taxNumber,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (selectedBlockId === 'bank') {
|
|
|
|
|
return {
|
|
|
|
|
id: selectedBlockId,
|
|
|
|
|
title: content.bankTitleKey,
|
|
|
|
|
description: 'Banka bilgilerini duzenleyin.',
|
|
|
|
|
fields: [
|
|
|
|
|
{
|
|
|
|
|
key: 'bankTitle',
|
|
|
|
|
label: content.bankTitleKey,
|
|
|
|
|
type: 'text',
|
|
|
|
|
value: content.bankTitle,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'bankAccountHolder',
|
|
|
|
|
label: 'Public.contact.bank.accountHolder',
|
|
|
|
|
type: 'text',
|
|
|
|
|
value: content.bankAccountHolder,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'bankBranch',
|
|
|
|
|
label: 'Public.contact.bank.branch',
|
|
|
|
|
type: 'text',
|
|
|
|
|
value: content.bankBranch,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'bankAccountNumber',
|
|
|
|
|
label: 'Public.contact.bank.accountNumber',
|
|
|
|
|
type: 'text',
|
|
|
|
|
value: content.bankAccountNumber,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'bankIban',
|
|
|
|
|
label: 'Public.contact.bank.iban',
|
|
|
|
|
type: 'text',
|
|
|
|
|
value: content.bankIban,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (selectedBlockId === 'work-hours') {
|
|
|
|
|
return {
|
|
|
|
|
id: selectedBlockId,
|
|
|
|
|
title: content.workHoursTitleKey,
|
|
|
|
|
description: 'Calisma saatleri metinlerini duzenleyin.',
|
|
|
|
|
fields: [
|
|
|
|
|
{
|
|
|
|
|
key: 'workHoursTitle',
|
|
|
|
|
label: content.workHoursTitleKey,
|
|
|
|
|
type: 'text',
|
|
|
|
|
value: content.workHoursTitle,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'workWeekday',
|
|
|
|
|
label: content.workWeekdayKey,
|
|
|
|
|
type: 'text',
|
|
|
|
|
value: content.workWeekday,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'workWeekend',
|
|
|
|
|
label: content.workWeekendKey,
|
|
|
|
|
type: 'text',
|
|
|
|
|
value: content.workWeekend,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'workWhatsapp',
|
|
|
|
|
label: content.workWhatsappKey,
|
|
|
|
|
type: 'text',
|
|
|
|
|
value: content.workWhatsapp,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (selectedBlockId === 'map') {
|
|
|
|
|
return {
|
|
|
|
|
id: selectedBlockId,
|
|
|
|
|
title: content.mapTitleKey,
|
|
|
|
|
description: 'Harita basligi ve iframe ayarlarini duzenleyin.',
|
|
|
|
|
fields: [
|
|
|
|
|
{
|
|
|
|
|
key: 'mapTitle',
|
|
|
|
|
label: content.mapTitleKey,
|
|
|
|
|
type: 'text',
|
|
|
|
|
value: content.mapTitle,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'mapSrc',
|
|
|
|
|
label: 'Public.contact.map.src',
|
|
|
|
|
type: 'textarea',
|
|
|
|
|
value: content.mapSrc,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'mapWidth',
|
|
|
|
|
label: 'Public.contact.map.width',
|
|
|
|
|
type: 'text',
|
|
|
|
|
value: content.mapWidth,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'mapHeight',
|
|
|
|
|
label: 'Public.contact.map.height',
|
|
|
|
|
type: 'text',
|
|
|
|
|
value: content.mapHeight,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'mapAllowFullScreen',
|
|
|
|
|
label: 'Public.contact.map.allowFullScreen',
|
|
|
|
|
type: 'text',
|
|
|
|
|
value: content.mapAllowFullScreen,
|
|
|
|
|
placeholder: 'true veya false',
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'mapLoading',
|
|
|
|
|
label: 'Public.contact.map.loading',
|
|
|
|
|
type: 'text',
|
|
|
|
|
value: content.mapLoading,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
key: 'mapReferrerPolicy',
|
|
|
|
|
label: 'Public.contact.map.referrerPolicy',
|
|
|
|
|
type: 'text',
|
|
|
|
|
value: content.mapReferrerPolicy,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null
|
|
|
|
|
}, [content, selectedBlockId])
|
|
|
|
|
|
|
|
|
|
const handleSaveAndExit = async () => {
|
|
|
|
|
if (!content || isSaving) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setIsSaving(true)
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await saveContactPage({
|
|
|
|
|
cultureName: selectedLanguage,
|
|
|
|
|
heroTitleKey: content.heroTitleKey,
|
|
|
|
|
heroTitleValue: content.heroTitle,
|
|
|
|
|
heroSubtitleKey: content.heroSubtitleKey,
|
|
|
|
|
heroSubtitleValue: content.heroSubtitle,
|
|
|
|
|
heroImageKey: content.heroImageKey,
|
|
|
|
|
heroImageValue: content.heroImage,
|
|
|
|
|
contactInfoTitleKey: content.contactInfoTitleKey,
|
|
|
|
|
contactInfoTitleValue: content.contactInfoTitle,
|
|
|
|
|
addressKey: content.addressKey,
|
|
|
|
|
addressValue: content.address,
|
|
|
|
|
phoneNumber: content.phoneNumber,
|
|
|
|
|
email: content.email,
|
|
|
|
|
location: content.location,
|
|
|
|
|
taxNumber: content.taxNumber,
|
|
|
|
|
bankTitleKey: content.bankTitleKey,
|
|
|
|
|
bankTitleValue: content.bankTitle,
|
|
|
|
|
bankAccountHolder: content.bankAccountHolder,
|
|
|
|
|
bankBranch: content.bankBranch,
|
|
|
|
|
bankAccountNumber: content.bankAccountNumber,
|
|
|
|
|
bankIban: content.bankIban,
|
|
|
|
|
workHoursTitleKey: content.workHoursTitleKey,
|
|
|
|
|
workHoursTitleValue: content.workHoursTitle,
|
|
|
|
|
workWeekdayKey: content.workWeekdayKey,
|
|
|
|
|
workWeekdayValue: content.workWeekday,
|
|
|
|
|
workWeekendKey: content.workWeekendKey,
|
|
|
|
|
workWeekendValue: content.workWeekend,
|
|
|
|
|
workWhatsappKey: content.workWhatsappKey,
|
|
|
|
|
workWhatsappValue: content.workWhatsapp,
|
|
|
|
|
mapTitleKey: content.mapTitleKey,
|
|
|
|
|
mapTitleValue: content.mapTitle,
|
|
|
|
|
mapSrc: content.mapSrc,
|
|
|
|
|
mapWidth: content.mapWidth,
|
|
|
|
|
mapHeight: content.mapHeight,
|
|
|
|
|
mapAllowFullScreen: content.mapAllowFullScreen.toLowerCase() === 'true',
|
|
|
|
|
mapLoading: content.mapLoading,
|
|
|
|
|
mapReferrerPolicy: content.mapReferrerPolicy,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
await getConfig(false)
|
|
|
|
|
setSelectedBlockId(null)
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Contact tasarimi kaydedilemedi:', error)
|
|
|
|
|
} finally {
|
|
|
|
|
setIsSaving(false)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleLanguageChange = (language: string) => {
|
|
|
|
|
setLang(language)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleSelectBlock = (blockId: string) => {
|
|
|
|
|
setSelectedBlockId(blockId)
|
|
|
|
|
if (!isPanelVisible) {
|
|
|
|
|
setIsPanelVisible(true)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-24 20:44:16 +00:00
|
|
|
if (loading) {
|
|
|
|
|
return (
|
|
|
|
|
<div className="flex items-center justify-center min-h-screen bg-gray-50">
|
|
|
|
|
<div className="text-center">
|
|
|
|
|
<Loading loading={loading} />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
2026-03-17 11:27:30 +00:00
|
|
|
<div className={`min-h-screen bg-gray-50 ${isDesignMode && isPanelVisible ? 'xl:pr-[420px]' : ''}`}>
|
2026-02-24 20:44:16 +00:00
|
|
|
<Helmet
|
|
|
|
|
titleTemplate={`%s | ${APP_NAME}`}
|
|
|
|
|
title={translate('::App.Contact')}
|
|
|
|
|
defaultTitle={APP_NAME}
|
|
|
|
|
></Helmet>
|
|
|
|
|
|
2026-03-17 11:27:30 +00:00
|
|
|
{isDesignMode && (
|
|
|
|
|
<div className="fixed bottom-6 left-6 z-40 rounded-full bg-slate-900 px-4 py-2 text-sm font-medium text-white shadow-xl">
|
|
|
|
|
Contact designer aktif
|
2026-02-24 20:44:16 +00:00
|
|
|
</div>
|
2026-03-17 11:27:30 +00:00
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{isDesignMode && !isPanelVisible && (
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
onClick={() => setIsPanelVisible(true)}
|
|
|
|
|
className="fixed right-4 top-1/2 z-40 -translate-y-1/2 rounded-full bg-slate-900 px-4 py-2 text-sm font-semibold text-white shadow-xl"
|
|
|
|
|
>
|
|
|
|
|
{translate('::Public.designer.showPanel')}
|
|
|
|
|
</button>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
<SelectableBlock
|
|
|
|
|
id="hero"
|
|
|
|
|
isActive={selectedBlockId === 'hero'}
|
|
|
|
|
isDesignMode={isDesignMode}
|
|
|
|
|
onSelect={handleSelectBlock}
|
|
|
|
|
>
|
|
|
|
|
<div className="relative bg-blue-900 py-12 text-white">
|
|
|
|
|
<div
|
|
|
|
|
className="absolute inset-0 opacity-20"
|
|
|
|
|
style={{
|
|
|
|
|
backgroundImage: `url("${content?.heroImage || CONTACT_HERO_IMAGE_DEFAULT}")`,
|
|
|
|
|
backgroundSize: 'cover',
|
|
|
|
|
backgroundPosition: 'center',
|
|
|
|
|
}}
|
|
|
|
|
></div>
|
|
|
|
|
<div className="container relative mx-auto pt-20">
|
|
|
|
|
<h1 className="ml-4 mb-2 mt-3 text-5xl font-bold text-white">{content?.heroTitle}</h1>
|
|
|
|
|
<p className="ml-4 max-w-3xl text-xl">{content?.heroSubtitle}</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</SelectableBlock>
|
2026-02-24 20:44:16 +00:00
|
|
|
|
|
|
|
|
<div className="py-16">
|
|
|
|
|
<div className="container mx-auto px-4">
|
2026-03-17 11:27:30 +00:00
|
|
|
<div className="grid grid-cols-1 gap-12 lg:grid-cols-2">
|
2026-02-24 20:44:16 +00:00
|
|
|
<div className="space-y-4">
|
2026-03-17 11:27:30 +00:00
|
|
|
<SelectableBlock
|
|
|
|
|
id="contact-info"
|
|
|
|
|
isActive={selectedBlockId === 'contact-info'}
|
|
|
|
|
isDesignMode={isDesignMode}
|
|
|
|
|
onSelect={handleSelectBlock}
|
|
|
|
|
>
|
|
|
|
|
<div className="rounded-xl bg-white p-8 shadow-lg">
|
|
|
|
|
<h2 className="mb-6 text-2xl font-bold text-gray-900">{content?.contactInfoTitle}</h2>
|
|
|
|
|
<div className="space-y-4">
|
|
|
|
|
<div className="flex items-start space-x-2">
|
|
|
|
|
<FaMapMarkerAlt className="mt-1 h-5 w-5 flex-shrink-0 text-blue-600" />
|
|
|
|
|
<div>
|
|
|
|
|
<p className="text-gray-600">{content?.address}</p>
|
|
|
|
|
</div>
|
2026-02-24 20:44:16 +00:00
|
|
|
</div>
|
2026-03-17 11:27:30 +00:00
|
|
|
<div className="flex items-start space-x-2">
|
|
|
|
|
<FaPhone className="h-5 w-5 flex-shrink-0 text-blue-600" />
|
|
|
|
|
<div>
|
|
|
|
|
<p className="text-gray-600">{content?.phoneNumber}</p>
|
|
|
|
|
</div>
|
2026-02-24 20:44:16 +00:00
|
|
|
</div>
|
2026-03-17 11:27:30 +00:00
|
|
|
<div className="flex items-start space-x-2">
|
|
|
|
|
<FaEnvelope className="h-5 w-5 flex-shrink-0 text-blue-600" />
|
|
|
|
|
<div>
|
|
|
|
|
<p className="text-gray-600">
|
|
|
|
|
<a href={`mailto:${content?.email}`} className="text-blue-600 hover:underline">
|
|
|
|
|
{content?.email}
|
|
|
|
|
</a>
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
2026-02-24 20:44:16 +00:00
|
|
|
</div>
|
2026-03-17 11:27:30 +00:00
|
|
|
<div className="flex items-start space-x-2">
|
|
|
|
|
<FaBuilding className="h-5 w-5 flex-shrink-0 text-blue-600" />
|
|
|
|
|
<div>
|
|
|
|
|
<p className="text-gray-600">{content?.location}</p>
|
|
|
|
|
</div>
|
2026-02-24 20:44:16 +00:00
|
|
|
</div>
|
2026-03-17 11:27:30 +00:00
|
|
|
<div className="flex items-start space-x-2">
|
|
|
|
|
<FaIdCard className="h-5 w-5 flex-shrink-0 text-blue-600" />
|
|
|
|
|
<div>
|
|
|
|
|
<p className="text-gray-600">{content?.taxNumber}</p>
|
|
|
|
|
</div>
|
2026-02-24 20:44:16 +00:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-03-17 11:27:30 +00:00
|
|
|
</SelectableBlock>
|
2026-02-24 20:44:16 +00:00
|
|
|
|
2026-03-17 11:27:30 +00:00
|
|
|
<SelectableBlock
|
|
|
|
|
id="bank"
|
|
|
|
|
isActive={selectedBlockId === 'bank'}
|
|
|
|
|
isDesignMode={isDesignMode}
|
|
|
|
|
onSelect={handleSelectBlock}
|
|
|
|
|
>
|
|
|
|
|
<div className="rounded-xl bg-white p-8 shadow-lg">
|
|
|
|
|
<h2 className="mb-6 text-2xl font-bold text-gray-900">{content?.bankTitle}</h2>
|
|
|
|
|
<div className="mb-2">
|
|
|
|
|
<img
|
|
|
|
|
src="/img/enpara.svg"
|
|
|
|
|
alt="Enpara Logo"
|
|
|
|
|
className="mt-1 w-24 flex-shrink-0 object-contain"
|
|
|
|
|
/>
|
|
|
|
|
<div>
|
|
|
|
|
<h3 className="mb-1 font-semibold text-gray-900">{content?.bankAccountHolder}</h3>
|
|
|
|
|
<p className="mb-1 ml-1 text-gray-600">{content?.bankBranch}</p>
|
|
|
|
|
<p className="mb-1 ml-1 text-gray-600">{content?.bankAccountNumber}</p>
|
|
|
|
|
<p className="mb-1 ml-1 text-gray-600">{content?.bankIban}</p>
|
|
|
|
|
</div>
|
2026-02-24 20:44:16 +00:00
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-03-17 11:27:30 +00:00
|
|
|
</SelectableBlock>
|
2026-02-24 20:44:16 +00:00
|
|
|
|
2026-03-17 11:27:30 +00:00
|
|
|
<SelectableBlock
|
|
|
|
|
id="work-hours"
|
|
|
|
|
isActive={selectedBlockId === 'work-hours'}
|
|
|
|
|
isDesignMode={isDesignMode}
|
|
|
|
|
onSelect={handleSelectBlock}
|
|
|
|
|
>
|
|
|
|
|
<div className="rounded-xl bg-white p-8 shadow-lg">
|
|
|
|
|
<h2 className="mb-6 text-2xl font-bold text-gray-900">{content?.workHoursTitle}</h2>
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
<div className="flex items-center space-x-2">
|
|
|
|
|
<FaCalendarAlt className="h-5 w-5 text-blue-500" />
|
|
|
|
|
<p className="text-gray-600">{content?.workWeekday}</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex items-center space-x-2">
|
|
|
|
|
<FaCalendarCheck className="h-5 w-5 text-blue-500" />
|
|
|
|
|
<p className="text-gray-600">{content?.workWeekend}</p>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex items-center space-x-2">
|
|
|
|
|
<FaRegComment className="h-5 w-5 text-green-500" />
|
|
|
|
|
<p className="text-gray-600">{content?.workWhatsapp}</p>
|
|
|
|
|
</div>
|
2026-02-24 20:44:16 +00:00
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-03-17 11:27:30 +00:00
|
|
|
</SelectableBlock>
|
2026-02-24 20:44:16 +00:00
|
|
|
</div>
|
|
|
|
|
|
2026-03-17 11:27:30 +00:00
|
|
|
<SelectableBlock
|
|
|
|
|
id="map"
|
|
|
|
|
isActive={selectedBlockId === 'map'}
|
|
|
|
|
isDesignMode={isDesignMode}
|
|
|
|
|
onSelect={handleSelectBlock}
|
|
|
|
|
>
|
|
|
|
|
<div className="rounded-xl bg-white p-8 shadow-lg">
|
|
|
|
|
<h2 className="mb-2 text-center text-2xl font-bold text-gray-900">{content?.mapTitle}</h2>
|
|
|
|
|
<div className="aspect-w-16 aspect-h-9 overflow-hidden rounded-xl bg-gray-200">
|
|
|
|
|
<iframe
|
|
|
|
|
src={content?.mapSrc}
|
|
|
|
|
width={content?.mapWidth}
|
|
|
|
|
style={{ border: 0 }}
|
|
|
|
|
height={content?.mapHeight}
|
|
|
|
|
allowFullScreen={(content?.mapAllowFullScreen || '').toLowerCase() === 'true'}
|
|
|
|
|
loading={content?.mapLoading as 'lazy' | 'eager' | undefined}
|
|
|
|
|
referrerPolicy={content?.mapReferrerPolicy as React.HTMLAttributeReferrerPolicy | undefined}
|
|
|
|
|
></iframe>
|
|
|
|
|
</div>
|
2026-02-24 20:44:16 +00:00
|
|
|
</div>
|
2026-03-17 11:27:30 +00:00
|
|
|
</SelectableBlock>
|
2026-02-24 20:44:16 +00:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-03-17 11:27:30 +00:00
|
|
|
|
|
|
|
|
<DesignerDrawer
|
|
|
|
|
isOpen={isDesignMode && isPanelVisible}
|
|
|
|
|
selection={selectedSelection}
|
|
|
|
|
pageTitle="Contact"
|
|
|
|
|
selectedLanguage={selectedLanguage}
|
|
|
|
|
languages={
|
|
|
|
|
languageOptions.length > 0
|
|
|
|
|
? languageOptions
|
|
|
|
|
: supportedLanguages.map((language) => ({
|
|
|
|
|
key: language,
|
|
|
|
|
cultureName: language,
|
|
|
|
|
displayName: language.toUpperCase(),
|
|
|
|
|
}))
|
|
|
|
|
}
|
|
|
|
|
onClose={() => setIsPanelVisible(false)}
|
|
|
|
|
onSave={handleSaveAndExit}
|
|
|
|
|
onLanguageChange={handleLanguageChange}
|
|
|
|
|
onReset={resetContent}
|
|
|
|
|
onFieldChange={handleFieldChange}
|
|
|
|
|
/>
|
2026-02-24 20:44:16 +00:00
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default Contact
|