From e40e7f394b4134f2212bbbc31ac3bd23f86f88f9 Mon Sep 17 00:00:00 2001 From: manas kumar Date: Sat, 18 Apr 2026 14:23:13 +0530 Subject: [PATCH 1/3] feat: add ThemeFontControl component for managing homepage fonts - Created ThemeFontControl component to allow users to select and preview fonts. - Added styles for font options in ThemeFontControl.module.scss. - Introduced constants for homepage fonts in homepageFonts.ts, including font properties and stacks. - Implemented font loading utility in fontLoader.ts to ensure selected fonts are loaded dynamically. --- .../Enums/HomepageFontFamily.php | 32 ++++ .../UpdateEventSettingsRequest.php | 3 + .../PartialUpdateOrganizerSettingsRequest.php | 2 + .../Enums/HomepageFontFamilyTest.php | 33 ++++ .../ThemeFontControl.module.scss | 18 +++ .../common/ThemeFontControl/index.tsx | 87 +++++++++++ .../EventHomepage/EventHomepage.module.scss | 4 +- .../layouts/EventHomepage/index.tsx | 7 + .../EventCard/EventCard.module.scss | 4 +- .../OrganizerHomepage.module.scss | 4 +- .../layouts/OrganizerHomepage/index.tsx | 7 + .../routes/event/HomepageDesigner/index.tsx | 23 ++- .../OrganizerHomepageDesigner/index.tsx | 26 +++- frontend/src/constants/homepageFonts.ts | 63 ++++++++ frontend/src/locales/de.js | 2 +- frontend/src/locales/de.po | 147 ++++++++++-------- frontend/src/locales/en.js | 2 +- frontend/src/locales/en.po | 147 ++++++++++-------- frontend/src/locales/es.js | 2 +- frontend/src/locales/es.po | 147 ++++++++++-------- frontend/src/locales/fr.js | 2 +- frontend/src/locales/fr.po | 147 ++++++++++-------- frontend/src/locales/hu.js | 2 +- frontend/src/locales/hu.po | 147 ++++++++++-------- frontend/src/locales/it.js | 2 +- frontend/src/locales/it.po | 147 ++++++++++-------- frontend/src/locales/nl.js | 2 +- frontend/src/locales/nl.po | 147 ++++++++++-------- frontend/src/locales/pl.js | 2 +- frontend/src/locales/pl.po | 147 ++++++++++-------- frontend/src/locales/pt-br.js | 2 +- frontend/src/locales/pt-br.po | 147 ++++++++++-------- frontend/src/locales/pt.js | 2 +- frontend/src/locales/pt.po | 147 ++++++++++-------- frontend/src/locales/ru.js | 2 +- frontend/src/locales/ru.po | 147 ++++++++++-------- frontend/src/locales/se.js | 2 +- frontend/src/locales/se.po | 147 ++++++++++-------- frontend/src/locales/tr.js | 2 +- frontend/src/locales/tr.po | 147 ++++++++++-------- frontend/src/locales/vi.js | 2 +- frontend/src/locales/vi.po | 147 ++++++++++-------- frontend/src/locales/zh-cn.js | 2 +- frontend/src/locales/zh-cn.po | 147 ++++++++++-------- frontend/src/locales/zh-hk.js | 2 +- frontend/src/locales/zh-hk.po | 147 ++++++++++-------- frontend/src/types.ts | 1 + frontend/src/utilites/fontLoader.ts | 27 ++++ frontend/src/utilites/themeUtils.ts | 5 + 49 files changed, 1697 insertions(+), 1033 deletions(-) create mode 100644 backend/app/DomainObjects/Enums/HomepageFontFamily.php create mode 100644 backend/tests/Unit/DomainObjects/Enums/HomepageFontFamilyTest.php create mode 100644 frontend/src/components/common/ThemeFontControl/ThemeFontControl.module.scss create mode 100644 frontend/src/components/common/ThemeFontControl/index.tsx create mode 100644 frontend/src/constants/homepageFonts.ts create mode 100644 frontend/src/utilites/fontLoader.ts diff --git a/backend/app/DomainObjects/Enums/HomepageFontFamily.php b/backend/app/DomainObjects/Enums/HomepageFontFamily.php new file mode 100644 index 0000000000..8c565d6ef2 --- /dev/null +++ b/backend/app/DomainObjects/Enums/HomepageFontFamily.php @@ -0,0 +1,32 @@ + ['nullable', 'string', ...RulesHelper::HEX_COLOR], 'homepage_theme_settings.mode' => ['nullable', 'string', Rule::in(['light', 'dark'])], 'homepage_theme_settings.background_type' => ['nullable', 'string', Rule::in(HomepageBackgroundType::valuesArray())], + 'homepage_theme_settings.font_family' => ['nullable', 'string', Rule::in(HomepageFontFamily::valuesArray())], // Self-service settings 'allow_attendee_self_edit' => ['boolean'], @@ -147,6 +149,7 @@ public function messages(): array 'homepage_theme_settings.background' => $colorMessage, 'homepage_theme_settings.mode.in' => __('The mode must be light or dark.'), 'homepage_theme_settings.background_type.in' => __('The background type must be COLOR or MIRROR_COVER_IMAGE.'), + 'homepage_theme_settings.font_family.in' => __('The selected font is not supported.'), ]; } } diff --git a/backend/app/Http/Request/Organizer/Settings/PartialUpdateOrganizerSettingsRequest.php b/backend/app/Http/Request/Organizer/Settings/PartialUpdateOrganizerSettingsRequest.php index 4466966af2..0ea98d0a64 100644 --- a/backend/app/Http/Request/Organizer/Settings/PartialUpdateOrganizerSettingsRequest.php +++ b/backend/app/Http/Request/Organizer/Settings/PartialUpdateOrganizerSettingsRequest.php @@ -4,6 +4,7 @@ use HiEvents\DomainObjects\Enums\AttendeeDetailsCollectionMethod; use HiEvents\DomainObjects\Enums\HomepageBackgroundType; +use HiEvents\DomainObjects\Enums\HomepageFontFamily; use HiEvents\DomainObjects\Enums\OrganizerHomepageVisibility; use HiEvents\Http\Request\BaseRequest; use HiEvents\Validators\Rules\RulesHelper; @@ -64,6 +65,7 @@ public static function rules(): array 'homepage_theme_settings.background' => ['nullable', 'string', ...RulesHelper::HEX_COLOR], 'homepage_theme_settings.mode' => ['nullable', 'string', Rule::in(['light', 'dark'])], 'homepage_theme_settings.background_type' => ['nullable', 'string', Rule::in(HomepageBackgroundType::valuesArray())], + 'homepage_theme_settings.font_family' => ['nullable', 'string', Rule::in(HomepageFontFamily::valuesArray())], // SEO 'seo_keywords' => ['sometimes', 'nullable', 'string', 'max:255'], diff --git a/backend/tests/Unit/DomainObjects/Enums/HomepageFontFamilyTest.php b/backend/tests/Unit/DomainObjects/Enums/HomepageFontFamilyTest.php new file mode 100644 index 0000000000..37ab229d68 --- /dev/null +++ b/backend/tests/Unit/DomainObjects/Enums/HomepageFontFamilyTest.php @@ -0,0 +1,33 @@ +assertContains('Outfit', $values); + $this->assertContains('Inter', $values); + $this->assertContains('Plus Jakarta Sans', $values); + $this->assertContains('Playfair Display', $values); + $this->assertContains('Bebas Neue', $values); + } + + public function test_values_are_unique_non_empty_strings(): void + { + $values = HomepageFontFamily::valuesArray(); + + $this->assertNotEmpty($values); + $this->assertSame($values, array_values(array_unique($values))); + + foreach ($values as $value) { + $this->assertIsString($value); + $this->assertNotSame('', trim($value)); + } + } +} diff --git a/frontend/src/components/common/ThemeFontControl/ThemeFontControl.module.scss b/frontend/src/components/common/ThemeFontControl/ThemeFontControl.module.scss new file mode 100644 index 0000000000..b6b6dfda58 --- /dev/null +++ b/frontend/src/components/common/ThemeFontControl/ThemeFontControl.module.scss @@ -0,0 +1,18 @@ +.option { + display: flex; + align-items: baseline; + justify-content: space-between; + width: 100%; + gap: 12px; +} + +.optionLabel { + font-size: 14px; + font-weight: 500; +} + +.optionSample { + font-size: 13px; + color: var(--mantine-color-dimmed); + letter-spacing: 0.5px; +} diff --git a/frontend/src/components/common/ThemeFontControl/index.tsx b/frontend/src/components/common/ThemeFontControl/index.tsx new file mode 100644 index 0000000000..8fd4c2eee6 --- /dev/null +++ b/frontend/src/components/common/ThemeFontControl/index.tsx @@ -0,0 +1,87 @@ +import {Select, Text} from "@mantine/core"; +import {t} from "@lingui/macro"; +import {useEffect, useMemo} from "react"; +import { + buildHomepageFontStack, + buildHomepageFontUrl, + DEFAULT_HOMEPAGE_FONT, + HOMEPAGE_FONTS, +} from "../../../constants/homepageFonts.ts"; +import {ensureHomepageFontLoaded} from "../../../utilites/fontLoader.ts"; +import classes from "./ThemeFontControl.module.scss"; + +interface ThemeFontControlProps { + value: string | null | undefined; + onChange: (fontFamily: string) => void; + disabled?: boolean; +} + +export const ThemeFontControl = ({value, onChange, disabled = false}: ThemeFontControlProps) => { + const selected = value || DEFAULT_HOMEPAGE_FONT; + + const data = useMemo( + () => HOMEPAGE_FONTS.map(font => ({value: font.value, label: font.label})), + [], + ); + + useEffect(() => { + HOMEPAGE_FONTS.forEach(font => { + if (font.value === DEFAULT_HOMEPAGE_FONT || typeof document === 'undefined') { + return; + } + const id = `hi-font-preview-${font.bunnyFamily}`; + if (document.getElementById(id)) { + return; + } + const link = document.createElement('link'); + link.id = id; + link.rel = 'stylesheet'; + link.href = buildHomepageFontUrl(font.value); + document.head.appendChild(link); + }); + }, []); + + useEffect(() => { + ensureHomepageFontLoaded(selected); + }, [selected]); + + const handleChange = (next: string | null) => { + if (!next) { + return; + } + onChange(next); + }; + + const renderOption = ({option}: {option: {value: string; label: string}}) => ( +
+ {option.label} + Aa 123 +
+ ); + + return ( +
+