diff --git a/app/HackathonsPage.php b/app/HackathonsPage.php index fa732ce1a..60f511146 100644 --- a/app/HackathonsPage.php +++ b/app/HackathonsPage.php @@ -21,6 +21,8 @@ class HackathonsPage extends Model 'details_paragraph_3', 'details_paragraph_4', 'video_url', + 'extra_button_text', + 'extra_button_link', 'recap_button_text', 'recap_button_link', 'toolkit_button_text', diff --git a/app/Nova/DreamJobsPage.php b/app/Nova/DreamJobsPage.php index 7b56d4db6..25b7a4c6a 100644 --- a/app/Nova/DreamJobsPage.php +++ b/app/Nova/DreamJobsPage.php @@ -4,6 +4,7 @@ use Illuminate\Http\Request; use Laravel\Nova\Fields\Boolean; +use Laravel\Nova\Fields\Code; use Laravel\Nova\Fields\HasMany; use Laravel\Nova\Fields\ID; use Laravel\Nova\Fields\Text; @@ -39,70 +40,8 @@ public static function indexQuery(NovaRequest $request, $query) return $query->where('id', 1); } - private static function localesSorted(): array - { - $locales = config('app.locales', ['en']); - if (is_string($locales)) { - $locales = array_map('trim', explode(',', $locales)); - } - $locales = array_values(array_filter($locales)); - if (empty($locales)) { - $locales = ['en']; - } - sort($locales); - - return $locales; - } - public function fields(Request $request): array { - $translationKeys = [ - 'hero_intro' => 'Hero intro', - 'hero_cta_text' => 'Hero button text', - 'about_title' => 'About title', - 'about_description' => 'About description', - 'role_models_title' => 'Role models section title', - 'resources_title' => 'Resources section title', - ]; - - $translationFields = []; - foreach (self::localesSorted() as $locale) { - if ($locale === 'en') { - continue; - } - foreach ($translationKeys as $key => $label) { - $isLongText = in_array($key, ['hero_intro', 'about_description'], true); - $fieldName = 'locale_' . $locale . '_' . $key; - if ($isLongText) { - $translationFields[] = Textarea::make($label . ' (' . strtoupper($locale) . ')', $fieldName) - ->nullable() - ->resolveUsing(function () use ($locale, $key) { - $overrides = $this->resource->locale_overrides ?? []; - - return $overrides[$locale][$key] ?? ''; - }) - ->fillUsing(function ($request, $model, $attribute, $requestAttribute) use ($locale, $key) { - $overrides = $model->locale_overrides ?? []; - $overrides[$locale][$key] = $request->get($requestAttribute) ?: null; - $model->locale_overrides = $overrides; - }); - } else { - $translationFields[] = Text::make($label . ' (' . strtoupper($locale) . ')', $fieldName) - ->nullable() - ->resolveUsing(function () use ($locale, $key) { - $overrides = $this->resource->locale_overrides ?? []; - - return $overrides[$locale][$key] ?? ''; - }) - ->fillUsing(function ($request, $model, $attribute, $requestAttribute) use ($locale, $key) { - $overrides = $model->locale_overrides ?? []; - $overrides[$locale][$key] = $request->get($requestAttribute) ?: null; - $model->locale_overrides = $overrides; - }); - } - } - } - $resourcesPanelFields = [ Boolean::make('Use dynamic content for this section', 'resources_dynamic'), Text::make('Resources section title', 'resources_title')->nullable(), @@ -136,12 +75,13 @@ public function fields(Request $request): array Panel::make('Resources section (global for all role model pages)', $resourcesPanelFields) ->collapsable() ->collapsedByDefault(), + Panel::make('Translations (JSON)', [ + Code::make('Locale overrides', 'locale_overrides') + ->json() + ->help('Optional per-locale overrides. Example: {"fr":{"hero_intro":"..."}}'), + ])->collapsable()->collapsedByDefault(), ]; - if (!empty($translationFields)) { - $fields[] = new Panel('Translations', $translationFields); - } - return $fields; } } diff --git a/app/Nova/HackathonsPage.php b/app/Nova/HackathonsPage.php index 172c735c5..1351c1bf0 100644 --- a/app/Nova/HackathonsPage.php +++ b/app/Nova/HackathonsPage.php @@ -4,6 +4,7 @@ use Illuminate\Http\Request; use Laravel\Nova\Fields\Boolean; +use Laravel\Nova\Fields\Code; use Laravel\Nova\Fields\ID; use Laravel\Nova\Fields\Text; use Laravel\Nova\Fields\Trix; @@ -38,86 +39,8 @@ public static function indexQuery(NovaRequest $request, $query) return $query->where('id', 1); } - private static function localesSorted(): array - { - $locales = config('app.locales', ['en']); - if (is_string($locales)) { - $locales = array_map('trim', explode(',', $locales)); - } - $locales = array_values(array_filter($locales)); - if (empty($locales)) { - $locales = ['en']; - } - sort($locales); - - return $locales; - } - public function fields(Request $request): array { - $translationKeys = [ - 'hero_title' => 'Hero title', - 'hero_subtitle' => 'Hero subtitle', - 'intro_title' => 'Intro title', - 'intro_paragraph_1' => 'Intro paragraph 1', - 'intro_paragraph_2' => 'Intro paragraph 2', - 'details_title' => 'Details title', - 'details_paragraph_1' => 'Details paragraph 1', - 'details_paragraph_2' => 'Details paragraph 2', - 'details_paragraph_3' => 'Details paragraph 3', - 'details_paragraph_4' => 'Details paragraph 4', - 'recap_button_text' => 'Recap button text', - 'toolkit_button_text' => 'Toolkit button text', - ]; - - $longTextKeys = [ - 'hero_subtitle', - 'intro_paragraph_1', - 'intro_paragraph_2', - 'details_paragraph_1', - 'details_paragraph_2', - 'details_paragraph_3', - 'details_paragraph_4', - ]; - - $translationFields = []; - foreach (self::localesSorted() as $locale) { - if ($locale === 'en') { - continue; - } - - foreach ($translationKeys as $key => $label) { - $fieldName = 'locale_' . $locale . '_' . $key; - if (in_array($key, $longTextKeys, true)) { - $translationFields[] = Trix::make($label . ' (' . strtoupper($locale) . ')', $fieldName) - ->nullable() - ->resolveUsing(function () use ($locale, $key) { - $overrides = $this->resource->locale_overrides ?? []; - - return $overrides[$locale][$key] ?? ''; - }) - ->fillUsing(function ($request, $model, $attribute, $requestAttribute) use ($locale, $key) { - $overrides = $model->locale_overrides ?? []; - $overrides[$locale][$key] = $request->get($requestAttribute) ?: null; - $model->locale_overrides = $overrides; - }); - } else { - $translationFields[] = Text::make($label . ' (' . strtoupper($locale) . ')', $fieldName) - ->nullable() - ->resolveUsing(function () use ($locale, $key) { - $overrides = $this->resource->locale_overrides ?? []; - - return $overrides[$locale][$key] ?? ''; - }) - ->fillUsing(function ($request, $model, $attribute, $requestAttribute) use ($locale, $key) { - $overrides = $model->locale_overrides ?? []; - $overrides[$locale][$key] = $request->get($requestAttribute) ?: null; - $model->locale_overrides = $overrides; - }); - } - } - } - $fields = [ ID::make()->onlyOnForms(), Boolean::make('Use dynamic content', 'dynamic_content') @@ -141,17 +64,20 @@ public function fields(Request $request): array Trix::make('Details paragraph 3', 'details_paragraph_3')->nullable(), Trix::make('Details paragraph 4', 'details_paragraph_4')->nullable(), Text::make('Video URL (embed)', 'video_url')->nullable(), + Text::make('Extra button text (optional)', 'extra_button_text')->nullable(), + Text::make('Extra button link (optional)', 'extra_button_link')->nullable(), Text::make('Recap button text', 'recap_button_text')->nullable(), Text::make('Recap button link', 'recap_button_link')->nullable(), Text::make('Toolkit button text', 'toolkit_button_text')->nullable(), Text::make('Toolkit button link', 'toolkit_button_link')->nullable(), ])->collapsable()->collapsedByDefault(), + Panel::make('Translations (JSON)', [ + Code::make('Locale overrides', 'locale_overrides') + ->json() + ->help('Optional per-locale overrides. Example: {"fr":{"hero_title":"Hackathons"}}'), + ])->collapsable()->collapsedByDefault(), ]; - if (!empty($translationFields)) { - $fields[] = new Panel('Translations', $translationFields); - } - return $fields; } } diff --git a/database/migrations/2026_02_16_140000_create_hackathons_page_table.php b/database/migrations/2026_02_16_140000_create_hackathons_page_table.php index eeb8f5a93..b9b976bf9 100644 --- a/database/migrations/2026_02_16_140000_create_hackathons_page_table.php +++ b/database/migrations/2026_02_16_140000_create_hackathons_page_table.php @@ -23,6 +23,8 @@ public function up(): void $table->text('details_paragraph_3')->nullable(); $table->text('details_paragraph_4')->nullable(); $table->string('video_url')->nullable(); + $table->string('extra_button_text')->nullable(); + $table->string('extra_button_link')->nullable(); $table->string('recap_button_text')->nullable(); $table->string('recap_button_link')->nullable(); $table->string('toolkit_button_text')->nullable(); diff --git a/database/migrations/2026_02_16_141000_add_extra_button_to_hackathons_page_table.php b/database/migrations/2026_02_16_141000_add_extra_button_to_hackathons_page_table.php new file mode 100644 index 000000000..0db51f8d3 --- /dev/null +++ b/database/migrations/2026_02_16_141000_add_extra_button_to_hackathons_page_table.php @@ -0,0 +1,37 @@ +string('extra_button_text')->nullable()->after('video_url'); + }); + } + + if (Schema::hasTable('hackathons_page') && !Schema::hasColumn('hackathons_page', 'extra_button_link')) { + Schema::table('hackathons_page', function (Blueprint $table) { + $table->string('extra_button_link')->nullable()->after('extra_button_text'); + }); + } + } + + public function down(): void + { + if (Schema::hasTable('hackathons_page') && Schema::hasColumn('hackathons_page', 'extra_button_link')) { + Schema::table('hackathons_page', function (Blueprint $table) { + $table->dropColumn('extra_button_link'); + }); + } + + if (Schema::hasTable('hackathons_page') && Schema::hasColumn('hackathons_page', 'extra_button_text')) { + Schema::table('hackathons_page', function (Blueprint $table) { + $table->dropColumn('extra_button_text'); + }); + } + } +}; diff --git a/resources/views/hackathons/index.blade.php b/resources/views/hackathons/index.blade.php index 41a908e33..68888f48b 100644 --- a/resources/views/hackathons/index.blade.php +++ b/resources/views/hackathons/index.blade.php @@ -150,6 +150,14 @@ class="animation-element move-background duration-[1.5s] absolute z-0 lg:-bottom @endif

+ @if($dynamic && $page && $page->extra_button_link && $page->contentForLocale('extra_button_text')) + + {{ $page->contentForLocale('extra_button_text') }} + + @endif