Public Designer için style ve css
This commit is contained in:
parent
900f2815f9
commit
ffa3ff2d63
18 changed files with 1124 additions and 286 deletions
|
|
@ -66,6 +66,7 @@ public class HomeSlideDto
|
|||
{
|
||||
public string TitleKey { get; set; }
|
||||
public string SubtitleKey { get; set; }
|
||||
public string StyleClass { get; set; }
|
||||
public List<HomeSlideServiceDto> Services { get; set; } = [];
|
||||
}
|
||||
|
||||
|
|
@ -74,6 +75,7 @@ public class HomeSlideServiceDto
|
|||
public string Icon { get; set; }
|
||||
public string TitleKey { get; set; }
|
||||
public string DescriptionKey { get; set; }
|
||||
public string StyleClass { get; set; }
|
||||
}
|
||||
|
||||
public class HomeFeatureDto
|
||||
|
|
@ -81,6 +83,7 @@ public class HomeFeatureDto
|
|||
public string Icon { get; set; }
|
||||
public string TitleKey { get; set; }
|
||||
public string DescriptionKey { get; set; }
|
||||
public string StyleClass { get; set; }
|
||||
}
|
||||
|
||||
public class HomeSolutionDto
|
||||
|
|
@ -89,4 +92,5 @@ public class HomeSolutionDto
|
|||
public string ColorClass { get; set; }
|
||||
public string TitleKey { get; set; }
|
||||
public string DescriptionKey { get; set; }
|
||||
public string StyleClass { get; set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ public class SaveAboutPageInput
|
|||
public List<SaveAboutStatInput> Stats { get; set; } = [];
|
||||
public List<SaveLocalizedTextInput> Descriptions { get; set; } = [];
|
||||
public List<SaveAboutSectionInput> Sections { get; set; } = [];
|
||||
public List<SaveLocalizedTextInput> StyleTexts { get; set; } = [];
|
||||
}
|
||||
|
||||
public class SaveAboutStatInput
|
||||
|
|
@ -58,6 +59,7 @@ public class SaveServicesPageInput
|
|||
public string CtaButtonLabelValue { get; set; }
|
||||
public List<SaveServiceItemInput> ServiceItems { get; set; } = [];
|
||||
public List<SaveServiceItemInput> SupportItems { get; set; } = [];
|
||||
public List<SaveLocalizedTextInput> StyleTexts { get; set; } = [];
|
||||
}
|
||||
|
||||
public class SaveServiceItemInput
|
||||
|
|
@ -84,22 +86,40 @@ public class SaveHomePageInput
|
|||
public string HeroBackgroundImageValue { get; set; }
|
||||
public string HeroPrimaryCtaKey { get; set; }
|
||||
public string HeroPrimaryCtaValue { get; set; }
|
||||
public string HeroPrimaryCtaStyleKey { get; set; }
|
||||
public string HeroPrimaryCtaStyleValue { get; set; }
|
||||
public string HeroSecondaryCtaKey { get; set; }
|
||||
public string HeroSecondaryCtaValue { get; set; }
|
||||
public string HeroSecondaryCtaStyleKey { get; set; }
|
||||
public string HeroSecondaryCtaStyleValue { get; set; }
|
||||
public string FeaturesTitleKey { get; set; }
|
||||
public string FeaturesTitleValue { get; set; }
|
||||
public string FeaturesTitleStyleKey { get; set; }
|
||||
public string FeaturesTitleStyleValue { get; set; }
|
||||
public string FeaturesSubtitleKey { get; set; }
|
||||
public string FeaturesSubtitleValue { get; set; }
|
||||
public string FeaturesSubtitleStyleKey { get; set; }
|
||||
public string FeaturesSubtitleStyleValue { get; set; }
|
||||
public string SolutionsTitleKey { get; set; }
|
||||
public string SolutionsTitleValue { get; set; }
|
||||
public string SolutionsTitleStyleKey { get; set; }
|
||||
public string SolutionsTitleStyleValue { get; set; }
|
||||
public string SolutionsSubtitleKey { get; set; }
|
||||
public string SolutionsSubtitleValue { get; set; }
|
||||
public string SolutionsSubtitleStyleKey { get; set; }
|
||||
public string SolutionsSubtitleStyleValue { get; set; }
|
||||
public string CtaTitleKey { get; set; }
|
||||
public string CtaTitleValue { get; set; }
|
||||
public string CtaTitleStyleKey { get; set; }
|
||||
public string CtaTitleStyleValue { get; set; }
|
||||
public string CtaSubtitleKey { get; set; }
|
||||
public string CtaSubtitleValue { get; set; }
|
||||
public string CtaSubtitleStyleKey { get; set; }
|
||||
public string CtaSubtitleStyleValue { get; set; }
|
||||
public string CtaButtonLabelKey { get; set; }
|
||||
public string CtaButtonLabelValue { get; set; }
|
||||
public string CtaButtonStyleKey { get; set; }
|
||||
public string CtaButtonStyleValue { get; set; }
|
||||
public List<SaveHomeSlideInput> Slides { get; set; } = [];
|
||||
public List<SaveHomeFeatureInput> Features { get; set; } = [];
|
||||
public List<SaveHomeSolutionInput> Solutions { get; set; } = [];
|
||||
|
|
@ -111,6 +131,7 @@ public class SaveHomeSlideInput
|
|||
public string TitleValue { get; set; }
|
||||
public string SubtitleKey { get; set; }
|
||||
public string SubtitleValue { get; set; }
|
||||
public string StyleClass { get; set; }
|
||||
public List<SaveHomeSlideServiceInput> Services { get; set; } = [];
|
||||
}
|
||||
|
||||
|
|
@ -121,6 +142,7 @@ public class SaveHomeSlideServiceInput
|
|||
public string TitleValue { get; set; }
|
||||
public string DescriptionKey { get; set; }
|
||||
public string DescriptionValue { get; set; }
|
||||
public string StyleClass { get; set; }
|
||||
}
|
||||
|
||||
public class SaveHomeFeatureInput
|
||||
|
|
@ -130,6 +152,7 @@ public class SaveHomeFeatureInput
|
|||
public string TitleValue { get; set; }
|
||||
public string DescriptionKey { get; set; }
|
||||
public string DescriptionValue { get; set; }
|
||||
public string StyleClass { get; set; }
|
||||
}
|
||||
|
||||
public class SaveHomeSolutionInput
|
||||
|
|
@ -140,6 +163,7 @@ public class SaveHomeSolutionInput
|
|||
public string TitleValue { get; set; }
|
||||
public string DescriptionKey { get; set; }
|
||||
public string DescriptionValue { get; set; }
|
||||
public string StyleClass { get; set; }
|
||||
}
|
||||
|
||||
public class SaveContactPageInput
|
||||
|
|
@ -168,6 +192,7 @@ public class SaveContactPageInput
|
|||
public string BankBranch { get; set; }
|
||||
public string BankAccountNumber { get; set; }
|
||||
public string BankIban { get; set; }
|
||||
public string BankStyleClass { get; set; }
|
||||
|
||||
public string WorkHoursTitleKey { get; set; }
|
||||
public string WorkHoursTitleValue { get; set; }
|
||||
|
|
@ -177,6 +202,7 @@ public class SaveContactPageInput
|
|||
public string WorkWeekendValue { get; set; }
|
||||
public string WorkWhatsappKey { get; set; }
|
||||
public string WorkWhatsappValue { get; set; }
|
||||
public string WorkHoursStyleClass { get; set; }
|
||||
|
||||
public string MapTitleKey { get; set; }
|
||||
public string MapTitleValue { get; set; }
|
||||
|
|
@ -186,4 +212,7 @@ public class SaveContactPageInput
|
|||
public bool MapAllowFullScreen { get; set; }
|
||||
public string MapLoading { get; set; }
|
||||
public string MapReferrerPolicy { get; set; }
|
||||
public string MapContainerStyleClass { get; set; }
|
||||
public string MapFrameStyleClass { get; set; }
|
||||
public List<SaveLocalizedTextInput> StyleTexts { get; set; } = [];
|
||||
}
|
||||
|
|
@ -128,6 +128,11 @@ public class PublicAppService : PlatformAppService
|
|||
await UpsertLanguageTextAsync(input.CultureName, section.DescriptionKey, section.DescriptionValue);
|
||||
}
|
||||
|
||||
foreach (var styleText in input.StyleTexts)
|
||||
{
|
||||
await UpsertLanguageTextAsync(input.CultureName, styleText.Key, styleText.Value);
|
||||
}
|
||||
|
||||
await _aboutRepository.UpdateAsync(entity, autoSave: true);
|
||||
await _languageTextAppService.ClearRedisCacheAsync();
|
||||
}
|
||||
|
|
@ -176,6 +181,11 @@ public class PublicAppService : PlatformAppService
|
|||
await UpsertLanguageTextAsync(input.CultureName, input.CtaDescriptionKey, input.CtaDescriptionValue);
|
||||
await UpsertLanguageTextAsync(input.CultureName, input.CtaButtonLabelKey, input.CtaButtonLabelValue);
|
||||
|
||||
foreach (var styleText in input.StyleTexts)
|
||||
{
|
||||
await UpsertLanguageTextAsync(input.CultureName, styleText.Key, styleText.Value);
|
||||
}
|
||||
|
||||
await CurrentUnitOfWork!.SaveChangesAsync();
|
||||
await _languageTextAppService.ClearRedisCacheAsync();
|
||||
}
|
||||
|
|
@ -212,11 +222,13 @@ public class PublicAppService : PlatformAppService
|
|||
{
|
||||
TitleKey = slide.TitleKey,
|
||||
SubtitleKey = slide.SubtitleKey,
|
||||
StyleClass = slide.StyleClass,
|
||||
Services = slide.Services.Select(service => new HomeSlideServiceDto
|
||||
{
|
||||
Icon = service.Icon,
|
||||
TitleKey = service.TitleKey,
|
||||
DescriptionKey = service.DescriptionKey,
|
||||
StyleClass = service.StyleClass,
|
||||
}).ToList(),
|
||||
}).ToList());
|
||||
|
||||
|
|
@ -225,6 +237,7 @@ public class PublicAppService : PlatformAppService
|
|||
Icon = feature.Icon,
|
||||
TitleKey = feature.TitleKey,
|
||||
DescriptionKey = feature.DescriptionKey,
|
||||
StyleClass = feature.StyleClass,
|
||||
}).ToList());
|
||||
|
||||
entity.SolutionsJson = JsonSerializer.Serialize(input.Solutions.Select(solution => new HomeSolutionDto
|
||||
|
|
@ -233,6 +246,7 @@ public class PublicAppService : PlatformAppService
|
|||
ColorClass = solution.ColorClass,
|
||||
TitleKey = solution.TitleKey,
|
||||
DescriptionKey = solution.DescriptionKey,
|
||||
StyleClass = solution.StyleClass,
|
||||
}).ToList());
|
||||
|
||||
if (isNewEntity)
|
||||
|
|
@ -246,14 +260,23 @@ public class PublicAppService : PlatformAppService
|
|||
|
||||
await UpsertLanguageTextAsync(input.CultureName, input.HeroBackgroundImageKey, input.HeroBackgroundImageValue);
|
||||
await UpsertLanguageTextAsync(input.CultureName, input.HeroPrimaryCtaKey, input.HeroPrimaryCtaValue);
|
||||
await UpsertLanguageTextAsync(input.CultureName, input.HeroPrimaryCtaStyleKey, input.HeroPrimaryCtaStyleValue);
|
||||
await UpsertLanguageTextAsync(input.CultureName, input.HeroSecondaryCtaKey, input.HeroSecondaryCtaValue);
|
||||
await UpsertLanguageTextAsync(input.CultureName, input.HeroSecondaryCtaStyleKey, input.HeroSecondaryCtaStyleValue);
|
||||
await UpsertLanguageTextAsync(input.CultureName, input.FeaturesTitleKey, input.FeaturesTitleValue);
|
||||
await UpsertLanguageTextAsync(input.CultureName, input.FeaturesTitleStyleKey, input.FeaturesTitleStyleValue);
|
||||
await UpsertLanguageTextAsync(input.CultureName, input.FeaturesSubtitleKey, input.FeaturesSubtitleValue);
|
||||
await UpsertLanguageTextAsync(input.CultureName, input.FeaturesSubtitleStyleKey, input.FeaturesSubtitleStyleValue);
|
||||
await UpsertLanguageTextAsync(input.CultureName, input.SolutionsTitleKey, input.SolutionsTitleValue);
|
||||
await UpsertLanguageTextAsync(input.CultureName, input.SolutionsTitleStyleKey, input.SolutionsTitleStyleValue);
|
||||
await UpsertLanguageTextAsync(input.CultureName, input.SolutionsSubtitleKey, input.SolutionsSubtitleValue);
|
||||
await UpsertLanguageTextAsync(input.CultureName, input.SolutionsSubtitleStyleKey, input.SolutionsSubtitleStyleValue);
|
||||
await UpsertLanguageTextAsync(input.CultureName, input.CtaTitleKey, input.CtaTitleValue);
|
||||
await UpsertLanguageTextAsync(input.CultureName, input.CtaTitleStyleKey, input.CtaTitleStyleValue);
|
||||
await UpsertLanguageTextAsync(input.CultureName, input.CtaSubtitleKey, input.CtaSubtitleValue);
|
||||
await UpsertLanguageTextAsync(input.CultureName, input.CtaSubtitleStyleKey, input.CtaSubtitleStyleValue);
|
||||
await UpsertLanguageTextAsync(input.CultureName, input.CtaButtonLabelKey, input.CtaButtonLabelValue);
|
||||
await UpsertLanguageTextAsync(input.CultureName, input.CtaButtonStyleKey, input.CtaButtonStyleValue);
|
||||
|
||||
foreach (var slide in input.Slides)
|
||||
{
|
||||
|
|
@ -528,6 +551,7 @@ public class PublicAppService : PlatformAppService
|
|||
Branch = input.BankBranch,
|
||||
AccountNumber = input.BankAccountNumber,
|
||||
Iban = input.BankIban,
|
||||
StyleClass = input.BankStyleClass,
|
||||
});
|
||||
|
||||
entity.WorkHoursJson = JsonSerializer.Serialize(new WorkHoursDto
|
||||
|
|
@ -535,6 +559,7 @@ public class PublicAppService : PlatformAppService
|
|||
Weekday = input.WorkWeekdayKey,
|
||||
Weekend = input.WorkWeekendKey,
|
||||
Whatsapp = input.WorkWhatsappKey,
|
||||
StyleClass = input.WorkHoursStyleClass,
|
||||
});
|
||||
|
||||
entity.MapJson = JsonSerializer.Serialize(new MapDto
|
||||
|
|
@ -546,6 +571,8 @@ public class PublicAppService : PlatformAppService
|
|||
AllowFullScreen = input.MapAllowFullScreen,
|
||||
Loading = input.MapLoading,
|
||||
ReferrerPolicy = input.MapReferrerPolicy,
|
||||
ContainerStyleClass = input.MapContainerStyleClass,
|
||||
FrameStyleClass = input.MapFrameStyleClass,
|
||||
});
|
||||
|
||||
await _contactRepository.UpdateAsync(entity, autoSave: false);
|
||||
|
|
@ -562,6 +589,11 @@ public class PublicAppService : PlatformAppService
|
|||
await UpsertLanguageTextAsync(input.CultureName, input.WorkWhatsappKey, input.WorkWhatsappValue);
|
||||
await UpsertLanguageTextAsync(input.CultureName, input.MapTitleKey, input.MapTitleValue);
|
||||
|
||||
foreach (var styleText in input.StyleTexts)
|
||||
{
|
||||
await UpsertLanguageTextAsync(input.CultureName, styleText.Key, styleText.Value);
|
||||
}
|
||||
|
||||
await CurrentUnitOfWork!.SaveChangesAsync();
|
||||
await _languageTextAppService.ClearRedisCacheAsync();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,4 +6,5 @@ public class BankDto
|
|||
public string Branch { get; set; }
|
||||
public string AccountNumber { get; set; }
|
||||
public string Iban { get; set; }
|
||||
public string StyleClass { get; set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ public class MapDto
|
|||
public string Width { get; set; }
|
||||
public string Height { get; set; }
|
||||
public string Style { get; set; }
|
||||
public string ContainerStyleClass { get; set; }
|
||||
public string FrameStyleClass { get; set; }
|
||||
public bool? AllowFullScreen { get; set; }
|
||||
public string Loading { get; set; }
|
||||
public string ReferrerPolicy { get; set; }
|
||||
|
|
|
|||
|
|
@ -5,4 +5,5 @@ public class WorkHoursDto
|
|||
public string Weekday { get; set; }
|
||||
public string Weekend { get; set; }
|
||||
public string Whatsapp { get; set; }
|
||||
public string StyleClass { get; set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ using Volo.Abp.EntityFrameworkCore;
|
|||
namespace Sozsoft.Platform.Migrations
|
||||
{
|
||||
[DbContext(typeof(PlatformDbContext))]
|
||||
[Migration("20260317104139_Initial")]
|
||||
[Migration("20260317120000_Initial")]
|
||||
partial class Initial
|
||||
{
|
||||
/// <inheritdoc />
|
||||
|
|
@ -15,12 +15,14 @@ export interface BankDto {
|
|||
branch: string
|
||||
accountNumber: string
|
||||
iban: string
|
||||
styleClass?: string
|
||||
}
|
||||
|
||||
export interface WorkHoursDto {
|
||||
weekday: string
|
||||
weekend: string
|
||||
whatsapp: string
|
||||
styleClass?: string
|
||||
}
|
||||
|
||||
export interface MapDto {
|
||||
|
|
@ -28,6 +30,9 @@ export interface MapDto {
|
|||
src: string
|
||||
width: string
|
||||
height: string
|
||||
style?: string
|
||||
containerStyleClass?: string
|
||||
frameStyleClass?: string
|
||||
allowFullScreen?: boolean
|
||||
loading: string
|
||||
referrerPolicy: string
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ export interface SaveAboutPageInput {
|
|||
stats: SaveAboutStatInput[]
|
||||
descriptions: SaveLocalizedTextInput[]
|
||||
sections: SaveAboutSectionInput[]
|
||||
styleTexts: SaveLocalizedTextInput[]
|
||||
}
|
||||
|
||||
export function getAbout() {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
import apiService from './api.service'
|
||||
import { ContactDto } from '@/proxy/contact/models'
|
||||
|
||||
export interface SaveLocalizedTextInput {
|
||||
key: string
|
||||
value: string
|
||||
}
|
||||
|
||||
export function getContact() {
|
||||
return apiService.fetchData<ContactDto>(
|
||||
{
|
||||
|
|
@ -33,6 +38,7 @@ export interface SaveContactPageInput extends Record<string, unknown> {
|
|||
bankBranch: string
|
||||
bankAccountNumber: string
|
||||
bankIban: string
|
||||
bankStyleClass: string
|
||||
workHoursTitleKey: string
|
||||
workHoursTitleValue: string
|
||||
workWeekdayKey: string
|
||||
|
|
@ -41,6 +47,7 @@ export interface SaveContactPageInput extends Record<string, unknown> {
|
|||
workWeekendValue: string
|
||||
workWhatsappKey: string
|
||||
workWhatsappValue: string
|
||||
workHoursStyleClass: string
|
||||
mapTitleKey: string
|
||||
mapTitleValue: string
|
||||
mapSrc: string
|
||||
|
|
@ -49,6 +56,9 @@ export interface SaveContactPageInput extends Record<string, unknown> {
|
|||
mapAllowFullScreen: boolean
|
||||
mapLoading: string
|
||||
mapReferrerPolicy: string
|
||||
mapContainerStyleClass: string
|
||||
mapFrameStyleClass: string
|
||||
styleTexts: SaveLocalizedTextInput[]
|
||||
}
|
||||
|
||||
export function saveContactPage(input: SaveContactPageInput) {
|
||||
|
|
|
|||
|
|
@ -4,11 +4,13 @@ export interface HomeSlideServiceDto {
|
|||
icon: string
|
||||
titleKey: string
|
||||
descriptionKey: string
|
||||
styleClass?: string
|
||||
}
|
||||
|
||||
export interface HomeSlideDto {
|
||||
titleKey: string
|
||||
subtitleKey: string
|
||||
styleClass?: string
|
||||
services: HomeSlideServiceDto[]
|
||||
}
|
||||
|
||||
|
|
@ -16,6 +18,7 @@ export interface HomeFeatureDto {
|
|||
icon: string
|
||||
titleKey: string
|
||||
descriptionKey: string
|
||||
styleClass?: string
|
||||
}
|
||||
|
||||
export interface HomeSolutionDto {
|
||||
|
|
@ -23,6 +26,7 @@ export interface HomeSolutionDto {
|
|||
colorClass: string
|
||||
titleKey: string
|
||||
descriptionKey: string
|
||||
styleClass?: string
|
||||
}
|
||||
|
||||
export interface HomeDto {
|
||||
|
|
@ -48,6 +52,7 @@ export interface SaveHomeSlideServiceInput {
|
|||
titleValue: string
|
||||
descriptionKey: string
|
||||
descriptionValue: string
|
||||
styleClass: string
|
||||
}
|
||||
|
||||
export interface SaveHomeSlideInput {
|
||||
|
|
@ -55,6 +60,7 @@ export interface SaveHomeSlideInput {
|
|||
titleValue: string
|
||||
subtitleKey: string
|
||||
subtitleValue: string
|
||||
styleClass: string
|
||||
services: SaveHomeSlideServiceInput[]
|
||||
}
|
||||
|
||||
|
|
@ -64,6 +70,7 @@ export interface SaveHomeFeatureInput {
|
|||
titleValue: string
|
||||
descriptionKey: string
|
||||
descriptionValue: string
|
||||
styleClass: string
|
||||
}
|
||||
|
||||
export interface SaveHomeSolutionInput {
|
||||
|
|
@ -73,6 +80,7 @@ export interface SaveHomeSolutionInput {
|
|||
titleValue: string
|
||||
descriptionKey: string
|
||||
descriptionValue: string
|
||||
styleClass: string
|
||||
}
|
||||
|
||||
export interface SaveHomePageInput {
|
||||
|
|
@ -81,22 +89,40 @@ export interface SaveHomePageInput {
|
|||
heroBackgroundImageValue: string
|
||||
heroPrimaryCtaKey: string
|
||||
heroPrimaryCtaValue: string
|
||||
heroPrimaryCtaStyleKey: string
|
||||
heroPrimaryCtaStyleValue: string
|
||||
heroSecondaryCtaKey: string
|
||||
heroSecondaryCtaValue: string
|
||||
heroSecondaryCtaStyleKey: string
|
||||
heroSecondaryCtaStyleValue: string
|
||||
featuresTitleKey: string
|
||||
featuresTitleValue: string
|
||||
featuresTitleStyleKey: string
|
||||
featuresTitleStyleValue: string
|
||||
featuresSubtitleKey: string
|
||||
featuresSubtitleValue: string
|
||||
featuresSubtitleStyleKey: string
|
||||
featuresSubtitleStyleValue: string
|
||||
solutionsTitleKey: string
|
||||
solutionsTitleValue: string
|
||||
solutionsTitleStyleKey: string
|
||||
solutionsTitleStyleValue: string
|
||||
solutionsSubtitleKey: string
|
||||
solutionsSubtitleValue: string
|
||||
solutionsSubtitleStyleKey: string
|
||||
solutionsSubtitleStyleValue: string
|
||||
ctaTitleKey: string
|
||||
ctaTitleValue: string
|
||||
ctaTitleStyleKey: string
|
||||
ctaTitleStyleValue: string
|
||||
ctaSubtitleKey: string
|
||||
ctaSubtitleValue: string
|
||||
ctaSubtitleStyleKey: string
|
||||
ctaSubtitleStyleValue: string
|
||||
ctaButtonLabelKey: string
|
||||
ctaButtonLabelValue: string
|
||||
ctaButtonStyleKey: string
|
||||
ctaButtonStyleValue: string
|
||||
slides: SaveHomeSlideInput[]
|
||||
features: SaveHomeFeatureInput[]
|
||||
solutions: SaveHomeSolutionInput[]
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ export interface SaveServicesPageInput {
|
|||
ctaButtonLabelValue: string
|
||||
serviceItems: SaveServiceItemInput[]
|
||||
supportItems: SaveServiceItemInput[]
|
||||
styleTexts: SaveLocalizedTextInput[]
|
||||
}
|
||||
|
||||
export function getServices() {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import { APP_NAME } from '@/constants/app.constant'
|
|||
import { Notification, toast } from '@/components/ui'
|
||||
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'
|
||||
|
|
@ -20,6 +19,12 @@ interface AboutStatContent {
|
|||
value: string
|
||||
label: string
|
||||
labelKey: string
|
||||
styleClassKey: string
|
||||
styleClass: string
|
||||
valueStyleClassKey: string
|
||||
valueStyleClass: string
|
||||
labelStyleClassKey: string
|
||||
labelStyleClass: string
|
||||
useCounter?: boolean
|
||||
counterEnd?: string
|
||||
counterSuffix?: string
|
||||
|
|
@ -29,6 +34,8 @@ interface AboutStatContent {
|
|||
interface AboutDescriptionContent {
|
||||
key: string
|
||||
text: string
|
||||
styleClassKey: string
|
||||
styleClass: string
|
||||
}
|
||||
|
||||
interface AboutSectionContent {
|
||||
|
|
@ -36,15 +43,29 @@ interface AboutSectionContent {
|
|||
description: string
|
||||
titleKey: string
|
||||
descriptionKey: string
|
||||
cardStyleClassKey: string
|
||||
cardStyleClass: string
|
||||
titleStyleClassKey: string
|
||||
titleStyleClass: string
|
||||
descriptionStyleClassKey: string
|
||||
descriptionStyleClass: string
|
||||
}
|
||||
|
||||
interface AboutContent {
|
||||
heroTitle: string
|
||||
heroTitleKey: string
|
||||
heroTitleStyleClassKey: string
|
||||
heroTitleStyleClass: string
|
||||
heroSubtitle: string
|
||||
heroSubtitleKey: string
|
||||
heroSubtitleStyleClassKey: string
|
||||
heroSubtitleStyleClass: string
|
||||
heroImage: string
|
||||
heroImageKey: string
|
||||
heroSectionStyleClassKey: string
|
||||
heroSectionStyleClass: string
|
||||
descriptionsContainerStyleClassKey: string
|
||||
descriptionsContainerStyleClass: string
|
||||
stats: AboutStatContent[]
|
||||
descriptions: AboutDescriptionContent[]
|
||||
sections: AboutSectionContent[]
|
||||
|
|
@ -55,6 +76,10 @@ const ABOUT_HERO_IMAGE =
|
|||
const ABOUT_HERO_TITLE_KEY = 'App.About'
|
||||
const ABOUT_HERO_SUBTITLE_KEY = 'Public.about.subtitle'
|
||||
const ABOUT_HERO_IMAGE_KEY = 'Public.about.heroImage'
|
||||
const ABOUT_HERO_SECTION_STYLE_KEY = 'Public.about.hero.sectionStyleClass'
|
||||
const ABOUT_HERO_TITLE_STYLE_KEY = 'Public.about.hero.titleStyleClass'
|
||||
const ABOUT_HERO_SUBTITLE_STYLE_KEY = 'Public.about.hero.subtitleStyleClass'
|
||||
const ABOUT_DESCRIPTIONS_CONTAINER_STYLE_KEY = 'Public.about.descriptions.containerStyleClass'
|
||||
|
||||
function isLikelyLocalizationKey(value?: string) {
|
||||
return Boolean(value && /^[A-Za-z0-9_.-]+$/.test(value) && value.includes('.'))
|
||||
|
|
@ -84,18 +109,60 @@ function buildAboutContent(
|
|||
return {
|
||||
heroTitle: resolveLocalizedValue(translate, ABOUT_HERO_TITLE_KEY, 'About'),
|
||||
heroTitleKey: ABOUT_HERO_TITLE_KEY,
|
||||
heroTitleStyleClassKey: ABOUT_HERO_TITLE_STYLE_KEY,
|
||||
heroTitleStyleClass: resolveLocalizedValue(
|
||||
translate,
|
||||
ABOUT_HERO_TITLE_STYLE_KEY,
|
||||
'text-5xl font-bold ml-4 mt-3 mb-2 text-white',
|
||||
),
|
||||
heroSubtitle: resolveLocalizedValue(translate, ABOUT_HERO_SUBTITLE_KEY),
|
||||
heroSubtitleKey: ABOUT_HERO_SUBTITLE_KEY,
|
||||
heroSubtitleStyleClassKey: ABOUT_HERO_SUBTITLE_STYLE_KEY,
|
||||
heroSubtitleStyleClass: resolveLocalizedValue(
|
||||
translate,
|
||||
ABOUT_HERO_SUBTITLE_STYLE_KEY,
|
||||
'text-xl max-w-3xl ml-4',
|
||||
),
|
||||
heroImage: resolveLocalizedValue(translate, ABOUT_HERO_IMAGE_KEY, ABOUT_HERO_IMAGE),
|
||||
heroImageKey: ABOUT_HERO_IMAGE_KEY,
|
||||
heroSectionStyleClassKey: ABOUT_HERO_SECTION_STYLE_KEY,
|
||||
heroSectionStyleClass: resolveLocalizedValue(
|
||||
translate,
|
||||
ABOUT_HERO_SECTION_STYLE_KEY,
|
||||
'relative bg-blue-900 text-white py-12',
|
||||
),
|
||||
descriptionsContainerStyleClassKey: ABOUT_DESCRIPTIONS_CONTAINER_STYLE_KEY,
|
||||
descriptionsContainerStyleClass: resolveLocalizedValue(
|
||||
translate,
|
||||
ABOUT_DESCRIPTIONS_CONTAINER_STYLE_KEY,
|
||||
'p-5 mx-auto text-gray-800 text-lg leading-relaxed shadow-md bg-white border-l-4 border-blue-600',
|
||||
),
|
||||
stats:
|
||||
about?.statsDto.map((stat) => ({
|
||||
about?.statsDto.map((stat, index) => ({
|
||||
styleClassKey: `Public.about.dynamic.stat.${index + 1}.styleClass`,
|
||||
styleClass: resolveLocalizedValue(
|
||||
translate,
|
||||
`Public.about.dynamic.stat.${index + 1}.styleClass`,
|
||||
'text-center rounded-xl px-4 py-6',
|
||||
),
|
||||
valueStyleClassKey: `Public.about.dynamic.stat.${index + 1}.valueStyleClass`,
|
||||
valueStyleClass: resolveLocalizedValue(
|
||||
translate,
|
||||
`Public.about.dynamic.stat.${index + 1}.valueStyleClass`,
|
||||
'text-4xl font-bold text-gray-900 mb-2',
|
||||
),
|
||||
labelStyleClassKey: `Public.about.dynamic.stat.${index + 1}.labelStyleClass`,
|
||||
labelStyleClass: resolveLocalizedValue(
|
||||
translate,
|
||||
`Public.about.dynamic.stat.${index + 1}.labelStyleClass`,
|
||||
'text-gray-600',
|
||||
),
|
||||
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`,
|
||||
`Public.about.dynamic.stat.${index + 1}.label`,
|
||||
useCounter: stat.useCounter,
|
||||
counterEnd: stat.counterEnd,
|
||||
counterSuffix: stat.counterSuffix,
|
||||
|
|
@ -107,6 +174,12 @@ function buildAboutContent(
|
|||
(isLikelyLocalizationKey(item) ? item : undefined) ||
|
||||
`Public.about.dynamic.description.${index + 1}`,
|
||||
text: resolveLocalizedValue(translate, item, item),
|
||||
styleClassKey: `Public.about.dynamic.description.${index + 1}.styleClass`,
|
||||
styleClass: resolveLocalizedValue(
|
||||
translate,
|
||||
`Public.about.dynamic.description.${index + 1}.styleClass`,
|
||||
index % 2 === 0 ? '' : 'text-center p-5 text-blue-800',
|
||||
),
|
||||
})) ?? [],
|
||||
sections:
|
||||
about?.sectionsDto.map((section) => ({
|
||||
|
|
@ -118,13 +191,30 @@ function buildAboutContent(
|
|||
descriptionKey:
|
||||
(isLikelyLocalizationKey(section.descKey) ? section.descKey : undefined) ||
|
||||
`Public.about.dynamic.section.${section.key}.description`,
|
||||
cardStyleClassKey: `Public.about.dynamic.section.${section.key}.cardStyleClass`,
|
||||
cardStyleClass: resolveLocalizedValue(
|
||||
translate,
|
||||
`Public.about.dynamic.section.${section.key}.cardStyleClass`,
|
||||
'bg-white p-8 rounded-xl shadow-lg',
|
||||
),
|
||||
titleStyleClassKey: `Public.about.dynamic.section.${section.key}.titleStyleClass`,
|
||||
titleStyleClass: resolveLocalizedValue(
|
||||
translate,
|
||||
`Public.about.dynamic.section.${section.key}.titleStyleClass`,
|
||||
'text-2xl font-bold text-gray-900 mb-4',
|
||||
),
|
||||
descriptionStyleClassKey: `Public.about.dynamic.section.${section.key}.descriptionStyleClass`,
|
||||
descriptionStyleClass: resolveLocalizedValue(
|
||||
translate,
|
||||
`Public.about.dynamic.section.${section.key}.descriptionStyleClass`,
|
||||
'text-gray-700',
|
||||
),
|
||||
})) ?? [],
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
|
|
@ -207,7 +297,15 @@ const About: React.FC = () => {
|
|||
|
||||
const handleFieldChange = (fieldKey: string, value: string | string[]) => {
|
||||
updateContent((current) => {
|
||||
if (fieldKey === 'heroTitle' || fieldKey === 'heroSubtitle' || fieldKey === 'heroImage') {
|
||||
if (
|
||||
fieldKey === 'heroTitle' ||
|
||||
fieldKey === 'heroSubtitle' ||
|
||||
fieldKey === 'heroImage' ||
|
||||
fieldKey === 'heroSectionStyleClass' ||
|
||||
fieldKey === 'heroTitleStyleClass' ||
|
||||
fieldKey === 'heroSubtitleStyleClass' ||
|
||||
fieldKey === 'descriptionsContainerStyleClass'
|
||||
) {
|
||||
return {
|
||||
...current,
|
||||
[fieldKey]: value as string,
|
||||
|
|
@ -228,6 +326,20 @@ const About: React.FC = () => {
|
|||
}
|
||||
}
|
||||
|
||||
if (fieldKey.startsWith('descriptionStyle-')) {
|
||||
const index = Number(fieldKey.replace('descriptionStyle-', ''))
|
||||
const descriptions = [...current.descriptions]
|
||||
descriptions[index] = {
|
||||
...descriptions[index],
|
||||
styleClass: value as string,
|
||||
}
|
||||
|
||||
return {
|
||||
...current,
|
||||
descriptions,
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedBlockId?.startsWith('stat-')) {
|
||||
const index = Number(selectedBlockId.replace('stat-', ''))
|
||||
const stats = [...current.stats]
|
||||
|
|
@ -277,18 +389,36 @@ const About: React.FC = () => {
|
|||
type: 'text',
|
||||
value: content.heroTitle,
|
||||
},
|
||||
{
|
||||
key: 'heroTitleStyleClass',
|
||||
label: content.heroTitleStyleClassKey,
|
||||
type: 'text',
|
||||
value: content.heroTitleStyleClass,
|
||||
},
|
||||
{
|
||||
key: 'heroSubtitle',
|
||||
label: content.heroSubtitleKey,
|
||||
type: 'textarea',
|
||||
value: content.heroSubtitle,
|
||||
},
|
||||
{
|
||||
key: 'heroSubtitleStyleClass',
|
||||
label: content.heroSubtitleStyleClassKey,
|
||||
type: 'text',
|
||||
value: content.heroSubtitleStyleClass,
|
||||
},
|
||||
{
|
||||
key: 'heroImage',
|
||||
label: content.heroImageKey,
|
||||
type: 'image',
|
||||
value: content.heroImage,
|
||||
},
|
||||
{
|
||||
key: 'heroSectionStyleClass',
|
||||
label: content.heroSectionStyleClassKey,
|
||||
type: 'text',
|
||||
value: content.heroSectionStyleClass,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
|
@ -298,13 +428,29 @@ const About: React.FC = () => {
|
|||
id: 'descriptions',
|
||||
title: 'Public.about.description.*',
|
||||
description: 'Orta bolumdeki aciklama metinlerini duzenleyin.',
|
||||
fields: content.descriptions.map((item, index) => ({
|
||||
fields: [
|
||||
{
|
||||
key: 'descriptionsContainerStyleClass',
|
||||
label: content.descriptionsContainerStyleClassKey,
|
||||
type: 'text',
|
||||
value: content.descriptionsContainerStyleClass,
|
||||
},
|
||||
...content.descriptions.flatMap((item, index) => [
|
||||
{
|
||||
key: `description-${index}`,
|
||||
label: item.key || `Public.about.dynamic.description.${index + 1}`,
|
||||
type: 'textarea',
|
||||
type: 'textarea' as const,
|
||||
value: item.text,
|
||||
rows: index % 2 === 0 ? 4 : 3,
|
||||
})),
|
||||
},
|
||||
{
|
||||
key: `descriptionStyle-${index}`,
|
||||
label: item.styleClassKey,
|
||||
type: 'text' as const,
|
||||
value: item.styleClass,
|
||||
},
|
||||
]),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -340,6 +486,24 @@ const About: React.FC = () => {
|
|||
type: 'text',
|
||||
value: stat.label,
|
||||
},
|
||||
{
|
||||
key: 'styleClass',
|
||||
label: stat.styleClassKey,
|
||||
type: 'text',
|
||||
value: stat.styleClass,
|
||||
},
|
||||
{
|
||||
key: 'valueStyleClass',
|
||||
label: stat.valueStyleClassKey,
|
||||
type: 'text',
|
||||
value: stat.valueStyleClass,
|
||||
},
|
||||
{
|
||||
key: 'labelStyleClass',
|
||||
label: stat.labelStyleClassKey,
|
||||
type: 'text',
|
||||
value: stat.labelStyleClass,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
|
@ -369,6 +533,24 @@ const About: React.FC = () => {
|
|||
type: 'textarea',
|
||||
value: section.description,
|
||||
},
|
||||
{
|
||||
key: 'cardStyleClass',
|
||||
label: section.cardStyleClassKey,
|
||||
type: 'text',
|
||||
value: section.cardStyleClass,
|
||||
},
|
||||
{
|
||||
key: 'titleStyleClass',
|
||||
label: section.titleStyleClassKey,
|
||||
type: 'text',
|
||||
value: section.titleStyleClass,
|
||||
},
|
||||
{
|
||||
key: 'descriptionStyleClass',
|
||||
label: section.descriptionStyleClassKey,
|
||||
type: 'text',
|
||||
value: section.descriptionStyleClass,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
|
@ -413,6 +595,56 @@ const About: React.FC = () => {
|
|||
section.descriptionKey || `Public.about.dynamic.section.${index + 1}.description`,
|
||||
descriptionValue: section.description,
|
||||
})),
|
||||
styleTexts: [
|
||||
{
|
||||
key: content.heroSectionStyleClassKey,
|
||||
value: content.heroSectionStyleClass,
|
||||
},
|
||||
{
|
||||
key: content.heroTitleStyleClassKey,
|
||||
value: content.heroTitleStyleClass,
|
||||
},
|
||||
{
|
||||
key: content.heroSubtitleStyleClassKey,
|
||||
value: content.heroSubtitleStyleClass,
|
||||
},
|
||||
{
|
||||
key: content.descriptionsContainerStyleClassKey,
|
||||
value: content.descriptionsContainerStyleClass,
|
||||
},
|
||||
...content.stats.flatMap((stat) => [
|
||||
{
|
||||
key: stat.styleClassKey,
|
||||
value: stat.styleClass,
|
||||
},
|
||||
{
|
||||
key: stat.valueStyleClassKey,
|
||||
value: stat.valueStyleClass,
|
||||
},
|
||||
{
|
||||
key: stat.labelStyleClassKey,
|
||||
value: stat.labelStyleClass,
|
||||
},
|
||||
]),
|
||||
...content.descriptions.map((item) => ({
|
||||
key: item.styleClassKey,
|
||||
value: item.styleClass,
|
||||
})),
|
||||
...content.sections.flatMap((section) => [
|
||||
{
|
||||
key: section.cardStyleClassKey,
|
||||
value: section.cardStyleClass,
|
||||
},
|
||||
{
|
||||
key: section.titleStyleClassKey,
|
||||
value: section.titleStyleClass,
|
||||
},
|
||||
{
|
||||
key: section.descriptionStyleClassKey,
|
||||
value: section.descriptionStyleClass,
|
||||
},
|
||||
]),
|
||||
],
|
||||
})
|
||||
|
||||
await getConfig(false)
|
||||
|
|
@ -483,7 +715,7 @@ const About: React.FC = () => {
|
|||
isDesignMode={isDesignMode}
|
||||
onSelect={handleSelectBlock}
|
||||
>
|
||||
<div className="relative bg-blue-900 text-white py-12">
|
||||
<div className={content?.heroSectionStyleClass || 'relative bg-blue-900 text-white py-12'}>
|
||||
<div
|
||||
className="absolute inset-0 opacity-20"
|
||||
style={{
|
||||
|
|
@ -493,10 +725,10 @@ const About: React.FC = () => {
|
|||
}}
|
||||
></div>
|
||||
<div className="container mx-auto pt-20 relative">
|
||||
<h1 className="text-5xl font-bold ml-4 mt-3 mb-2 text-white">
|
||||
<h1 className={content?.heroTitleStyleClass || 'text-5xl font-bold ml-4 mt-3 mb-2 text-white'}>
|
||||
{content?.heroTitle}
|
||||
</h1>
|
||||
<p className="text-xl max-w-3xl ml-4">{content?.heroSubtitle}</p>
|
||||
<p className={content?.heroSubtitleStyleClass || 'text-xl max-w-3xl ml-4'}>{content?.heroSubtitle}</p>
|
||||
</div>
|
||||
</div>
|
||||
</SelectableBlock>
|
||||
|
|
@ -516,14 +748,14 @@ const About: React.FC = () => {
|
|||
isDesignMode={isDesignMode}
|
||||
onSelect={handleSelectBlock}
|
||||
>
|
||||
<div className="text-center rounded-xl px-4 py-6">
|
||||
<div className={stat.styleClass || 'text-center rounded-xl px-4 py-6'}>
|
||||
{IconComponent && (
|
||||
<IconComponent
|
||||
className={`w-12 h-12 mx-auto mb-4 ${getIconColor(index)}`}
|
||||
/>
|
||||
)}
|
||||
<div className="text-4xl font-bold text-gray-900 mb-2">{stat.value}</div>
|
||||
<div className="text-gray-600">{stat.label}</div>
|
||||
<div className={stat.valueStyleClass || 'text-4xl font-bold text-gray-900 mb-2'}>{stat.value}</div>
|
||||
<div className={stat.labelStyleClass || 'text-gray-600'}>{stat.label}</div>
|
||||
</div>
|
||||
</SelectableBlock>
|
||||
)
|
||||
|
|
@ -542,11 +774,12 @@ const About: React.FC = () => {
|
|||
isDesignMode={isDesignMode}
|
||||
onSelect={handleSelectBlock}
|
||||
>
|
||||
<div className="p-5 mx-auto mx-auto text-gray-800 text-lg leading-relaxed shadow-md bg-white border-l-4 border-blue-600">
|
||||
<p>{content?.descriptions[0]?.text}</p>
|
||||
<p className="text-center p-5 text-blue-800">{content?.descriptions[1]?.text}</p>
|
||||
<p>{content?.descriptions[2]?.text}</p>
|
||||
<p className="text-center p-5 text-blue-800">{content?.descriptions[3]?.text}</p>
|
||||
<div className={content?.descriptionsContainerStyleClass || 'p-5 mx-auto text-gray-800 text-lg leading-relaxed shadow-md bg-white border-l-4 border-blue-600'}>
|
||||
{content?.descriptions.map((item, index) => (
|
||||
<p key={item.key || `description-${index}`} className={item.styleClass || ''}>
|
||||
{item.text}
|
||||
</p>
|
||||
))}
|
||||
</div>
|
||||
</SelectableBlock>
|
||||
</div>
|
||||
|
|
@ -560,9 +793,9 @@ const About: React.FC = () => {
|
|||
isDesignMode={isDesignMode}
|
||||
onSelect={handleSelectBlock}
|
||||
>
|
||||
<div className="bg-white p-8 rounded-xl shadow-lg">
|
||||
<h3 className="text-2xl font-bold text-gray-900 mb-4">{section.title}</h3>
|
||||
<p className="text-gray-700">{section.description}</p>
|
||||
<div className={section.cardStyleClass || 'bg-white p-8 rounded-xl shadow-lg'}>
|
||||
<h3 className={section.titleStyleClass || 'text-2xl font-bold text-gray-900 mb-4'}>{section.title}</h3>
|
||||
<p className={section.descriptionStyleClass || 'text-gray-700'}>{section.description}</p>
|
||||
</div>
|
||||
</SelectableBlock>
|
||||
))}
|
||||
|
|
|
|||
|
|
@ -25,12 +25,22 @@ import { Notification, toast } from '@/components/ui'
|
|||
interface ContactContent {
|
||||
heroTitle: string
|
||||
heroTitleKey: string
|
||||
heroSectionStyleClass: string
|
||||
heroSectionStyleClassKey: string
|
||||
heroTitleStyleClass: string
|
||||
heroTitleStyleClassKey: string
|
||||
heroSubtitle: string
|
||||
heroSubtitleKey: string
|
||||
heroSubtitleStyleClass: string
|
||||
heroSubtitleStyleClassKey: string
|
||||
heroImage: string
|
||||
heroImageKey: string
|
||||
contactInfoTitle: string
|
||||
contactInfoTitleKey: string
|
||||
contactInfoCardStyleClass: string
|
||||
contactInfoCardStyleClassKey: string
|
||||
contactInfoTitleStyleClass: string
|
||||
contactInfoTitleStyleClassKey: string
|
||||
address: string
|
||||
addressKey: string
|
||||
phoneNumber: string
|
||||
|
|
@ -43,6 +53,7 @@ interface ContactContent {
|
|||
bankBranch: string
|
||||
bankAccountNumber: string
|
||||
bankIban: string
|
||||
bankStyleClass: string
|
||||
workHoursTitle: string
|
||||
workHoursTitleKey: string
|
||||
workWeekday: string
|
||||
|
|
@ -51,6 +62,7 @@ interface ContactContent {
|
|||
workWeekendKey: string
|
||||
workWhatsapp: string
|
||||
workWhatsappKey: string
|
||||
workHoursStyleClass: string
|
||||
mapTitle: string
|
||||
mapTitleKey: string
|
||||
mapSrc: string
|
||||
|
|
@ -59,6 +71,8 @@ interface ContactContent {
|
|||
mapAllowFullScreen: string
|
||||
mapLoading: string
|
||||
mapReferrerPolicy: string
|
||||
mapContainerStyleClass: string
|
||||
mapFrameStyleClass: string
|
||||
}
|
||||
|
||||
const CONTACT_HERO_TITLE_KEY = 'App.Contact'
|
||||
|
|
@ -67,13 +81,13 @@ 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'
|
||||
const CONTACT_HERO_SECTION_STYLE_KEY = 'Public.contact.hero.sectionStyleClass'
|
||||
const CONTACT_HERO_TITLE_STYLE_KEY = 'Public.contact.hero.titleStyleClass'
|
||||
const CONTACT_HERO_SUBTITLE_STYLE_KEY = 'Public.contact.hero.subtitleStyleClass'
|
||||
const CONTACT_INFO_CARD_STYLE_KEY = 'Public.contact.info.cardStyleClass'
|
||||
const CONTACT_INFO_TITLE_STYLE_KEY = 'Public.contact.info.titleStyleClass'
|
||||
|
||||
function isLikelyLocalizationKey(value?: string) {
|
||||
return Boolean(value && /^[A-Za-z0-9_.-]+$/.test(value) && value.includes('.'))
|
||||
|
|
@ -103,14 +117,44 @@ function buildContactContent(
|
|||
return {
|
||||
heroTitle: resolveLocalizedValue(translate, CONTACT_HERO_TITLE_KEY, 'Contact'),
|
||||
heroTitleKey: CONTACT_HERO_TITLE_KEY,
|
||||
heroSectionStyleClass: resolveLocalizedValue(
|
||||
translate,
|
||||
CONTACT_HERO_SECTION_STYLE_KEY,
|
||||
'relative bg-blue-900 py-12 text-white',
|
||||
),
|
||||
heroSectionStyleClassKey: CONTACT_HERO_SECTION_STYLE_KEY,
|
||||
heroTitleStyleClass: resolveLocalizedValue(
|
||||
translate,
|
||||
CONTACT_HERO_TITLE_STYLE_KEY,
|
||||
'ml-4 mb-2 mt-3 text-5xl font-bold text-white',
|
||||
),
|
||||
heroTitleStyleClassKey: CONTACT_HERO_TITLE_STYLE_KEY,
|
||||
heroSubtitle: resolveLocalizedValue(translate, CONTACT_HERO_SUBTITLE_KEY),
|
||||
heroSubtitleKey: CONTACT_HERO_SUBTITLE_KEY,
|
||||
heroSubtitleStyleClass: resolveLocalizedValue(
|
||||
translate,
|
||||
CONTACT_HERO_SUBTITLE_STYLE_KEY,
|
||||
'ml-4 max-w-3xl text-xl',
|
||||
),
|
||||
heroSubtitleStyleClassKey: CONTACT_HERO_SUBTITLE_STYLE_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,
|
||||
contactInfoCardStyleClass: resolveLocalizedValue(
|
||||
translate,
|
||||
CONTACT_INFO_CARD_STYLE_KEY,
|
||||
'rounded-xl bg-white p-8 shadow-lg',
|
||||
),
|
||||
contactInfoCardStyleClassKey: CONTACT_INFO_CARD_STYLE_KEY,
|
||||
contactInfoTitleStyleClass: resolveLocalizedValue(
|
||||
translate,
|
||||
CONTACT_INFO_TITLE_STYLE_KEY,
|
||||
'mb-6 text-2xl font-bold text-gray-900',
|
||||
),
|
||||
contactInfoTitleStyleClassKey: CONTACT_INFO_TITLE_STYLE_KEY,
|
||||
address: resolveLocalizedValue(translate, contact?.address, ''),
|
||||
addressKey: contact?.address || '',
|
||||
phoneNumber: contact?.phoneNumber || '',
|
||||
email: contact?.email || '',
|
||||
location: contact?.location || '',
|
||||
|
|
@ -121,25 +165,27 @@ function buildContactContent(
|
|||
bankBranch: contact?.bankDto?.branch || '',
|
||||
bankAccountNumber: contact?.bankDto?.accountNumber || '',
|
||||
bankIban: contact?.bankDto?.iban || '',
|
||||
bankStyleClass: contact?.bankDto?.styleClass || 'rounded-xl bg-white p-8 shadow-lg',
|
||||
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,
|
||||
workWeekday: resolveLocalizedValue(translate, contact?.workHoursDto?.weekday, ''),
|
||||
workWeekdayKey: contact?.workHoursDto?.weekday || '',
|
||||
workWeekend: resolveLocalizedValue(translate, contact?.workHoursDto?.weekend, ''),
|
||||
workWeekendKey: contact?.workHoursDto?.weekend || '',
|
||||
workWhatsapp: resolveLocalizedValue(translate, contact?.workHoursDto?.whatsapp, ''),
|
||||
workWhatsappKey: contact?.workHoursDto?.whatsapp || '',
|
||||
workHoursStyleClass: contact?.workHoursDto?.styleClass || 'rounded-xl bg-white p-8 shadow-lg',
|
||||
mapTitle: resolveLocalizedValue(translate, contact?.mapDto?.title, ''),
|
||||
mapTitleKey: contact?.mapDto?.title || '',
|
||||
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',
|
||||
mapContainerStyleClass: contact?.mapDto?.containerStyleClass || 'rounded-xl bg-white p-8 shadow-lg',
|
||||
mapFrameStyleClass:
|
||||
contact?.mapDto?.frameStyleClass || 'aspect-w-16 aspect-h-9 overflow-hidden rounded-xl bg-gray-200',
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -238,18 +284,36 @@ const Contact: React.FC = () => {
|
|||
type: 'text',
|
||||
value: content.heroTitle,
|
||||
},
|
||||
{
|
||||
key: 'heroTitleStyleClass',
|
||||
label: content.heroTitleStyleClassKey,
|
||||
type: 'text',
|
||||
value: content.heroTitleStyleClass,
|
||||
},
|
||||
{
|
||||
key: 'heroSubtitle',
|
||||
label: content.heroSubtitleKey,
|
||||
type: 'textarea',
|
||||
value: content.heroSubtitle,
|
||||
},
|
||||
{
|
||||
key: 'heroSubtitleStyleClass',
|
||||
label: content.heroSubtitleStyleClassKey,
|
||||
type: 'text',
|
||||
value: content.heroSubtitleStyleClass,
|
||||
},
|
||||
{
|
||||
key: 'heroImage',
|
||||
label: content.heroImageKey,
|
||||
type: 'image',
|
||||
value: content.heroImage,
|
||||
},
|
||||
{
|
||||
key: 'heroSectionStyleClass',
|
||||
label: content.heroSectionStyleClassKey,
|
||||
type: 'text',
|
||||
value: content.heroSectionStyleClass,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
|
@ -266,6 +330,18 @@ const Contact: React.FC = () => {
|
|||
type: 'text',
|
||||
value: content.contactInfoTitle,
|
||||
},
|
||||
{
|
||||
key: 'contactInfoTitleStyleClass',
|
||||
label: content.contactInfoTitleStyleClassKey,
|
||||
type: 'text',
|
||||
value: content.contactInfoTitleStyleClass,
|
||||
},
|
||||
{
|
||||
key: 'contactInfoCardStyleClass',
|
||||
label: content.contactInfoCardStyleClassKey,
|
||||
type: 'text',
|
||||
value: content.contactInfoCardStyleClass,
|
||||
},
|
||||
{
|
||||
key: 'address',
|
||||
label: content.addressKey,
|
||||
|
|
@ -336,6 +412,12 @@ const Contact: React.FC = () => {
|
|||
type: 'text',
|
||||
value: content.bankIban,
|
||||
},
|
||||
{
|
||||
key: 'bankStyleClass',
|
||||
label: 'Public.contact.bank.styleClass',
|
||||
type: 'text',
|
||||
value: content.bankStyleClass,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
|
@ -370,6 +452,12 @@ const Contact: React.FC = () => {
|
|||
type: 'text',
|
||||
value: content.workWhatsapp,
|
||||
},
|
||||
{
|
||||
key: 'workHoursStyleClass',
|
||||
label: 'Public.contact.workHours.styleClass',
|
||||
type: 'text',
|
||||
value: content.workHoursStyleClass,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
|
@ -423,6 +511,18 @@ const Contact: React.FC = () => {
|
|||
type: 'text',
|
||||
value: content.mapReferrerPolicy,
|
||||
},
|
||||
{
|
||||
key: 'mapContainerStyleClass',
|
||||
label: 'Public.contact.map.containerStyleClass',
|
||||
type: 'text',
|
||||
value: content.mapContainerStyleClass,
|
||||
},
|
||||
{
|
||||
key: 'mapFrameStyleClass',
|
||||
label: 'Public.contact.map.frameStyleClass',
|
||||
type: 'text',
|
||||
value: content.mapFrameStyleClass,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
|
@ -460,6 +560,7 @@ const Contact: React.FC = () => {
|
|||
bankBranch: content.bankBranch,
|
||||
bankAccountNumber: content.bankAccountNumber,
|
||||
bankIban: content.bankIban,
|
||||
bankStyleClass: content.bankStyleClass,
|
||||
workHoursTitleKey: content.workHoursTitleKey,
|
||||
workHoursTitleValue: content.workHoursTitle,
|
||||
workWeekdayKey: content.workWeekdayKey,
|
||||
|
|
@ -468,6 +569,7 @@ const Contact: React.FC = () => {
|
|||
workWeekendValue: content.workWeekend,
|
||||
workWhatsappKey: content.workWhatsappKey,
|
||||
workWhatsappValue: content.workWhatsapp,
|
||||
workHoursStyleClass: content.workHoursStyleClass,
|
||||
mapTitleKey: content.mapTitleKey,
|
||||
mapTitleValue: content.mapTitle,
|
||||
mapSrc: content.mapSrc,
|
||||
|
|
@ -476,6 +578,30 @@ const Contact: React.FC = () => {
|
|||
mapAllowFullScreen: content.mapAllowFullScreen.toLowerCase() === 'true',
|
||||
mapLoading: content.mapLoading,
|
||||
mapReferrerPolicy: content.mapReferrerPolicy,
|
||||
mapContainerStyleClass: content.mapContainerStyleClass,
|
||||
mapFrameStyleClass: content.mapFrameStyleClass,
|
||||
styleTexts: [
|
||||
{
|
||||
key: content.heroSectionStyleClassKey,
|
||||
value: content.heroSectionStyleClass,
|
||||
},
|
||||
{
|
||||
key: content.heroTitleStyleClassKey,
|
||||
value: content.heroTitleStyleClass,
|
||||
},
|
||||
{
|
||||
key: content.heroSubtitleStyleClassKey,
|
||||
value: content.heroSubtitleStyleClass,
|
||||
},
|
||||
{
|
||||
key: content.contactInfoCardStyleClassKey,
|
||||
value: content.contactInfoCardStyleClass,
|
||||
},
|
||||
{
|
||||
key: content.contactInfoTitleStyleClassKey,
|
||||
value: content.contactInfoTitleStyleClass,
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
await getConfig(false)
|
||||
|
|
@ -548,7 +674,7 @@ const Contact: React.FC = () => {
|
|||
isDesignMode={isDesignMode}
|
||||
onSelect={handleSelectBlock}
|
||||
>
|
||||
<div className="relative bg-blue-900 py-12 text-white">
|
||||
<div className={content?.heroSectionStyleClass || 'relative bg-blue-900 py-12 text-white'}>
|
||||
<div
|
||||
className="absolute inset-0 opacity-20"
|
||||
style={{
|
||||
|
|
@ -558,8 +684,8 @@ const Contact: React.FC = () => {
|
|||
}}
|
||||
></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>
|
||||
<h1 className={content?.heroTitleStyleClass || 'ml-4 mb-2 mt-3 text-5xl font-bold text-white'}>{content?.heroTitle}</h1>
|
||||
<p className={content?.heroSubtitleStyleClass || 'ml-4 max-w-3xl text-xl'}>{content?.heroSubtitle}</p>
|
||||
</div>
|
||||
</div>
|
||||
</SelectableBlock>
|
||||
|
|
@ -574,8 +700,8 @@ const Contact: React.FC = () => {
|
|||
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={content?.contactInfoCardStyleClass || 'rounded-xl bg-white p-8 shadow-lg'}>
|
||||
<h2 className={content?.contactInfoTitleStyleClass || '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" />
|
||||
|
|
@ -621,7 +747,7 @@ const Contact: React.FC = () => {
|
|||
isDesignMode={isDesignMode}
|
||||
onSelect={handleSelectBlock}
|
||||
>
|
||||
<div className="rounded-xl bg-white p-8 shadow-lg">
|
||||
<div className={content?.bankStyleClass || '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
|
||||
|
|
@ -645,7 +771,7 @@ const Contact: React.FC = () => {
|
|||
isDesignMode={isDesignMode}
|
||||
onSelect={handleSelectBlock}
|
||||
>
|
||||
<div className="rounded-xl bg-white p-8 shadow-lg">
|
||||
<div className={content?.workHoursStyleClass || '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">
|
||||
|
|
@ -671,9 +797,9 @@ const Contact: React.FC = () => {
|
|||
isDesignMode={isDesignMode}
|
||||
onSelect={handleSelectBlock}
|
||||
>
|
||||
<div className="rounded-xl bg-white p-8 shadow-lg">
|
||||
<div className={content?.mapContainerStyleClass || '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">
|
||||
<div className={content?.mapFrameStyleClass || 'aspect-w-16 aspect-h-9 overflow-hidden rounded-xl bg-gray-200'}>
|
||||
<iframe
|
||||
src={content?.mapSrc}
|
||||
width={content?.mapWidth}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ interface HomeSlideServiceContent {
|
|||
titleKey: string
|
||||
description: string
|
||||
descriptionKey: string
|
||||
styleClass: string
|
||||
}
|
||||
|
||||
interface HomeSlideContent {
|
||||
|
|
@ -32,6 +33,7 @@ interface HomeSlideContent {
|
|||
titleKey: string
|
||||
subtitle: string
|
||||
subtitleKey: string
|
||||
styleClass: string
|
||||
services: HomeSlideServiceContent[]
|
||||
}
|
||||
|
||||
|
|
@ -41,6 +43,7 @@ interface HomeFeatureContent {
|
|||
titleKey: string
|
||||
description: string
|
||||
descriptionKey: string
|
||||
styleClass: string
|
||||
}
|
||||
|
||||
interface HomeSolutionContent {
|
||||
|
|
@ -50,6 +53,7 @@ interface HomeSolutionContent {
|
|||
titleKey: string
|
||||
description: string
|
||||
descriptionKey: string
|
||||
styleClass: string
|
||||
}
|
||||
|
||||
interface HomeContent {
|
||||
|
|
@ -57,40 +61,75 @@ interface HomeContent {
|
|||
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: HomeFeatureContent[]
|
||||
solutions: HomeSolutionContent[]
|
||||
}
|
||||
|
||||
const HOME_HERO_BACKGROUND_KEY = 'Public.home.hero.backgroundImage'
|
||||
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 HOME_HERO_PRIMARY_CTA_KEY = 'Public.hero.cta.consultation'
|
||||
const HOME_HERO_SECONDARY_CTA_KEY = 'Public.hero.cta.discover'
|
||||
const HOME_FEATURES_TITLE_KEY = 'Public.features.title'
|
||||
const HOME_FEATURES_SUBTITLE_KEY = 'Public.features.subtitle'
|
||||
const HOME_SOLUTIONS_TITLE_KEY = 'Public.solutions.title'
|
||||
const HOME_SOLUTIONS_SUBTITLE_KEY = 'Public.solutions.subtitle'
|
||||
const HOME_CTA_TITLE_KEY = 'Public.common.getStarted'
|
||||
const HOME_CTA_SUBTITLE_KEY = 'Public.common.contact'
|
||||
const HOME_CTA_BUTTON_KEY = 'Public.common.learnMore'
|
||||
const HOME_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 HOME_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 HOME_FEATURES_TITLE_STYLE_DEFAULT = 'text-4xl font-bold text-gray-900 mb-4'
|
||||
const HOME_FEATURES_SUBTITLE_STYLE_DEFAULT = 'text-xl text-gray-600 max-w-2xl mx-auto'
|
||||
const HOME_SOLUTIONS_TITLE_STYLE_DEFAULT = 'text-4xl font-bold text-gray-900 mb-4'
|
||||
const HOME_SOLUTIONS_SUBTITLE_STYLE_DEFAULT = 'text-xl text-gray-600 max-w-2xl mx-auto'
|
||||
const HOME_CTA_TITLE_STYLE_DEFAULT = 'text-3xl font-bold text-white mb-4'
|
||||
const HOME_CTA_SUBTITLE_STYLE_DEFAULT = 'text-white text-lg mb-8'
|
||||
const HOME_CTA_BUTTON_STYLE_DEFAULT =
|
||||
'bg-white text-blue-600 px-8 py-3 rounded-lg font-semibold hover:bg-blue-50 transition-colors'
|
||||
const HOME_SLIDE_STYLE_DEFAULT = 'max-w-4xl mx-auto text-center'
|
||||
const HOME_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 HOME_FEATURE_CARD_STYLE_DEFAULT =
|
||||
'p-8 bg-white rounded-xl shadow-lg hover:shadow-xl transition-shadow'
|
||||
const HOME_SOLUTION_CARD_STYLE_DEFAULT = 'p-8 h-full rounded-2xl'
|
||||
|
||||
function isLikelyLocalizationKey(value?: string) {
|
||||
return Boolean(value && /^[A-Za-z0-9_.-]+$/.test(value) && value.includes('.'))
|
||||
|
|
@ -113,206 +152,121 @@ function resolveLocalizedValue(
|
|||
return translatedValue === keyOrValue ? fallback || keyOrValue : translatedValue
|
||||
}
|
||||
|
||||
function defaultSlides() {
|
||||
return [
|
||||
{
|
||||
titleKey: 'Public.hero.slide1.title',
|
||||
subtitleKey: 'Public.hero.slide1.subtitle',
|
||||
services: [
|
||||
{
|
||||
icon: 'FaCalendarAlt',
|
||||
titleKey: 'Public.hero.slide1.service1.title',
|
||||
descriptionKey: 'Public.hero.slide1.service1.desc',
|
||||
},
|
||||
{
|
||||
icon: 'FaUsers',
|
||||
titleKey: 'Public.hero.slide1.service2.title',
|
||||
descriptionKey: 'Public.hero.slide1.service2.desc',
|
||||
},
|
||||
{
|
||||
icon: 'FaShieldAlt',
|
||||
titleKey: 'Public.hero.slide1.service3.title',
|
||||
descriptionKey: 'Public.hero.slide1.service3.desc',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
titleKey: 'Public.hero.slide2.title',
|
||||
subtitleKey: 'Public.hero.slide2.subtitle',
|
||||
services: [
|
||||
{
|
||||
icon: 'FaChartBar',
|
||||
titleKey: 'Public.hero.slide2.service1.title',
|
||||
descriptionKey: 'Public.hero.slide2.service1.desc',
|
||||
},
|
||||
{
|
||||
icon: 'FaCreditCard',
|
||||
titleKey: 'Public.hero.slide2.service2.title',
|
||||
descriptionKey: 'Public.hero.slide2.service2.desc',
|
||||
},
|
||||
{
|
||||
icon: 'FaDatabase',
|
||||
titleKey: 'Public.hero.slide2.service3.title',
|
||||
descriptionKey: 'Public.hero.slide2.service3.desc',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
titleKey: 'Public.hero.slide3.title',
|
||||
subtitleKey: 'Public.hero.slide3.subtitle',
|
||||
services: [
|
||||
{
|
||||
icon: 'FaDesktop',
|
||||
titleKey: 'Public.hero.slide3.service1.title',
|
||||
descriptionKey: 'Public.hero.slide3.service1.desc',
|
||||
},
|
||||
{
|
||||
icon: 'FaServer',
|
||||
titleKey: 'Public.hero.slide3.service2.title',
|
||||
descriptionKey: 'Public.hero.slide3.service2.desc',
|
||||
},
|
||||
{
|
||||
icon: 'FaMobileAlt',
|
||||
titleKey: 'Public.hero.slide3.service3.title',
|
||||
descriptionKey: 'Public.hero.slide3.service3.desc',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
function defaultFeatures() {
|
||||
return [
|
||||
{ icon: 'FaUsers', titleKey: 'Public.features.reliable', descriptionKey: 'Public.features.reliable.desc' },
|
||||
{
|
||||
icon: 'FaCalendarAlt',
|
||||
titleKey: 'App.Coordinator.Classroom.Planning',
|
||||
descriptionKey: 'Public.features.rapid.desc',
|
||||
},
|
||||
{ icon: 'FaBookOpen', titleKey: 'Public.features.expert', descriptionKey: 'Public.features.expert.desc' },
|
||||
{
|
||||
icon: 'FaCreditCard',
|
||||
titleKey: 'Public.features.muhasebe',
|
||||
descriptionKey: 'Public.features.muhasebe.desc',
|
||||
},
|
||||
{
|
||||
icon: 'FaRegComment',
|
||||
titleKey: 'Public.features.iletisim',
|
||||
descriptionKey: 'Public.features.iletisim.desc',
|
||||
},
|
||||
{ icon: 'FaPhone', titleKey: 'Public.features.mobil', descriptionKey: 'Public.features.mobil.desc' },
|
||||
{ icon: 'FaChartBar', titleKey: 'Public.features.scalable', descriptionKey: 'Public.features.scalable.desc' },
|
||||
{
|
||||
icon: 'FaShieldAlt',
|
||||
titleKey: 'Public.features.guvenlik',
|
||||
descriptionKey: 'Public.features.guvenlik.desc',
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
function defaultSolutions() {
|
||||
return [
|
||||
{
|
||||
icon: 'FaDesktop',
|
||||
colorClass: 'bg-blue-600',
|
||||
titleKey: 'Public.services.web.title',
|
||||
descriptionKey: 'Public.solutions.web.desc',
|
||||
},
|
||||
{
|
||||
icon: 'FaMobileAlt',
|
||||
colorClass: 'bg-purple-600',
|
||||
titleKey: 'Public.services.mobile.title',
|
||||
descriptionKey: 'Public.solutions.mobile.desc',
|
||||
},
|
||||
{
|
||||
icon: 'FaServer',
|
||||
colorClass: 'bg-green-600',
|
||||
titleKey: 'Public.solutions.custom.title',
|
||||
descriptionKey: 'Public.solutions.custom.desc',
|
||||
},
|
||||
{
|
||||
icon: 'FaDatabase',
|
||||
colorClass: 'bg-red-600',
|
||||
titleKey: 'Public.solutions.database.title',
|
||||
descriptionKey: 'Public.solutions.database.desc',
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
function buildHomeContent(home: HomeDto | undefined, translate: (key: string) => string): HomeContent {
|
||||
const slideItems = home?.slidesDto?.length ? home.slidesDto : defaultSlides()
|
||||
const featureItems = home?.featuresDto?.length ? home.featuresDto : defaultFeatures()
|
||||
const solutionItems = home?.solutionsDto?.length ? home.solutionsDto : defaultSolutions()
|
||||
const slideItems = home?.slidesDto || []
|
||||
const featureItems = home?.featuresDto || []
|
||||
const solutionItems = home?.solutionsDto || []
|
||||
|
||||
return {
|
||||
heroBackgroundImage: resolveLocalizedValue(
|
||||
translate,
|
||||
home?.heroBackgroundImageKey || HOME_HERO_BACKGROUND_KEY,
|
||||
home?.heroBackgroundImageKey,
|
||||
HOME_HERO_DEFAULT_IMAGE,
|
||||
),
|
||||
heroBackgroundImageKey: home?.heroBackgroundImageKey || HOME_HERO_BACKGROUND_KEY,
|
||||
heroPrimaryCtaLabel: resolveLocalizedValue(
|
||||
heroBackgroundImageKey: home?.heroBackgroundImageKey || '',
|
||||
heroPrimaryCtaLabel: resolveLocalizedValue(translate, home?.heroPrimaryCtaKey, ''),
|
||||
heroPrimaryCtaKey: home?.heroPrimaryCtaKey || '',
|
||||
heroPrimaryCtaStyle: resolveLocalizedValue(
|
||||
translate,
|
||||
home?.heroPrimaryCtaKey || HOME_HERO_PRIMARY_CTA_KEY,
|
||||
HOME_HERO_PRIMARY_CTA_STYLE_KEY,
|
||||
HOME_HERO_PRIMARY_CTA_STYLE_DEFAULT,
|
||||
),
|
||||
heroPrimaryCtaKey: home?.heroPrimaryCtaKey || HOME_HERO_PRIMARY_CTA_KEY,
|
||||
heroSecondaryCtaLabel: resolveLocalizedValue(
|
||||
heroPrimaryCtaStyleKey: HOME_HERO_PRIMARY_CTA_STYLE_KEY,
|
||||
heroSecondaryCtaLabel: resolveLocalizedValue(translate, home?.heroSecondaryCtaKey, ''),
|
||||
heroSecondaryCtaKey: home?.heroSecondaryCtaKey || '',
|
||||
heroSecondaryCtaStyle: resolveLocalizedValue(
|
||||
translate,
|
||||
home?.heroSecondaryCtaKey || HOME_HERO_SECONDARY_CTA_KEY,
|
||||
HOME_HERO_SECONDARY_CTA_STYLE_KEY,
|
||||
HOME_HERO_SECONDARY_CTA_STYLE_DEFAULT,
|
||||
),
|
||||
heroSecondaryCtaKey: home?.heroSecondaryCtaKey || HOME_HERO_SECONDARY_CTA_KEY,
|
||||
featuresTitle: resolveLocalizedValue(translate, home?.featuresTitleKey || HOME_FEATURES_TITLE_KEY),
|
||||
featuresTitleKey: home?.featuresTitleKey || HOME_FEATURES_TITLE_KEY,
|
||||
featuresSubtitle: resolveLocalizedValue(
|
||||
heroSecondaryCtaStyleKey: HOME_HERO_SECONDARY_CTA_STYLE_KEY,
|
||||
featuresTitle: resolveLocalizedValue(translate, home?.featuresTitleKey, ''),
|
||||
featuresTitleKey: home?.featuresTitleKey || '',
|
||||
featuresTitleStyle: resolveLocalizedValue(
|
||||
translate,
|
||||
home?.featuresSubtitleKey || HOME_FEATURES_SUBTITLE_KEY,
|
||||
HOME_FEATURES_TITLE_STYLE_KEY,
|
||||
HOME_FEATURES_TITLE_STYLE_DEFAULT,
|
||||
),
|
||||
featuresSubtitleKey: home?.featuresSubtitleKey || HOME_FEATURES_SUBTITLE_KEY,
|
||||
solutionsTitle: resolveLocalizedValue(translate, home?.solutionsTitleKey || HOME_SOLUTIONS_TITLE_KEY),
|
||||
solutionsTitleKey: home?.solutionsTitleKey || HOME_SOLUTIONS_TITLE_KEY,
|
||||
solutionsSubtitle: resolveLocalizedValue(
|
||||
featuresTitleStyleKey: HOME_FEATURES_TITLE_STYLE_KEY,
|
||||
featuresSubtitle: resolveLocalizedValue(translate, home?.featuresSubtitleKey, ''),
|
||||
featuresSubtitleKey: home?.featuresSubtitleKey || '',
|
||||
featuresSubtitleStyle: resolveLocalizedValue(
|
||||
translate,
|
||||
home?.solutionsSubtitleKey || HOME_SOLUTIONS_SUBTITLE_KEY,
|
||||
HOME_FEATURES_SUBTITLE_STYLE_KEY,
|
||||
HOME_FEATURES_SUBTITLE_STYLE_DEFAULT,
|
||||
),
|
||||
solutionsSubtitleKey: home?.solutionsSubtitleKey || HOME_SOLUTIONS_SUBTITLE_KEY,
|
||||
ctaTitle: resolveLocalizedValue(translate, home?.ctaTitleKey || HOME_CTA_TITLE_KEY),
|
||||
ctaTitleKey: home?.ctaTitleKey || HOME_CTA_TITLE_KEY,
|
||||
ctaSubtitle: resolveLocalizedValue(translate, home?.ctaSubtitleKey || HOME_CTA_SUBTITLE_KEY),
|
||||
ctaSubtitleKey: home?.ctaSubtitleKey || HOME_CTA_SUBTITLE_KEY,
|
||||
ctaButtonLabel: resolveLocalizedValue(translate, home?.ctaButtonLabelKey || HOME_CTA_BUTTON_KEY),
|
||||
ctaButtonLabelKey: home?.ctaButtonLabelKey || HOME_CTA_BUTTON_KEY,
|
||||
slides: slideItems.map((slide, slideIndex) => ({
|
||||
featuresSubtitleStyleKey: HOME_FEATURES_SUBTITLE_STYLE_KEY,
|
||||
solutionsTitle: resolveLocalizedValue(translate, home?.solutionsTitleKey, ''),
|
||||
solutionsTitleKey: home?.solutionsTitleKey || '',
|
||||
solutionsTitleStyle: resolveLocalizedValue(
|
||||
translate,
|
||||
HOME_SOLUTIONS_TITLE_STYLE_KEY,
|
||||
HOME_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,
|
||||
HOME_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,
|
||||
HOME_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,
|
||||
HOME_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,
|
||||
HOME_CTA_BUTTON_STYLE_DEFAULT,
|
||||
),
|
||||
ctaButtonStyleKey: HOME_CTA_BUTTON_STYLE_KEY,
|
||||
slides: slideItems.map((slide) => ({
|
||||
title: resolveLocalizedValue(translate, slide.titleKey, slide.titleKey),
|
||||
titleKey: slide.titleKey || `Public.home.dynamic.slide.${slideIndex + 1}.title`,
|
||||
titleKey: slide.titleKey || '',
|
||||
subtitle: resolveLocalizedValue(translate, slide.subtitleKey, slide.subtitleKey),
|
||||
subtitleKey: slide.subtitleKey || `Public.home.dynamic.slide.${slideIndex + 1}.subtitle`,
|
||||
services: (slide.services || []).map((service, serviceIndex) => ({
|
||||
icon: service.icon || 'FaCircle',
|
||||
subtitleKey: slide.subtitleKey || '',
|
||||
styleClass: slide.styleClass || HOME_SLIDE_STYLE_DEFAULT,
|
||||
services: (slide.services || []).map((service) => ({
|
||||
icon: service.icon || '',
|
||||
title: resolveLocalizedValue(translate, service.titleKey, service.titleKey),
|
||||
titleKey:
|
||||
service.titleKey ||
|
||||
`Public.home.dynamic.slide.${slideIndex + 1}.service.${serviceIndex + 1}.title`,
|
||||
titleKey: service.titleKey || '',
|
||||
description: resolveLocalizedValue(translate, service.descriptionKey, service.descriptionKey),
|
||||
descriptionKey:
|
||||
service.descriptionKey ||
|
||||
`Public.home.dynamic.slide.${slideIndex + 1}.service.${serviceIndex + 1}.description`,
|
||||
descriptionKey: service.descriptionKey || '',
|
||||
styleClass: service.styleClass || HOME_SLIDE_SERVICE_STYLE_DEFAULT,
|
||||
})),
|
||||
})),
|
||||
features: featureItems.map((feature, index) => ({
|
||||
icon: feature.icon || 'FaCircle',
|
||||
features: featureItems.map((feature) => ({
|
||||
icon: feature.icon || '',
|
||||
title: resolveLocalizedValue(translate, feature.titleKey, feature.titleKey),
|
||||
titleKey: feature.titleKey || `Public.home.dynamic.feature.${index + 1}.title`,
|
||||
titleKey: feature.titleKey || '',
|
||||
description: resolveLocalizedValue(translate, feature.descriptionKey, feature.descriptionKey),
|
||||
descriptionKey: feature.descriptionKey || `Public.home.dynamic.feature.${index + 1}.description`,
|
||||
descriptionKey: feature.descriptionKey || '',
|
||||
styleClass: feature.styleClass || HOME_FEATURE_CARD_STYLE_DEFAULT,
|
||||
})),
|
||||
solutions: solutionItems.map((solution, index) => ({
|
||||
icon: solution.icon || 'FaCircle',
|
||||
colorClass: solution.colorClass || 'bg-blue-600',
|
||||
solutions: solutionItems.map((solution) => ({
|
||||
icon: solution.icon || '',
|
||||
colorClass: solution.colorClass || '',
|
||||
title: resolveLocalizedValue(translate, solution.titleKey, solution.titleKey),
|
||||
titleKey: solution.titleKey || `Public.home.dynamic.solution.${index + 1}.title`,
|
||||
titleKey: solution.titleKey || '',
|
||||
description: resolveLocalizedValue(translate, solution.descriptionKey, solution.descriptionKey),
|
||||
descriptionKey:
|
||||
solution.descriptionKey || `Public.home.dynamic.solution.${index + 1}.description`,
|
||||
descriptionKey: solution.descriptionKey || '',
|
||||
styleClass: solution.styleClass || HOME_SOLUTION_CARD_STYLE_DEFAULT,
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
|
@ -428,14 +382,23 @@ const Home: React.FC = () => {
|
|||
if (
|
||||
fieldKey === 'heroBackgroundImage' ||
|
||||
fieldKey === 'heroPrimaryCtaLabel' ||
|
||||
fieldKey === 'heroPrimaryCtaStyle' ||
|
||||
fieldKey === 'heroSecondaryCtaLabel' ||
|
||||
fieldKey === 'heroSecondaryCtaStyle' ||
|
||||
fieldKey === 'featuresTitle' ||
|
||||
fieldKey === 'featuresTitleStyle' ||
|
||||
fieldKey === 'featuresSubtitle' ||
|
||||
fieldKey === 'featuresSubtitleStyle' ||
|
||||
fieldKey === 'solutionsTitle' ||
|
||||
fieldKey === 'solutionsTitleStyle' ||
|
||||
fieldKey === 'solutionsSubtitle' ||
|
||||
fieldKey === 'solutionsSubtitleStyle' ||
|
||||
fieldKey === 'ctaTitle' ||
|
||||
fieldKey === 'ctaTitleStyle' ||
|
||||
fieldKey === 'ctaSubtitle' ||
|
||||
fieldKey === 'ctaButtonLabel'
|
||||
fieldKey === 'ctaSubtitleStyle' ||
|
||||
fieldKey === 'ctaButtonLabel' ||
|
||||
fieldKey === 'ctaButtonStyle'
|
||||
) {
|
||||
return {
|
||||
...current,
|
||||
|
|
@ -452,12 +415,12 @@ const Home: React.FC = () => {
|
|||
const slides = [...current.slides]
|
||||
const target = { ...slides[index] }
|
||||
|
||||
if (fieldKey === 'title' || fieldKey === 'subtitle') {
|
||||
if (fieldKey === 'title' || fieldKey === 'subtitle' || fieldKey === 'styleClass') {
|
||||
target[fieldKey] = nextValue
|
||||
} else if (fieldKey.startsWith('service-')) {
|
||||
const parts = fieldKey.split('-')
|
||||
const serviceIndex = Number(parts[1])
|
||||
const serviceField = parts[2] as 'icon' | 'title' | 'description'
|
||||
const serviceField = parts[2] as 'icon' | 'title' | 'description' | 'styleClass'
|
||||
const services = [...target.services]
|
||||
const service = { ...services[serviceIndex] }
|
||||
service[serviceField] = nextValue
|
||||
|
|
@ -480,7 +443,7 @@ const Home: React.FC = () => {
|
|||
|
||||
const features = [...current.features]
|
||||
const feature = { ...features[index] }
|
||||
const key = fieldKey as 'icon' | 'title' | 'description'
|
||||
const key = fieldKey as 'icon' | 'title' | 'description' | 'styleClass'
|
||||
feature[key] = nextValue
|
||||
features[index] = feature
|
||||
|
||||
|
|
@ -498,7 +461,7 @@ const Home: React.FC = () => {
|
|||
|
||||
const solutions = [...current.solutions]
|
||||
const solution = { ...solutions[index] }
|
||||
const key = fieldKey as 'icon' | 'title' | 'description' | 'colorClass'
|
||||
const key = fieldKey as 'icon' | 'title' | 'description' | 'colorClass' | 'styleClass'
|
||||
solution[key] = nextValue
|
||||
solutions[index] = solution
|
||||
|
||||
|
|
@ -535,12 +498,24 @@ const Home: React.FC = () => {
|
|||
type: 'text',
|
||||
value: content.heroPrimaryCtaLabel,
|
||||
},
|
||||
{
|
||||
key: 'heroPrimaryCtaStyle',
|
||||
label: content.heroPrimaryCtaStyleKey,
|
||||
type: 'text',
|
||||
value: content.heroPrimaryCtaStyle,
|
||||
},
|
||||
{
|
||||
key: 'heroSecondaryCtaLabel',
|
||||
label: content.heroSecondaryCtaKey,
|
||||
type: 'text',
|
||||
value: content.heroSecondaryCtaLabel,
|
||||
},
|
||||
{
|
||||
key: 'heroSecondaryCtaStyle',
|
||||
label: content.heroSecondaryCtaStyleKey,
|
||||
type: 'text',
|
||||
value: content.heroSecondaryCtaStyle,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
|
@ -570,6 +545,12 @@ const Home: React.FC = () => {
|
|||
type: 'textarea',
|
||||
value: slide.subtitle,
|
||||
},
|
||||
{
|
||||
key: 'styleClass',
|
||||
label: `Slide${index + 1}.StyleClass`,
|
||||
type: 'text',
|
||||
value: slide.styleClass,
|
||||
},
|
||||
...slide.services.flatMap((service, serviceIndex) => [
|
||||
{
|
||||
key: `service-${serviceIndex}-icon`,
|
||||
|
|
@ -590,6 +571,12 @@ const Home: React.FC = () => {
|
|||
type: 'textarea' as const,
|
||||
value: service.description,
|
||||
},
|
||||
{
|
||||
key: `service-${serviceIndex}-styleClass`,
|
||||
label: `Slide${index + 1}.Service${serviceIndex + 1}.StyleClass`,
|
||||
type: 'text' as const,
|
||||
value: service.styleClass,
|
||||
},
|
||||
]),
|
||||
],
|
||||
}
|
||||
|
|
@ -607,12 +594,24 @@ const Home: React.FC = () => {
|
|||
type: 'text',
|
||||
value: content.featuresTitle,
|
||||
},
|
||||
{
|
||||
key: 'featuresTitleStyle',
|
||||
label: content.featuresTitleStyleKey,
|
||||
type: 'text',
|
||||
value: content.featuresTitleStyle,
|
||||
},
|
||||
{
|
||||
key: 'featuresSubtitle',
|
||||
label: content.featuresSubtitleKey,
|
||||
type: 'textarea',
|
||||
value: content.featuresSubtitle,
|
||||
},
|
||||
{
|
||||
key: 'featuresSubtitleStyle',
|
||||
label: content.featuresSubtitleStyleKey,
|
||||
type: 'text',
|
||||
value: content.featuresSubtitleStyle,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
|
@ -649,6 +648,12 @@ const Home: React.FC = () => {
|
|||
type: 'textarea',
|
||||
value: item.description,
|
||||
},
|
||||
{
|
||||
key: 'styleClass',
|
||||
label: `Feature${index + 1}.StyleClass`,
|
||||
type: 'text',
|
||||
value: item.styleClass,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
|
@ -665,12 +670,24 @@ const Home: React.FC = () => {
|
|||
type: 'text',
|
||||
value: content.solutionsTitle,
|
||||
},
|
||||
{
|
||||
key: 'solutionsTitleStyle',
|
||||
label: content.solutionsTitleStyleKey,
|
||||
type: 'text',
|
||||
value: content.solutionsTitleStyle,
|
||||
},
|
||||
{
|
||||
key: 'solutionsSubtitle',
|
||||
label: content.solutionsSubtitleKey,
|
||||
type: 'textarea',
|
||||
value: content.solutionsSubtitle,
|
||||
},
|
||||
{
|
||||
key: 'solutionsSubtitleStyle',
|
||||
label: content.solutionsSubtitleStyleKey,
|
||||
type: 'text',
|
||||
value: content.solutionsSubtitleStyle,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
|
@ -713,6 +730,12 @@ const Home: React.FC = () => {
|
|||
type: 'textarea',
|
||||
value: item.description,
|
||||
},
|
||||
{
|
||||
key: 'styleClass',
|
||||
label: `Solution${index + 1}.StyleClass`,
|
||||
type: 'text',
|
||||
value: item.styleClass,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
|
@ -729,18 +752,36 @@ const Home: React.FC = () => {
|
|||
type: 'text',
|
||||
value: content.ctaTitle,
|
||||
},
|
||||
{
|
||||
key: 'ctaTitleStyle',
|
||||
label: content.ctaTitleStyleKey,
|
||||
type: 'text',
|
||||
value: content.ctaTitleStyle,
|
||||
},
|
||||
{
|
||||
key: 'ctaSubtitle',
|
||||
label: content.ctaSubtitleKey,
|
||||
type: 'textarea',
|
||||
value: content.ctaSubtitle,
|
||||
},
|
||||
{
|
||||
key: 'ctaSubtitleStyle',
|
||||
label: content.ctaSubtitleStyleKey,
|
||||
type: 'text',
|
||||
value: content.ctaSubtitleStyle,
|
||||
},
|
||||
{
|
||||
key: 'ctaButtonLabel',
|
||||
label: content.ctaButtonLabelKey,
|
||||
type: 'text',
|
||||
value: content.ctaButtonLabel,
|
||||
},
|
||||
{
|
||||
key: 'ctaButtonStyle',
|
||||
label: content.ctaButtonStyleKey,
|
||||
type: 'text',
|
||||
value: content.ctaButtonStyle,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
|
@ -762,54 +803,71 @@ const Home: React.FC = () => {
|
|||
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, slideIndex) => ({
|
||||
titleKey: slide.titleKey || `Public.home.dynamic.slide.${slideIndex + 1}.title`,
|
||||
titleKey: slide.titleKey || '',
|
||||
titleValue: slide.title,
|
||||
subtitleKey: slide.subtitleKey || `Public.home.dynamic.slide.${slideIndex + 1}.subtitle`,
|
||||
subtitleKey: slide.subtitleKey || '',
|
||||
subtitleValue: slide.subtitle,
|
||||
styleClass: slide.styleClass,
|
||||
services: slide.services.map((service, serviceIndex) => ({
|
||||
icon: service.icon,
|
||||
titleKey:
|
||||
service.titleKey ||
|
||||
`Public.home.dynamic.slide.${slideIndex + 1}.service.${serviceIndex + 1}.title`,
|
||||
titleKey: service.titleKey || '',
|
||||
titleValue: service.title,
|
||||
descriptionKey:
|
||||
service.descriptionKey ||
|
||||
`Public.home.dynamic.slide.${slideIndex + 1}.service.${serviceIndex + 1}.description`,
|
||||
descriptionKey: service.descriptionKey || '',
|
||||
descriptionValue: service.description,
|
||||
styleClass: service.styleClass,
|
||||
})),
|
||||
})),
|
||||
features: content.features.map((feature, index) => ({
|
||||
icon: feature.icon,
|
||||
titleKey: feature.titleKey || `Public.home.dynamic.feature.${index + 1}.title`,
|
||||
titleKey: feature.titleKey || '',
|
||||
titleValue: feature.title,
|
||||
descriptionKey: feature.descriptionKey || `Public.home.dynamic.feature.${index + 1}.description`,
|
||||
descriptionKey: feature.descriptionKey || '',
|
||||
descriptionValue: feature.description,
|
||||
styleClass: feature.styleClass,
|
||||
})),
|
||||
solutions: content.solutions.map((solution, index) => ({
|
||||
icon: solution.icon,
|
||||
colorClass: solution.colorClass,
|
||||
titleKey: solution.titleKey || `Public.home.dynamic.solution.${index + 1}.title`,
|
||||
titleKey: solution.titleKey || '',
|
||||
titleValue: solution.title,
|
||||
descriptionKey:
|
||||
solution.descriptionKey || `Public.home.dynamic.solution.${index + 1}.description`,
|
||||
descriptionKey: solution.descriptionKey || '',
|
||||
descriptionValue: solution.description,
|
||||
styleClass: solution.styleClass,
|
||||
})),
|
||||
})
|
||||
|
||||
|
|
@ -918,7 +976,7 @@ const Home: React.FC = () => {
|
|||
}}
|
||||
className="h-full"
|
||||
>
|
||||
<div className="container mx-auto px-4 pt-32">
|
||||
<div className={`container mx-auto px-4 pt-32 ${slide.styleClass || ''}`}>
|
||||
<div className="max-w-4xl mx-auto text-center">
|
||||
<h1 className="text-3xl md:text-6xl font-bold mb-6 text-white animate-fade-in">
|
||||
{slide.title}
|
||||
|
|
@ -930,14 +988,14 @@ const Home: React.FC = () => {
|
|||
<div className="flex flex-col md:flex-row justify-center gap-6 mb-16">
|
||||
<Link
|
||||
to={ROUTES_ENUM.public.contact}
|
||||
className="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"
|
||||
className={content?.heroPrimaryCtaStyle}
|
||||
>
|
||||
{content?.heroPrimaryCtaLabel}{' '}
|
||||
<FaArrowRight className="ml-2" size={20} />
|
||||
</Link>
|
||||
<Link
|
||||
to={ROUTES_ENUM.public.products}
|
||||
className="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"
|
||||
className={content?.heroSecondaryCtaStyle}
|
||||
>
|
||||
{content?.heroSecondaryCtaLabel}
|
||||
</Link>
|
||||
|
|
@ -947,7 +1005,10 @@ const Home: React.FC = () => {
|
|||
{slide.services.map((service, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="bg-white/5 backdrop-blur-sm rounded-2xl p-8 text-center hover:scale-105 hover:bg-white/10 transition-all"
|
||||
className={
|
||||
service.styleClass ||
|
||||
'bg-white/5 backdrop-blur-sm rounded-2xl p-8 text-center hover:scale-105 hover:bg-white/10 transition-all'
|
||||
}
|
||||
>
|
||||
{(() => {
|
||||
const IconComponent = navigationIcon[service.icon || '']
|
||||
|
|
@ -1029,10 +1090,10 @@ const Home: React.FC = () => {
|
|||
onSelect={handleSelectBlock}
|
||||
>
|
||||
<div className="text-center mb-16">
|
||||
<h2 className="text-4xl font-bold text-gray-900 mb-4">
|
||||
<h2 className={content?.featuresTitleStyle}>
|
||||
{content?.featuresTitle}
|
||||
</h2>
|
||||
<p className="text-xl text-gray-600 max-w-2xl mx-auto">
|
||||
<p className={content?.featuresSubtitleStyle}>
|
||||
{content?.featuresSubtitle}
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -1047,7 +1108,12 @@ const Home: React.FC = () => {
|
|||
isDesignMode={isDesignMode}
|
||||
onSelect={handleSelectBlock}
|
||||
>
|
||||
<div className="p-8 bg-white rounded-xl shadow-lg hover:shadow-xl transition-shadow">
|
||||
<div
|
||||
className={
|
||||
feature.styleClass ||
|
||||
'p-8 bg-white rounded-xl shadow-lg hover:shadow-xl transition-shadow'
|
||||
}
|
||||
>
|
||||
<div className="mb-6">
|
||||
{(() => {
|
||||
const IconComponent = navigationIcon[feature.icon || '']
|
||||
|
|
@ -1073,10 +1139,10 @@ const Home: React.FC = () => {
|
|||
onSelect={handleSelectBlock}
|
||||
>
|
||||
<div className="text-center mb-16">
|
||||
<h2 className="text-4xl font-bold text-gray-900 mb-4">
|
||||
<h2 className={content?.solutionsTitleStyle}>
|
||||
{content?.solutionsTitle}
|
||||
</h2>
|
||||
<p className="text-xl text-gray-600 max-w-2xl mx-auto">
|
||||
<p className={content?.solutionsSubtitleStyle}>
|
||||
{content?.solutionsSubtitle}
|
||||
</p>
|
||||
</div>
|
||||
|
|
@ -1091,7 +1157,7 @@ const Home: React.FC = () => {
|
|||
isDesignMode={isDesignMode}
|
||||
onSelect={handleSelectBlock}
|
||||
>
|
||||
<div className={`${s.colorClass} p-8 h-full rounded-2xl`}>
|
||||
<div className={`${s.colorClass} ${s.styleClass || 'p-8 h-full rounded-2xl'}`}>
|
||||
<div className="mb-6">
|
||||
{(() => {
|
||||
const IconComponent = navigationIcon[s.icon || '']
|
||||
|
|
@ -1116,11 +1182,11 @@ const Home: React.FC = () => {
|
|||
>
|
||||
<section className="bg-blue-600 py-16">
|
||||
<div className="container mx-auto px-4 text-center">
|
||||
<h2 className="text-3xl font-bold text-white mb-4">{content?.ctaTitle}</h2>
|
||||
<p className="text-white text-lg mb-8">{content?.ctaSubtitle}</p>
|
||||
<h2 className={content?.ctaTitleStyle}>{content?.ctaTitle}</h2>
|
||||
<p className={content?.ctaSubtitleStyle}>{content?.ctaSubtitle}</p>
|
||||
<Link
|
||||
to={ROUTES_ENUM.public.contact}
|
||||
className="bg-white text-blue-600 px-8 py-3 rounded-lg font-semibold hover:bg-blue-50 transition-colors"
|
||||
className={content?.ctaButtonStyle}
|
||||
>
|
||||
{content?.ctaButtonLabel}
|
||||
</Link>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
import { FaCheckCircle } from 'react-icons/fa'
|
||||
import { Link, useNavigate } from 'react-router-dom'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { useLocalization } from '@/utils/hooks/useLocalization'
|
||||
import { ROUTES_ENUM } from '@/routes/route.constant'
|
||||
import { Helmet } from 'react-helmet'
|
||||
|
|
@ -23,6 +23,14 @@ interface ServiceCardContent {
|
|||
titleKey: string
|
||||
description: string
|
||||
descriptionKey?: string
|
||||
cardStyleClassKey: string
|
||||
cardStyleClass: string
|
||||
titleStyleClassKey: string
|
||||
titleStyleClass: string
|
||||
descriptionStyleClassKey: string
|
||||
descriptionStyleClass: string
|
||||
featureStyleClassKey: string
|
||||
featureStyleClass: string
|
||||
features: Array<{
|
||||
key: string
|
||||
value: string
|
||||
|
|
@ -33,6 +41,10 @@ interface SupportCardContent {
|
|||
icon: string
|
||||
title: string
|
||||
titleKey: string
|
||||
cardStyleClassKey: string
|
||||
cardStyleClass: string
|
||||
titleStyleClassKey: string
|
||||
titleStyleClass: string
|
||||
features: Array<{
|
||||
key: string
|
||||
value: string
|
||||
|
|
@ -42,22 +54,40 @@ interface SupportCardContent {
|
|||
interface ServicesContent {
|
||||
heroTitle: string
|
||||
heroTitleKey: string
|
||||
heroSectionStyleClassKey: string
|
||||
heroSectionStyleClass: string
|
||||
heroTitleStyleClassKey: string
|
||||
heroTitleStyleClass: string
|
||||
heroSubtitle: string
|
||||
heroSubtitleKey: string
|
||||
heroSubtitleStyleClassKey: string
|
||||
heroSubtitleStyleClass: string
|
||||
heroImage: string
|
||||
heroImageKey: string
|
||||
serviceItems: ServiceCardContent[]
|
||||
supportTitle: string
|
||||
supportTitleKey: string
|
||||
supportTitleStyleClassKey: string
|
||||
supportTitleStyleClass: string
|
||||
supportButtonStyleClassKey: string
|
||||
supportButtonStyleClass: string
|
||||
supportItems: SupportCardContent[]
|
||||
supportButtonLabel: string
|
||||
supportButtonLabelKey: string
|
||||
ctaTitle: string
|
||||
ctaTitleKey: string
|
||||
ctaSectionStyleClassKey: string
|
||||
ctaSectionStyleClass: string
|
||||
ctaTitleStyleClassKey: string
|
||||
ctaTitleStyleClass: string
|
||||
ctaDescription: string
|
||||
ctaDescriptionKey: string
|
||||
ctaDescriptionStyleClassKey: string
|
||||
ctaDescriptionStyleClass: string
|
||||
ctaButtonLabel: string
|
||||
ctaButtonLabelKey: string
|
||||
ctaButtonStyleClassKey: string
|
||||
ctaButtonStyleClass: string
|
||||
}
|
||||
|
||||
const SERVICES_HERO_IMAGE =
|
||||
|
|
@ -70,6 +100,15 @@ const SERVICES_SUPPORT_BUTTON_KEY = 'Public.services.support.contactButton'
|
|||
const SERVICES_CTA_TITLE_KEY = 'Public.services.cta.title'
|
||||
const SERVICES_CTA_DESCRIPTION_KEY = 'Public.services.cta.description'
|
||||
const SERVICES_CTA_BUTTON_KEY = 'Public.services.support.contactButton'
|
||||
const SERVICES_HERO_SECTION_STYLE_KEY = 'Public.services.hero.sectionStyleClass'
|
||||
const SERVICES_HERO_TITLE_STYLE_KEY = 'Public.services.hero.titleStyleClass'
|
||||
const SERVICES_HERO_SUBTITLE_STYLE_KEY = 'Public.services.hero.subtitleStyleClass'
|
||||
const SERVICES_SUPPORT_TITLE_STYLE_KEY = 'Public.services.support.titleStyleClass'
|
||||
const SERVICES_SUPPORT_BUTTON_STYLE_KEY = 'Public.services.support.buttonStyleClass'
|
||||
const SERVICES_CTA_SECTION_STYLE_KEY = 'Public.services.cta.sectionStyleClass'
|
||||
const SERVICES_CTA_TITLE_STYLE_KEY = 'Public.services.cta.titleStyleClass'
|
||||
const SERVICES_CTA_DESCRIPTION_STYLE_KEY = 'Public.services.cta.descriptionStyleClass'
|
||||
const SERVICES_CTA_BUTTON_STYLE_KEY = 'Public.services.cta.buttonStyleClass'
|
||||
|
||||
function isLikelyLocalizationKey(value?: string) {
|
||||
return Boolean(value && /^[A-Za-z0-9_.-]+$/.test(value) && value.includes('.'))
|
||||
|
|
@ -99,8 +138,26 @@ function buildServicesContent(
|
|||
return {
|
||||
heroTitle: resolveLocalizedValue(translate, SERVICES_HERO_TITLE_KEY, 'Services'),
|
||||
heroTitleKey: SERVICES_HERO_TITLE_KEY,
|
||||
heroSectionStyleClassKey: SERVICES_HERO_SECTION_STYLE_KEY,
|
||||
heroSectionStyleClass: resolveLocalizedValue(
|
||||
translate,
|
||||
SERVICES_HERO_SECTION_STYLE_KEY,
|
||||
'relative bg-blue-900 text-white py-12',
|
||||
),
|
||||
heroTitleStyleClassKey: SERVICES_HERO_TITLE_STYLE_KEY,
|
||||
heroTitleStyleClass: resolveLocalizedValue(
|
||||
translate,
|
||||
SERVICES_HERO_TITLE_STYLE_KEY,
|
||||
'text-5xl font-bold ml-4 mt-3 mb-2 text-white',
|
||||
),
|
||||
heroSubtitle: resolveLocalizedValue(translate, SERVICES_HERO_SUBTITLE_KEY),
|
||||
heroSubtitleKey: SERVICES_HERO_SUBTITLE_KEY,
|
||||
heroSubtitleStyleClassKey: SERVICES_HERO_SUBTITLE_STYLE_KEY,
|
||||
heroSubtitleStyleClass: resolveLocalizedValue(
|
||||
translate,
|
||||
SERVICES_HERO_SUBTITLE_STYLE_KEY,
|
||||
'text-xl max-w-3xl ml-4',
|
||||
),
|
||||
heroImage: resolveLocalizedValue(translate, SERVICES_HERO_IMAGE_KEY, SERVICES_HERO_IMAGE),
|
||||
heroImageKey: SERVICES_HERO_IMAGE_KEY,
|
||||
serviceItems: services
|
||||
|
|
@ -115,6 +172,30 @@ function buildServicesContent(
|
|||
descriptionKey:
|
||||
(item.description && isLikelyLocalizationKey(item.description) ? item.description : undefined) ||
|
||||
`Public.services.dynamic.service.${index + 1}.description`,
|
||||
cardStyleClassKey: `Public.services.dynamic.service.${index + 1}.cardStyleClass`,
|
||||
cardStyleClass: resolveLocalizedValue(
|
||||
translate,
|
||||
`Public.services.dynamic.service.${index + 1}.cardStyleClass`,
|
||||
'bg-white rounded-xl shadow-lg p-8 hover:shadow-xl transition-shadow',
|
||||
),
|
||||
titleStyleClassKey: `Public.services.dynamic.service.${index + 1}.titleStyleClass`,
|
||||
titleStyleClass: resolveLocalizedValue(
|
||||
translate,
|
||||
`Public.services.dynamic.service.${index + 1}.titleStyleClass`,
|
||||
'text-2xl font-bold text-gray-900 mb-4',
|
||||
),
|
||||
descriptionStyleClassKey: `Public.services.dynamic.service.${index + 1}.descriptionStyleClass`,
|
||||
descriptionStyleClass: resolveLocalizedValue(
|
||||
translate,
|
||||
`Public.services.dynamic.service.${index + 1}.descriptionStyleClass`,
|
||||
'text-gray-600 mb-6',
|
||||
),
|
||||
featureStyleClassKey: `Public.services.dynamic.service.${index + 1}.featureStyleClass`,
|
||||
featureStyleClass: resolveLocalizedValue(
|
||||
translate,
|
||||
`Public.services.dynamic.service.${index + 1}.featureStyleClass`,
|
||||
'flex items-center text-gray-700',
|
||||
),
|
||||
features: (item.features ?? []).map((feature, featureIndex) => ({
|
||||
key:
|
||||
(isLikelyLocalizationKey(feature) ? feature : undefined) ||
|
||||
|
|
@ -124,6 +205,18 @@ function buildServicesContent(
|
|||
})),
|
||||
supportTitle: resolveLocalizedValue(translate, SERVICES_SUPPORT_TITLE_KEY),
|
||||
supportTitleKey: SERVICES_SUPPORT_TITLE_KEY,
|
||||
supportTitleStyleClassKey: SERVICES_SUPPORT_TITLE_STYLE_KEY,
|
||||
supportTitleStyleClass: resolveLocalizedValue(
|
||||
translate,
|
||||
SERVICES_SUPPORT_TITLE_STYLE_KEY,
|
||||
'text-3xl font-bold text-center mb-10',
|
||||
),
|
||||
supportButtonStyleClassKey: SERVICES_SUPPORT_BUTTON_STYLE_KEY,
|
||||
supportButtonStyleClass: resolveLocalizedValue(
|
||||
translate,
|
||||
SERVICES_SUPPORT_BUTTON_STYLE_KEY,
|
||||
'block text-center bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 transition-colors',
|
||||
),
|
||||
supportItems: services
|
||||
.filter((item) => item.type === 'support')
|
||||
.map((item, index) => ({
|
||||
|
|
@ -132,6 +225,18 @@ function buildServicesContent(
|
|||
titleKey:
|
||||
(isLikelyLocalizationKey(item.title) ? item.title : undefined) ||
|
||||
`Public.services.dynamic.support.${index + 1}.title`,
|
||||
cardStyleClassKey: `Public.services.dynamic.support.${index + 1}.cardStyleClass`,
|
||||
cardStyleClass: resolveLocalizedValue(
|
||||
translate,
|
||||
`Public.services.dynamic.support.${index + 1}.cardStyleClass`,
|
||||
'bg-white rounded-xl shadow-lg p-8 border border-gray-200',
|
||||
),
|
||||
titleStyleClassKey: `Public.services.dynamic.support.${index + 1}.titleStyleClass`,
|
||||
titleStyleClass: resolveLocalizedValue(
|
||||
translate,
|
||||
`Public.services.dynamic.support.${index + 1}.titleStyleClass`,
|
||||
'text-xl font-bold mb-4',
|
||||
),
|
||||
features: (item.features ?? []).map((feature, featureIndex) => ({
|
||||
key:
|
||||
(isLikelyLocalizationKey(feature) ? feature : undefined) ||
|
||||
|
|
@ -143,16 +248,39 @@ function buildServicesContent(
|
|||
supportButtonLabelKey: SERVICES_SUPPORT_BUTTON_KEY,
|
||||
ctaTitle: resolveLocalizedValue(translate, SERVICES_CTA_TITLE_KEY),
|
||||
ctaTitleKey: SERVICES_CTA_TITLE_KEY,
|
||||
ctaSectionStyleClassKey: SERVICES_CTA_SECTION_STYLE_KEY,
|
||||
ctaSectionStyleClass: resolveLocalizedValue(
|
||||
translate,
|
||||
SERVICES_CTA_SECTION_STYLE_KEY,
|
||||
'bg-blue-900 text-white py-16',
|
||||
),
|
||||
ctaTitleStyleClassKey: SERVICES_CTA_TITLE_STYLE_KEY,
|
||||
ctaTitleStyleClass: resolveLocalizedValue(
|
||||
translate,
|
||||
SERVICES_CTA_TITLE_STYLE_KEY,
|
||||
'text-3xl font-bold mb-6 text-white',
|
||||
),
|
||||
ctaDescription: resolveLocalizedValue(translate, SERVICES_CTA_DESCRIPTION_KEY),
|
||||
ctaDescriptionKey: SERVICES_CTA_DESCRIPTION_KEY,
|
||||
ctaDescriptionStyleClassKey: SERVICES_CTA_DESCRIPTION_STYLE_KEY,
|
||||
ctaDescriptionStyleClass: resolveLocalizedValue(
|
||||
translate,
|
||||
SERVICES_CTA_DESCRIPTION_STYLE_KEY,
|
||||
'text-xl mb-8 max-w-2xl mx-auto',
|
||||
),
|
||||
ctaButtonLabel: resolveLocalizedValue(translate, SERVICES_CTA_BUTTON_KEY),
|
||||
ctaButtonLabelKey: SERVICES_CTA_BUTTON_KEY,
|
||||
ctaButtonStyleClassKey: SERVICES_CTA_BUTTON_STYLE_KEY,
|
||||
ctaButtonStyleClass: resolveLocalizedValue(
|
||||
translate,
|
||||
SERVICES_CTA_BUTTON_STYLE_KEY,
|
||||
'bg-white text-blue-900 px-8 py-3 rounded-lg font-semibold hover:bg-blue-50 transition-colors',
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
const Services: React.FC = () => {
|
||||
const { translate } = useLocalization()
|
||||
const navigate = useNavigate()
|
||||
const { setLang } = useStoreActions((actions) => actions.locale)
|
||||
const { getConfig } = useStoreActions((actions) => actions.abpConfig)
|
||||
const configCultureName = useStoreState(
|
||||
|
|
@ -249,11 +377,20 @@ const Services: React.FC = () => {
|
|||
fieldKey === 'heroTitle' ||
|
||||
fieldKey === 'heroSubtitle' ||
|
||||
fieldKey === 'heroImage' ||
|
||||
fieldKey === 'heroSectionStyleClass' ||
|
||||
fieldKey === 'heroTitleStyleClass' ||
|
||||
fieldKey === 'heroSubtitleStyleClass' ||
|
||||
fieldKey === 'supportTitle' ||
|
||||
fieldKey === 'supportTitleStyleClass' ||
|
||||
fieldKey === 'supportButtonLabel' ||
|
||||
fieldKey === 'supportButtonStyleClass' ||
|
||||
fieldKey === 'ctaTitle' ||
|
||||
fieldKey === 'ctaSectionStyleClass' ||
|
||||
fieldKey === 'ctaTitleStyleClass' ||
|
||||
fieldKey === 'ctaDescription' ||
|
||||
fieldKey === 'ctaDescriptionStyleClass' ||
|
||||
fieldKey === 'ctaButtonLabel'
|
||||
|| fieldKey === 'ctaButtonStyleClass'
|
||||
) {
|
||||
return {
|
||||
...current,
|
||||
|
|
@ -360,18 +497,36 @@ const Services: React.FC = () => {
|
|||
type: 'text',
|
||||
value: content.heroTitle,
|
||||
},
|
||||
{
|
||||
key: 'heroTitleStyleClass',
|
||||
label: content.heroTitleStyleClassKey,
|
||||
type: 'text',
|
||||
value: content.heroTitleStyleClass,
|
||||
},
|
||||
{
|
||||
key: 'heroSubtitle',
|
||||
label: content.heroSubtitleKey,
|
||||
type: 'textarea',
|
||||
value: content.heroSubtitle,
|
||||
},
|
||||
{
|
||||
key: 'heroSubtitleStyleClass',
|
||||
label: content.heroSubtitleStyleClassKey,
|
||||
type: 'text',
|
||||
value: content.heroSubtitleStyleClass,
|
||||
},
|
||||
{
|
||||
key: 'heroImage',
|
||||
label: content.heroImageKey,
|
||||
type: 'image',
|
||||
value: content.heroImage,
|
||||
},
|
||||
{
|
||||
key: 'heroSectionStyleClass',
|
||||
label: content.heroSectionStyleClassKey,
|
||||
type: 'text',
|
||||
value: content.heroSectionStyleClass,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
|
@ -388,12 +543,24 @@ const Services: React.FC = () => {
|
|||
type: 'text',
|
||||
value: content.supportTitle,
|
||||
},
|
||||
{
|
||||
key: 'supportTitleStyleClass',
|
||||
label: content.supportTitleStyleClassKey,
|
||||
type: 'text',
|
||||
value: content.supportTitleStyleClass,
|
||||
},
|
||||
{
|
||||
key: 'supportButtonLabel',
|
||||
label: content.supportButtonLabelKey,
|
||||
type: 'text',
|
||||
value: content.supportButtonLabel,
|
||||
},
|
||||
{
|
||||
key: 'supportButtonStyleClass',
|
||||
label: content.supportButtonStyleClassKey,
|
||||
type: 'text',
|
||||
value: content.supportButtonStyleClass,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
|
@ -410,18 +577,42 @@ const Services: React.FC = () => {
|
|||
type: 'text',
|
||||
value: content.ctaTitle,
|
||||
},
|
||||
{
|
||||
key: 'ctaTitleStyleClass',
|
||||
label: content.ctaTitleStyleClassKey,
|
||||
type: 'text',
|
||||
value: content.ctaTitleStyleClass,
|
||||
},
|
||||
{
|
||||
key: 'ctaDescription',
|
||||
label: content.ctaDescriptionKey,
|
||||
type: 'textarea',
|
||||
value: content.ctaDescription,
|
||||
},
|
||||
{
|
||||
key: 'ctaDescriptionStyleClass',
|
||||
label: content.ctaDescriptionStyleClassKey,
|
||||
type: 'text',
|
||||
value: content.ctaDescriptionStyleClass,
|
||||
},
|
||||
{
|
||||
key: 'ctaButtonLabel',
|
||||
label: content.ctaButtonLabelKey,
|
||||
type: 'text',
|
||||
value: content.ctaButtonLabel,
|
||||
},
|
||||
{
|
||||
key: 'ctaButtonStyleClass',
|
||||
label: content.ctaButtonStyleClassKey,
|
||||
type: 'text',
|
||||
value: content.ctaButtonStyleClass,
|
||||
},
|
||||
{
|
||||
key: 'ctaSectionStyleClass',
|
||||
label: content.ctaSectionStyleClassKey,
|
||||
type: 'text',
|
||||
value: content.ctaSectionStyleClass,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
|
@ -458,6 +649,30 @@ const Services: React.FC = () => {
|
|||
type: 'textarea',
|
||||
value: item.description,
|
||||
},
|
||||
{
|
||||
key: 'cardStyleClass',
|
||||
label: item.cardStyleClassKey,
|
||||
type: 'text',
|
||||
value: item.cardStyleClass,
|
||||
},
|
||||
{
|
||||
key: 'titleStyleClass',
|
||||
label: item.titleStyleClassKey,
|
||||
type: 'text',
|
||||
value: item.titleStyleClass,
|
||||
},
|
||||
{
|
||||
key: 'descriptionStyleClass',
|
||||
label: item.descriptionStyleClassKey,
|
||||
type: 'text',
|
||||
value: item.descriptionStyleClass,
|
||||
},
|
||||
{
|
||||
key: 'featureStyleClass',
|
||||
label: item.featureStyleClassKey,
|
||||
type: 'text',
|
||||
value: item.featureStyleClass,
|
||||
},
|
||||
...item.features.map((feature, featureIndex) => ({
|
||||
key: `feature-${featureIndex}`,
|
||||
label:
|
||||
|
|
@ -495,6 +710,18 @@ const Services: React.FC = () => {
|
|||
type: 'text',
|
||||
value: item.title,
|
||||
},
|
||||
{
|
||||
key: 'cardStyleClass',
|
||||
label: item.cardStyleClassKey,
|
||||
type: 'text',
|
||||
value: item.cardStyleClass,
|
||||
},
|
||||
{
|
||||
key: 'titleStyleClass',
|
||||
label: item.titleStyleClassKey,
|
||||
type: 'text',
|
||||
value: item.titleStyleClass,
|
||||
},
|
||||
...item.features.map((feature, featureIndex) => ({
|
||||
key: `feature-${featureIndex}`,
|
||||
label:
|
||||
|
|
@ -560,6 +787,72 @@ const Services: React.FC = () => {
|
|||
value: feature.value,
|
||||
})),
|
||||
})),
|
||||
styleTexts: [
|
||||
{
|
||||
key: content.heroSectionStyleClassKey,
|
||||
value: content.heroSectionStyleClass,
|
||||
},
|
||||
{
|
||||
key: content.heroTitleStyleClassKey,
|
||||
value: content.heroTitleStyleClass,
|
||||
},
|
||||
{
|
||||
key: content.heroSubtitleStyleClassKey,
|
||||
value: content.heroSubtitleStyleClass,
|
||||
},
|
||||
{
|
||||
key: content.supportTitleStyleClassKey,
|
||||
value: content.supportTitleStyleClass,
|
||||
},
|
||||
{
|
||||
key: content.supportButtonStyleClassKey,
|
||||
value: content.supportButtonStyleClass,
|
||||
},
|
||||
{
|
||||
key: content.ctaSectionStyleClassKey,
|
||||
value: content.ctaSectionStyleClass,
|
||||
},
|
||||
{
|
||||
key: content.ctaTitleStyleClassKey,
|
||||
value: content.ctaTitleStyleClass,
|
||||
},
|
||||
{
|
||||
key: content.ctaDescriptionStyleClassKey,
|
||||
value: content.ctaDescriptionStyleClass,
|
||||
},
|
||||
{
|
||||
key: content.ctaButtonStyleClassKey,
|
||||
value: content.ctaButtonStyleClass,
|
||||
},
|
||||
...content.serviceItems.flatMap((item) => [
|
||||
{
|
||||
key: item.cardStyleClassKey,
|
||||
value: item.cardStyleClass,
|
||||
},
|
||||
{
|
||||
key: item.titleStyleClassKey,
|
||||
value: item.titleStyleClass,
|
||||
},
|
||||
{
|
||||
key: item.descriptionStyleClassKey,
|
||||
value: item.descriptionStyleClass,
|
||||
},
|
||||
{
|
||||
key: item.featureStyleClassKey,
|
||||
value: item.featureStyleClass,
|
||||
},
|
||||
]),
|
||||
...content.supportItems.flatMap((item) => [
|
||||
{
|
||||
key: item.cardStyleClassKey,
|
||||
value: item.cardStyleClass,
|
||||
},
|
||||
{
|
||||
key: item.titleStyleClassKey,
|
||||
value: item.titleStyleClass,
|
||||
},
|
||||
]),
|
||||
],
|
||||
})
|
||||
|
||||
await getConfig(false)
|
||||
|
|
@ -628,7 +921,7 @@ const Services: React.FC = () => {
|
|||
isDesignMode={isDesignMode}
|
||||
onSelect={handleSelectBlock}
|
||||
>
|
||||
<div className="relative bg-blue-900 text-white py-12">
|
||||
<div className={content?.heroSectionStyleClass || 'relative bg-blue-900 text-white py-12'}>
|
||||
<div
|
||||
className="absolute inset-0 opacity-20"
|
||||
style={{
|
||||
|
|
@ -638,8 +931,8 @@ const Services: React.FC = () => {
|
|||
}}
|
||||
></div>
|
||||
<div className="container mx-auto pt-20 relative">
|
||||
<h1 className="text-5xl font-bold ml-4 mt-3 mb-2 text-white">{content?.heroTitle}</h1>
|
||||
<p className="text-xl max-w-3xl ml-4">{content?.heroSubtitle}</p>
|
||||
<h1 className={content?.heroTitleStyleClass || 'text-5xl font-bold ml-4 mt-3 mb-2 text-white'}>{content?.heroTitle}</h1>
|
||||
<p className={content?.heroSubtitleStyleClass || 'text-xl max-w-3xl ml-4'}>{content?.heroSubtitle}</p>
|
||||
</div>
|
||||
</div>
|
||||
</SelectableBlock>
|
||||
|
|
@ -658,17 +951,17 @@ const Services: React.FC = () => {
|
|||
isDesignMode={isDesignMode}
|
||||
onSelect={handleSelectBlock}
|
||||
>
|
||||
<div className="bg-white rounded-xl shadow-lg p-8 hover:shadow-xl transition-shadow">
|
||||
<div className={service.cardStyleClass || 'bg-white rounded-xl shadow-lg p-8 hover:shadow-xl transition-shadow'}>
|
||||
<div className="mb-6">
|
||||
{IconComponent && (
|
||||
<IconComponent className={`w-12 h-12 ${getIconColor(index)}`} />
|
||||
)}
|
||||
</div>
|
||||
<h3 className="text-2xl font-bold text-gray-900 mb-4">{service.title}</h3>
|
||||
<p className="text-gray-600 mb-6">{service.description}</p>
|
||||
<h3 className={service.titleStyleClass || 'text-2xl font-bold text-gray-900 mb-4'}>{service.title}</h3>
|
||||
<p className={service.descriptionStyleClass || 'text-gray-600 mb-6'}>{service.description}</p>
|
||||
<ul className="space-y-2">
|
||||
{(service.features ?? []).map((feature, fIndex) => (
|
||||
<li key={fIndex} className="flex items-center text-gray-700">
|
||||
<li key={fIndex} className={service.featureStyleClass || 'flex items-center text-gray-700'}>
|
||||
<span className="w-2 h-2 bg-blue-600 rounded-full mr-2"></span>
|
||||
{feature.value}
|
||||
</li>
|
||||
|
|
@ -691,7 +984,7 @@ const Services: React.FC = () => {
|
|||
isDesignMode={isDesignMode}
|
||||
onSelect={handleSelectBlock}
|
||||
>
|
||||
<h2 className="text-3xl font-bold text-center mb-10">{content?.supportTitle}</h2>
|
||||
<h2 className={content?.supportTitleStyleClass || 'text-3xl font-bold text-center mb-10'}>{content?.supportTitle}</h2>
|
||||
</SelectableBlock>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
||||
{content?.supportItems.map((plan, index) => {
|
||||
|
|
@ -705,13 +998,13 @@ const Services: React.FC = () => {
|
|||
isDesignMode={isDesignMode}
|
||||
onSelect={handleSelectBlock}
|
||||
>
|
||||
<div className="bg-white rounded-xl shadow-lg p-8 border border-gray-200">
|
||||
<div className={plan.cardStyleClass || 'bg-white rounded-xl shadow-lg p-8 border border-gray-200'}>
|
||||
<div className="mb-6">
|
||||
{IconComponent && (
|
||||
<IconComponent className={`w-12 h-12 ${getIconColor(index)}`} />
|
||||
)}
|
||||
</div>
|
||||
<h3 className="text-xl font-bold mb-4">{plan.title}</h3>
|
||||
<h3 className={plan.titleStyleClass || 'text-xl font-bold mb-4'}>{plan.title}</h3>
|
||||
<ul className="space-y-3 mb-8">
|
||||
{(plan.features ?? []).map((feature, fIndex) => (
|
||||
<li key={fIndex} className="flex items-center space-x-2 text-gray-700">
|
||||
|
|
@ -722,7 +1015,7 @@ const Services: React.FC = () => {
|
|||
</ul>
|
||||
<Link
|
||||
to={ROUTES_ENUM.public.contact}
|
||||
className="block text-center bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 transition-colors"
|
||||
className={content?.supportButtonStyleClass || 'block text-center bg-blue-600 text-white px-6 py-3 rounded-lg hover:bg-blue-700 transition-colors'}
|
||||
>
|
||||
{content?.supportButtonLabel}
|
||||
</Link>
|
||||
|
|
@ -741,13 +1034,13 @@ const Services: React.FC = () => {
|
|||
isDesignMode={isDesignMode}
|
||||
onSelect={handleSelectBlock}
|
||||
>
|
||||
<div className="bg-blue-900 text-white py-16">
|
||||
<div className={content?.ctaSectionStyleClass || 'bg-blue-900 text-white py-16'}>
|
||||
<div className="container mx-auto px-4 text-center">
|
||||
<h2 className="text-3xl font-bold mb-6 text-white">{content?.ctaTitle}</h2>
|
||||
<p className="text-xl mb-8 max-w-2xl mx-auto">{content?.ctaDescription}</p>
|
||||
<h2 className={content?.ctaTitleStyleClass || 'text-3xl font-bold mb-6 text-white'}>{content?.ctaTitle}</h2>
|
||||
<p className={content?.ctaDescriptionStyleClass || 'text-xl mb-8 max-w-2xl mx-auto'}>{content?.ctaDescription}</p>
|
||||
<Link
|
||||
to={ROUTES_ENUM.public.contact}
|
||||
className="bg-white text-blue-900 px-8 py-3 rounded-lg font-semibold hover:bg-blue-50 transition-colors"
|
||||
className={content?.ctaButtonStyleClass || 'bg-white text-blue-900 px-8 py-3 rounded-lg font-semibold hover:bg-blue-50 transition-colors'}
|
||||
>
|
||||
{content?.ctaButtonLabel}
|
||||
</Link>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,14 @@ const SAFELIST_COLORS = 'colors'
|
|||
|
||||
export default {
|
||||
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}', './safelist.txt'],
|
||||
safelist: [
|
||||
{
|
||||
// Covers most utility groups used by designer-entered class strings.
|
||||
pattern:
|
||||
/^(container|block|inline-block|inline-flex|hidden|absolute|relative|fixed|sticky|top-|right-|bottom-|left-|z-|w-|h-|min-w-|min-h-|max-w-|max-h-|m-|mx-|my-|mt-|mr-|mb-|ml-|p-|px-|py-|pt-|pr-|pb-|pl-|space-x-|space-y-|gap-|grid|grid-cols-|col-span-|row-span-|flex|flex-col|flex-row|flex-wrap|items-|justify-|self-|content-|overflow-|truncate|rounded|rounded-|border|border-|shadow|shadow-|text-|font-|leading-|tracking-|uppercase|lowercase|capitalize|bg-|from-|via-|to-|opacity-|backdrop-blur|transition|duration-|ease-|transform|scale-|rotate-|translate-|cursor-|select-|pointer-events-|ring-|outline-|whitespace-|break-|object-|aspect-|prose|prose-.*)$/,
|
||||
variants: ['hover', 'focus', 'active', 'dark', 'sm', 'md', 'lg', 'xl', '2xl'],
|
||||
},
|
||||
],
|
||||
darkMode: 'class',
|
||||
theme: {
|
||||
extend: {
|
||||
|
|
|
|||
Loading…
Reference in a new issue