diff --git a/app/DreamJobRoleModel.php b/app/DreamJobRoleModel.php new file mode 100644 index 000000000..7db8ceba4 --- /dev/null +++ b/app/DreamJobRoleModel.php @@ -0,0 +1,29 @@ + 'integer', + 'active' => 'boolean', + ]; +} diff --git a/app/DreamJobsPage.php b/app/DreamJobsPage.php new file mode 100644 index 000000000..adc621cbf --- /dev/null +++ b/app/DreamJobsPage.php @@ -0,0 +1,85 @@ + 'boolean', + 'about_dynamic' => 'boolean', + 'role_models_dynamic' => 'boolean', + 'resources_dynamic' => 'boolean', + 'locale_overrides' => 'array', + ]; + + public function resources() + { + return $this->hasMany(DreamJobsResource::class, 'page_id')->orderBy('position'); + } + + public static function config(): self + { + $page = self::first(); + if ($page) { + return $page; + } + + return self::create([ + 'hero_dynamic' => false, + 'about_dynamic' => false, + 'role_models_dynamic' => false, + 'resources_dynamic' => false, + 'hero_cta_link' => '#dream-job-resources', + 'about_video_url' => 'https://www.youtube.com/embed/pzP-kToeym4?si=FzutCQGW4rO5M_5A', + 'locale_overrides' => null, + ]); + } + + public function contentForLocale(string $key, ?string $locale = null): string + { + $locale = $locale ?? app()->getLocale(); + $overrides = $this->locale_overrides ?? []; + if (!empty($overrides[$locale][$key])) { + return (string) $overrides[$locale][$key]; + } + + $value = $this->getAttribute($key); + return $value !== null ? (string) $value : ''; + } + + public function resourcesForLocale(?string $locale = null): array + { + return $this->resources() + ->where('active', true) + ->orderBy('position') + ->get() + ->map(fn (DreamJobsResource $item) => [ + 'title' => $item->titleForLocale($locale), + 'description' => $item->descriptionForLocale($locale), + 'button_text' => $item->buttonTextForLocale($locale), + 'button_link' => $item->button_link, + 'image' => $item->image, + ]) + ->all(); + } +} diff --git a/app/DreamJobsResource.php b/app/DreamJobsResource.php new file mode 100644 index 000000000..04add80b6 --- /dev/null +++ b/app/DreamJobsResource.php @@ -0,0 +1,66 @@ + 'integer', + 'active' => 'boolean', + 'locale_overrides' => 'array', + ]; + + public function page() + { + return $this->belongsTo(DreamJobsPage::class, 'page_id'); + } + + public function titleForLocale(?string $locale = null): string + { + $locale = $locale ?? app()->getLocale(); + $overrides = $this->locale_overrides ?? []; + if (!empty($overrides[$locale]['title'])) { + return (string) $overrides[$locale]['title']; + } + + return (string) ($this->title ?? ''); + } + + public function descriptionForLocale(?string $locale = null): string + { + $locale = $locale ?? app()->getLocale(); + $overrides = $this->locale_overrides ?? []; + if (!empty($overrides[$locale]['description'])) { + return (string) $overrides[$locale]['description']; + } + + return (string) ($this->description ?? ''); + } + + public function buttonTextForLocale(?string $locale = null): string + { + $locale = $locale ?? app()->getLocale(); + $overrides = $this->locale_overrides ?? []; + if (!empty($overrides[$locale]['button_text'])) { + return (string) $overrides[$locale]['button_text']; + } + + return (string) ($this->button_text ?? ''); + } +} diff --git a/app/HackathonsPage.php b/app/HackathonsPage.php new file mode 100644 index 000000000..fa732ce1a --- /dev/null +++ b/app/HackathonsPage.php @@ -0,0 +1,60 @@ + 'boolean', + 'locale_overrides' => 'array', + ]; + + public static function config(): self + { + $page = self::first(); + if ($page) { + return $page; + } + + return self::create([ + 'dynamic_content' => false, + 'locale_overrides' => null, + ]); + } + + public function contentForLocale(string $key, ?string $locale = null): string + { + $locale = $locale ?? app()->getLocale(); + $overrides = $this->locale_overrides ?? []; + if (!empty($overrides[$locale][$key])) { + return (string) $overrides[$locale][$key]; + } + + $value = $this->getAttribute($key); + return $value !== null ? (string) $value : ''; + } +} diff --git a/app/Nova/DreamJobRoleModel.php b/app/Nova/DreamJobRoleModel.php new file mode 100644 index 000000000..063f7201c --- /dev/null +++ b/app/Nova/DreamJobRoleModel.php @@ -0,0 +1,80 @@ +sortable(), + + Text::make('First Name', 'first_name')->rules('required', 'max:255'), + Text::make('Last Name', 'last_name')->rules('required', 'max:255'), + Text::make('Slug', 'slug') + ->rules('required', 'max:255', 'unique:dream_job_role_models,slug,{{resourceId}}') + ->help('Used in URL, e.g. anny-tubbs'), + Textarea::make('Role', 'role')->rules('required'), + + Text::make('Image URL', 'image') + ->rules('required', 'max:2048') + ->help('Path from site root, e.g. /images/dream-jobs/anny-tubbs.png'), + Text::make('Country Code', 'country') + ->rules('required', 'max:8') + ->help('Flag code used by existing assets, e.g. be, fr, gr'), + + Textarea::make('Description 1', 'description1')->nullable(), + Textarea::make('Description 2', 'description2')->nullable(), + + Text::make('Profile Link', 'link')->nullable()->rules('nullable', 'max:2048'), + Text::make('Video Embed URL', 'video')->nullable()->rules('nullable', 'max:2048'), + Text::make('Pathway Map Filename', 'pathway_map_link') + ->nullable() + ->rules('nullable', 'max:255') + ->help('Filename in /public/docs/dream-jobs/, e.g. Career Pathway Map Anny Tubbs.pdf'), + + Number::make('Position', 'position') + ->min(0) + ->default(0) + ->help('Lower numbers appear first.'), + + Boolean::make('Active', 'active')->default(true), + ]; + } + + public static function indexQuery(NovaRequest $request, $query) + { + return $query->orderBy('position')->orderBy('first_name'); + } +} diff --git a/app/Nova/DreamJobsPage.php b/app/Nova/DreamJobsPage.php new file mode 100644 index 000000000..7b56d4db6 --- /dev/null +++ b/app/Nova/DreamJobsPage.php @@ -0,0 +1,147 @@ +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(), + HasMany::make('Resources', 'resources', DreamJobsResource::class), + ]; + + $fields = [ + ID::make()->onlyOnForms(), + + Panel::make('Hero', [ + Boolean::make('Use dynamic content for this section', 'hero_dynamic'), + Textarea::make('Hero intro', 'hero_intro')->nullable(), + Text::make('Hero button text', 'hero_cta_text')->nullable(), + Text::make('Hero button link', 'hero_cta_link')->nullable()->help('Defaults to #dream-job-resources'), + ])->collapsable()->collapsedByDefault(), + + Panel::make('About', [ + Boolean::make('Use dynamic content for this section', 'about_dynamic'), + Text::make('About title', 'about_title')->nullable(), + Textarea::make('About description', 'about_description')->nullable(), + Text::make('About video URL', 'about_video_url') + ->nullable() + ->help('YouTube embed URL used in the About section video player.'), + ])->collapsable()->collapsedByDefault(), + + Panel::make('Role models section', [ + Boolean::make('Use dynamic content for this section', 'role_models_dynamic'), + Text::make('Role models section title', 'role_models_title')->nullable(), + ])->collapsable()->collapsedByDefault(), + + Panel::make('Resources section (global for all role model pages)', $resourcesPanelFields) + ->collapsable() + ->collapsedByDefault(), + ]; + + if (!empty($translationFields)) { + $fields[] = new Panel('Translations', $translationFields); + } + + return $fields; + } +} diff --git a/app/Nova/DreamJobsResource.php b/app/Nova/DreamJobsResource.php new file mode 100644 index 000000000..60528562b --- /dev/null +++ b/app/Nova/DreamJobsResource.php @@ -0,0 +1,120 @@ +nullable() + ->resolveUsing(function () use ($locale) { + $overrides = $this->resource->locale_overrides ?? []; + + return $overrides[$locale]['title'] ?? ''; + }) + ->fillUsing(function ($request, $model, $attribute, $requestAttribute) use ($locale) { + $overrides = $model->locale_overrides ?? []; + $overrides[$locale]['title'] = $request->get($requestAttribute) ?: null; + $model->locale_overrides = $overrides; + }); + + $translationFields[] = Textarea::make('Description (' . strtoupper($locale) . ')', 'locale_' . $locale . '_description') + ->nullable() + ->resolveUsing(function () use ($locale) { + $overrides = $this->resource->locale_overrides ?? []; + + return $overrides[$locale]['description'] ?? ''; + }) + ->fillUsing(function ($request, $model, $attribute, $requestAttribute) use ($locale) { + $overrides = $model->locale_overrides ?? []; + $overrides[$locale]['description'] = $request->get($requestAttribute) ?: null; + $model->locale_overrides = $overrides; + }); + + $translationFields[] = Text::make('Button text (' . strtoupper($locale) . ')', 'locale_' . $locale . '_button_text') + ->nullable() + ->resolveUsing(function () use ($locale) { + $overrides = $this->resource->locale_overrides ?? []; + + return $overrides[$locale]['button_text'] ?? ''; + }) + ->fillUsing(function ($request, $model, $attribute, $requestAttribute) use ($locale) { + $overrides = $model->locale_overrides ?? []; + $overrides[$locale]['button_text'] = $request->get($requestAttribute) ?: null; + $model->locale_overrides = $overrides; + }); + } + + $fields = [ + ID::make()->onlyOnForms(), + Hidden::make('Page', 'page_id') + ->default(function ($request) { + if ($request instanceof \Laravel\Nova\Http\Requests\NovaRequest && method_exists($request, 'viaResourceId')) { + return $request->viaResourceId() ?? 1; + } + + return $request->query('viaResourceId', 1); + }), + Text::make('Title', 'title')->rules('required', 'string', 'max:255'), + Textarea::make('Description', 'description')->nullable(), + Text::make('Button text', 'button_text')->nullable(), + Text::make('Button link', 'button_link')->nullable(), + Text::make('Image URL', 'image')->nullable(), + Number::make('Position', 'position')->min(0)->default(0), + Boolean::make('Active', 'active')->default(true), + ]; + + if (!empty($translationFields)) { + $fields[] = new Panel('Translations', $translationFields); + } + + return $fields; + } + + public static function indexQuery(NovaRequest $request, $query) + { + return $query->orderBy('position')->orderBy('id'); + } +} diff --git a/app/Nova/HackathonsPage.php b/app/Nova/HackathonsPage.php new file mode 100644 index 000000000..172c735c5 --- /dev/null +++ b/app/Nova/HackathonsPage.php @@ -0,0 +1,157 @@ +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') + ->help('Keep OFF to use current static template content. Turn ON to use the values below.'), + + Panel::make('Hero', [ + Text::make('Hero title', 'hero_title')->nullable(), + Trix::make('Hero subtitle', 'hero_subtitle')->nullable(), + ])->collapsable()->collapsedByDefault(), + + Panel::make('Intro section', [ + Text::make('Intro title', 'intro_title')->nullable(), + Trix::make('Intro paragraph 1', 'intro_paragraph_1')->nullable(), + Trix::make('Intro paragraph 2', 'intro_paragraph_2')->nullable(), + ])->collapsable()->collapsedByDefault(), + + Panel::make('Details section', [ + Text::make('Details title', 'details_title')->nullable(), + Trix::make('Details paragraph 1', 'details_paragraph_1')->nullable(), + Trix::make('Details paragraph 2', 'details_paragraph_2')->nullable(), + 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('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(), + ]; + + if (!empty($translationFields)) { + $fields[] = new Panel('Translations', $translationFields); + } + + return $fields; + } +} diff --git a/database/migrations/2026_02_16_120000_create_dream_job_role_models_table.php b/database/migrations/2026_02_16_120000_create_dream_job_role_models_table.php new file mode 100644 index 000000000..a0353bf19 --- /dev/null +++ b/database/migrations/2026_02_16_120000_create_dream_job_role_models_table.php @@ -0,0 +1,33 @@ +id(); + $table->string('first_name'); + $table->string('last_name'); + $table->string('slug')->unique(); + $table->string('role'); + $table->string('image'); + $table->string('country', 8); + $table->text('description1')->nullable(); + $table->text('description2')->nullable(); + $table->string('link')->nullable(); + $table->string('video')->nullable(); + $table->string('pathway_map_link')->nullable(); + $table->unsignedInteger('position')->default(0); + $table->boolean('active')->default(true); + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists('dream_job_role_models'); + } +}; diff --git a/database/migrations/2026_02_16_130000_create_dream_jobs_page_and_resources_tables.php b/database/migrations/2026_02_16_130000_create_dream_jobs_page_and_resources_tables.php new file mode 100644 index 000000000..e5ac1afdc --- /dev/null +++ b/database/migrations/2026_02_16_130000_create_dream_jobs_page_and_resources_tables.php @@ -0,0 +1,66 @@ +id(); + $table->boolean('hero_dynamic')->default(false); + $table->boolean('about_dynamic')->default(false); + $table->boolean('role_models_dynamic')->default(false); + $table->boolean('resources_dynamic')->default(false); + $table->text('hero_intro')->nullable(); + $table->string('hero_cta_text')->nullable(); + $table->string('hero_cta_link')->nullable(); + $table->string('about_title')->nullable(); + $table->text('about_description')->nullable(); + $table->string('about_video_url')->nullable(); + $table->string('role_models_title')->nullable(); + $table->string('resources_title')->nullable(); + $table->json('locale_overrides')->nullable(); + $table->timestamps(); + }); + } + + if (!Schema::hasTable('dream_jobs_resources')) { + Schema::create('dream_jobs_resources', function (Blueprint $table) { + $table->id(); + $table->unsignedBigInteger('page_id')->default(1); + $table->string('title'); + $table->text('description')->nullable(); + $table->string('button_text')->nullable(); + $table->string('button_link')->nullable(); + $table->string('image')->nullable(); + $table->unsignedInteger('position')->default(0); + $table->boolean('active')->default(true); + $table->json('locale_overrides')->nullable(); + $table->timestamps(); + }); + } + + if (DB::table('dream_jobs_page')->count() === 0) { + DB::table('dream_jobs_page')->insert([ + 'id' => 1, + 'hero_dynamic' => false, + 'about_dynamic' => false, + 'role_models_dynamic' => false, + 'resources_dynamic' => false, + 'hero_cta_link' => '#dream-job-resources', + 'created_at' => now(), + 'updated_at' => now(), + ]); + } + } + + public function down(): void + { + Schema::dropIfExists('dream_jobs_resources'); + Schema::dropIfExists('dream_jobs_page'); + } +}; diff --git a/database/migrations/2026_02_16_131000_add_about_video_url_to_dream_jobs_page_table.php b/database/migrations/2026_02_16_131000_add_about_video_url_to_dream_jobs_page_table.php new file mode 100644 index 000000000..d3a65f466 --- /dev/null +++ b/database/migrations/2026_02_16_131000_add_about_video_url_to_dream_jobs_page_table.php @@ -0,0 +1,25 @@ +string('about_video_url')->nullable()->after('about_description'); + }); + } + } + + public function down(): void + { + if (Schema::hasTable('dream_jobs_page') && Schema::hasColumn('dream_jobs_page', 'about_video_url')) { + Schema::table('dream_jobs_page', function (Blueprint $table) { + $table->dropColumn('about_video_url'); + }); + } + } +}; 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 new file mode 100644 index 000000000..eeb8f5a93 --- /dev/null +++ b/database/migrations/2026_02_16_140000_create_hackathons_page_table.php @@ -0,0 +1,49 @@ +id(); + $table->boolean('dynamic_content')->default(false); + $table->string('hero_title')->nullable(); + $table->text('hero_subtitle')->nullable(); + $table->string('intro_title')->nullable(); + $table->text('intro_paragraph_1')->nullable(); + $table->text('intro_paragraph_2')->nullable(); + $table->string('details_title')->nullable(); + $table->text('details_paragraph_1')->nullable(); + $table->text('details_paragraph_2')->nullable(); + $table->text('details_paragraph_3')->nullable(); + $table->text('details_paragraph_4')->nullable(); + $table->string('video_url')->nullable(); + $table->string('recap_button_text')->nullable(); + $table->string('recap_button_link')->nullable(); + $table->string('toolkit_button_text')->nullable(); + $table->string('toolkit_button_link')->nullable(); + $table->json('locale_overrides')->nullable(); + $table->timestamps(); + }); + } + + if (DB::table('hackathons_page')->count() === 0) { + DB::table('hackathons_page')->insert([ + 'id' => 1, + 'dynamic_content' => false, + 'created_at' => now(), + 'updated_at' => now(), + ]); + } + } + + public function down(): void + { + Schema::dropIfExists('hackathons_page'); + } +}; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index e784b2012..0761fcced 100755 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -29,6 +29,9 @@ public function run(): void // Resource seeders $this->call(ResourceEditorRoleSeeder::class); + $this->call(DreamJobRoleModelSeeder::class); + $this->call(DreamJobsPageSeeder::class); + $this->call(HackathonsPageSeeder::class); $this->call(SchoolSeeder::class); diff --git a/database/seeders/DreamJobRoleModelSeeder.php b/database/seeders/DreamJobRoleModelSeeder.php new file mode 100644 index 000000000..27ea35956 --- /dev/null +++ b/database/seeders/DreamJobRoleModelSeeder.php @@ -0,0 +1,210 @@ + 'Anny', + 'last_name' => 'Tubbs', + 'slug' => 'anny-tubbs', + 'role' => 'Multimedia producer at First Move Productions', + 'image' => '/images/dream-jobs/anny-tubbs.png', + 'description1' => "Anny Tubbs' professional path began in law and evolved into multinational leadership roles, where she was tasked with delivering large-scale change in complex matrix organisations. Her focus grew from antitrust matters and sales to anti-corruption and the full spectrum of business ethics. This taught her the importance of thoughtful communication and outreach.", + 'description2' => "Anny has always been inspired by individuals whose values and skills make a positive difference. In today’s hyperconnected world - where digital and real-world issues intertwine - she seeks to leverage digital media to spotlight constructive news and encourage informed dialogue for a better future. To update her skills, Anny attended film school and completed an MSc in Journalism and Media in Europe. She co-founded a production company, directed several short documentaries, and began working with the multimedia team of a European news agency. Today, she is involved in multimedia production, content creation and educational projects that focus on digital and values-driven learning. She enjoys the fact that technology makes it possible to connect with people from all over the world.", + 'link' => 'https://www.linkedin.com/in/annytubbs/', + 'video' => 'https://www.youtube.com/embed/MUYkkR45Ky4?si=_achxuxizZ4Fs_OE', + 'pathway_map_link' => 'Career Pathway Map Anny Tubbs.pdf', + 'country' => 'be', + ], + [ + 'first_name' => 'Magda', + 'last_name' => 'Vanzetto', + 'slug' => 'magda-vanzetto', + 'role' => 'Head of Energy Engineers at Siemens', + 'image' => '/images/dream-jobs/magda-vanzetto.png', + 'description1' => 'Magda Vanzetto is the Head of Energy Engineers at Siemens Smart Infrastructure in Sustainability Technical Unit.', + 'description2' => 'She has been active part in projects aimed at improving energy efficiency and reducing carbon footprint of multiple customers facilities.', + 'link' => 'https://www.linkedin.com/in/magda-vanzetto-42567034/', + 'video' => 'https://www.youtube.com/embed/es3l_bbGGu4?si=77C2SMDdr_-20m_s', + 'pathway_map_link' => 'C4E Career Pathway Map_MV.pdf', + 'country' => 'sz', + ], + [ + 'first_name' => 'Roxana', + 'last_name' => 'Freusse', + 'slug' => 'roxana-freusse', + 'role' => 'Team Lead Cloud Solutions & DevOps, Scrum Master at Siemens', + 'image' => '/images/dream-jobs/roxana-freusse.png', + 'description1' => 'Roxana Freusse is the Team Lead for Cloud Solutions & DevOps and a Scrum Master at Siemens. With extensive experience in cloud technologies and agile methodologies, she excels in leading cross-functional teams to deliver innovative solutions.', + 'description2' => 'Roxana is known for her expertise in cloud infrastructure, DevOps practices, and her ability to foster collaboration and continuous improvement within her teams. Her leadership and technical skills have been instrumental in driving successful projects and enhancing operational efficiency at Siemens.', + 'link' => 'https://www.linkedin.com/in/roxanafreusse/', + 'video' => 'https://www.youtube.com/embed/1ogvTUbmudY?si=yueOQZuAH8DF7I5R', + 'pathway_map_link' => 'C4E Career Pathway Map Roxana.pdf', + 'country' => 'sz', + ], + [ + 'first_name' => 'Vasileios', + 'last_name' => 'Linardos', + 'slug' => 'vasileios-linardos', + 'role' => 'Head of AI at ARCHEIOTHIKI SA', + 'image' => '/images/dream-jobs/vasileios-linardos.png', + 'description1' => 'Vasileios Linardos is Head of AI at Archeiothiki SA and a PhDc at the International Hellenic University in Thessaloniki, Greece, where he focuses on developing AI tools and methodologies for disaster management.', + 'description2' => 'He holds a Bachelor\'s degree in Informatics from the Athens University of Economics and Business. His expertise encompasses machine learning and deep learning applications in disaster management, with several publications on the topic. Additionally, Vasileios has expertise in large language models (LLMs), vision-language models (VLLMs), and other emerging technologies, which he applies to design and develop innovative AI solutions.', + 'link' => 'https://www.linkedin.com/in/vasileios-linardos-a18294195/', + 'video' => 'https://www.youtube.com/embed/Nubt062yw_Q?si=3FRAAin70H2XMCZ6', + 'pathway_map_link' => 'C4E Career Map Vasileios Linardos.pdf', + 'country' => 'gr', + ], + [ + 'first_name' => 'Carole', + 'last_name' => 'Colley', + 'slug' => 'carole-colley', + 'role' => 'Bid & Proposal Management at Avanade', + 'image' => '/images/dream-jobs/carole-colley.png', + 'description1' => 'Carole Colley has accumulated over 15 years of experience in IT. Carole\'s journey into digital transformation has been marked by significant milestones. As a functional consultant, project manager and bid manager (pre-sales), Carole has successfully navigated complex projects and contributed to the growth of the organizations she has worked with.', + 'description2' => 'Carole is committed to seizing every possible opportunity to grow both professionally and personally. This dedication is evident in her focus on people management, participation in the School of Innovation, and continuous efforts to train and get certified. Carole\'s growth mindset ensures she remains at the forefront of industry developments and best practices. For Carole, commitment is not just about receiving but also about giving back to peers. As an employee network lead and vice secretary to a Work Council, Carole has demonstrated a strong commitment to fostering a supportive and collaborative work environment. Her leadership in these roles underscores the importance of community and mutual support in achieving collective success.', + 'link' => 'https://www.linkedin.com/in/carole-colley-pmp%C2%AE-a932077b/', + 'video' => 'https://www.youtube.com/embed/CUJOwRUlTpo?si=jQ7LykPZSe_KSQQJ', + 'pathway_map_link' => 'Career Pathway Map Carole Colley.pdf', + 'country' => 'fr', + ], + [ + 'first_name' => 'Christina', + 'last_name' => 'Kiamili', + 'slug' => 'christina-kiamili', + 'role' => 'Digital Portfolio Manager at Siemens', + 'image' => '/images/dream-jobs/christina-kiamili.png', + 'description1' => 'Christina Kiamili is the Digital Portfolio Manager at Siemens.', + 'description2' => 'In her role, she oversees the development and management of digital projects, ensuring they align with the company\'s strategic objectives. Christina is known for her expertise in digital transformation, project management, and her ability to lead cross-functional teams. She focuses on driving innovation and continuous improvement within Siemens, leveraging her strong background in both agile and waterfall methodologies.', + 'link' => 'https://www.linkedin.com/in/christina-kiamili-23582a114/', + 'video' => 'https://www.youtube.com/embed/VcqM5B-iWsE?si=uh9jEpNxeL9yInXN', + 'pathway_map_link' => 'Career Pathway Map Christina Kiamili.pdf', + 'country' => 'sz', + ], + [ + 'first_name' => 'Devon', + 'last_name' => 'Young', + 'slug' => 'devon-young', + 'role' => 'UX Design at Avanade', + 'image' => '/images/dream-jobs/devon-young.png', + 'description1' => 'Devon Young is Senior User Experience Manager at Avanade. She has over 20 years of experience in design, with a recent focus on design operations, strategy, recruiting, and nurturing talent.', + 'description2' => 'She believes that great design outcomes stem from a great team environment. Her passion for design began in high school in Missouri, USA where she created CD covers and T-shirts. Encouraged by her father, she pursued a career in design, studying Graphic Design at university.', + 'link' => 'https://www.linkedin.com/in/devoneyoung/', + 'video' => 'https://www.youtube.com/embed/ZmEc9RKy-MI?si=jQnFHpPXQm5Usa-Y', + 'pathway_map_link' => 'C4E Career Pathway Map - Devon Young.pdf', + 'country' => 'uk', + ], + [ + 'first_name' => 'Paula', + 'last_name' => 'Panarra', + 'slug' => 'paula-panarra', + 'role' => 'General Manager at Avanade UK & Ireland', + 'image' => '/images/dream-jobs/paula-panarra.png', + 'description1' => 'Paula Panarra is the General Manager at Avanade UK & Ireland. Previously, she served as the Global Business Applications Sales Lead for Retail and Consumer Goods at Microsoft Portugal, where she was the General Director from 2016. In this role, she guided the organization through the digital transformation of the Portuguese economy, empowering people and organizations with technology.', + 'description2' => 'She joined Microsoft in 2010 as the CMO Lead, later becoming the Marketing & Operations Lead in 2013, and the Public Sector Lead in 2016. Before her tenure at Microsoft, Paula spent 15 years at Procter & Gamble Portugal and Iberia, holding various roles in Finance and Marketing, including Corporate Marketing Director, Finance Group Director, and Communication Director. Paula graduated in Chemical Engineering from the Instituto Superior Técnico in 1994. She is known for her optimism, solution-oriented mindset, and commitment to doing the right thing. Outside of her professional life, she is a proud mother of three daughters, an avid reader, a music lover, and an adventurous traveler.', + 'link' => 'https://www.linkedin.com/in/paulapanarra/', + 'video' => 'https://www.youtube.com/embed/z5NfcNKMiMk?si=P5CIKlqeft_z0mGN', + 'pathway_map_link' => '', + 'country' => 'sp', + ], + [ + 'first_name' => 'Ribka', + 'last_name' => 'Balakrishnan', + 'slug' => 'ribka-balakrishnan', + 'role' => 'Mechanical Design Engineer at WSAudiology', + 'image' => '/images/dream-jobs/ribka-balakrishnan.png', + 'description1' => 'Ribka Balakrishnan is a Mechanical Design Engineer working at WSAudiology.', + 'description2' => 'She is deeply involved in the research and development of hearing aids, and enjoys the hands-on aspects of her role, including prototyping and testing, which allow her to work closely with the products. She is also passionate about advocating for and supporting girls and women in all parts of society.', + 'link' => 'https://www.linkedin.com/in/ribka-balakrishnan/', + 'video' => 'https://www.youtube.com/embed/17X3-okWYhc?si=xyToCplHCQcbU4ex', + 'pathway_map_link' => 'Career Pathway Map Ribka Balakrishnan.pdf', + 'country' => 'da', + ], + [ + 'first_name' => 'Jeevantika', + 'last_name' => 'Lingalwar', + 'slug' => 'jeevantika-lingalwar', + 'role' => 'Cloud Solution Architect at Microsoft', + 'image' => '/images/dream-jobs/jeevantika-lingalwar.png', + 'description1' => 'Jeevantika Lingalwar is a Partner Solution Architect at Microsoft, TEDx speaker, and founder of International Women in Tech, leading a community of over 7000 members.', + 'description2' => 'With M.Sc. degree in Cloud Computing and B.E in Computer Science Engineering, she is passionate about Women in Technology and Diversity & Inclusion. Jeevantika mentors\' young minds about AI and hosts the podcast "The Unplanned Journey," celebrating women\'s resilience. She has been recognized as a Top Voice of the New Era of Leaders 2024, awarded the WomenTech Global Ambassador Award in 2021, and is a finalist for many awards in Ireland.', + 'link' => 'https://www.linkedin.com/in/jeevantika-lingalwar/', + 'video' => 'https://www.youtube.com/embed/98QFrA-mJkA?si=2d2wS4ll4tnDhO8F', + 'pathway_map_link' => 'Career Pathway Map Jeevantika Lingalwar.pdf', + 'country' => 'ei', + ], + [ + 'first_name' => 'Dominik', + 'last_name' => 'Bolerác', + 'slug' => 'dominik-bolerác', + 'role' => 'Application Developer at Zurich Insurance Company', + 'image' => '/images/dream-jobs/dominik-bolerác.png', + 'description1' => 'Dominik\'s professional journey began during his university studies at the international company Zurich Insurance. He participated in a rotational internship program, which allowed students to experience working in various departments within the company - including risk management, actuarial services, and pricing & analytics.', + 'description2' => 'Currently, Dominik works full-time as an application developer in the risk management department, where he is responsible for day-to-day production tasks and creating API packages that help automate internal processes. He works with programming languages (R, SQL, DAX) and data visualization tools (Power BI) on a daily basis. Dominik studied mathematics in high school and continued to pursue it at the university level. In his free time, he plays drums in an ABBA tribute band, traveling all over the world thanks to his flexible work. He is also very passionate about running and holds a professional running coaching certificate, which allows him to share his knowledge with colleagues, his athletes, and other passionate runners.', + 'link' => 'https://www.linkedin.com/in/dominik-boler%C3%A1c-39b333209/', + 'video' => 'https://www.youtube.com/embed/hNuyHzFE2J8?si=qjghlBjFnd4DKh6J', + 'pathway_map_link' => 'C4E Career Pathway Map - Dominik Bolerac.pdf', + 'country' => 'lo', + ], + [ + 'first_name' => 'Sara', + 'last_name' => 'Mathews', + 'slug' => 'sara-mathews', + 'role' => 'Group Responsible AI Manager at The Adecco Group', + 'image' => '/images/dream-jobs/sara-mathews.png', + 'description1' => 'Sarah Mathews is the Group Responsible AI Manager at The Adecco Group. She is responsible for operationalizing the group\'s Responsible AI Principles by establishing governance, guidelines, and change management processes, as well as co-leading the implementation of the AI Act. With a wealth of experience in leading global AI initiatives, she is renowned for her expertise in integrating ethical and inclusive AI practices within the company.', + 'description2' => 'Sarah\'s dual background in Human Resources and AI gives her a unique perspective on HR-related AI systems worldwide. She excels at engaging stakeholders and bridging the gap between business and development teams, ensuring AI projects are scaled strategically with a business-driven and human-centric approach. Passionate about fostering knowledge-sharing communities, Sarah actively promotes best practices in AI across The Adecco Group\'s brands and countries. Beyond her professional role, Sarah is dedicated to volunteering efforts that encourage women to enter the field of Data Science. She also mentors aspiring female leaders, helping them navigate and build successful careers in tech.', + 'link' => 'https://www.linkedin.com/in/sarah-mathews-87b481122/', + 'video' => 'https://www.youtube.com/embed/-rxlrF3Mt8A?si=4DMHeKV8LZS26_5x', + 'pathway_map_link' => '', + 'country' => 'gm', + ], + [ + 'first_name' => 'Paraskevi', + 'last_name' => 'Nomikou', + 'slug' => 'paraskevi-nomikou', + 'role' => 'Marine Geologist and an Assistant Professor at the Department of Geology and Geoenvironment at the National and Kapodistrian University of Athens', + 'image' => '/images/dream-jobs/paraskevi-nomikou.png', + 'description1' => 'Paraskevi Nomikou is a marine geologist and an Assistant Professor at the Department of Geology and Geoenvironment at the National and Kapodistrian University of Athens, Greece. She specializes in the study of underwater volcanoes and seafloor processes. With extensive experience in marine volcanic activity, she has participated in over 70 oceanographic cruises, focusing on submarine volcanism, mud volcanoes, landslides, and seafloor mineral deposits', + 'description2' => 'Paraskevi has played a significant role in evaluating potential hazards associated with volcanic activity at the Santorini volcano and has been involved in mapping the seafloor of ocean core complexes and offshore volcanoes. She is also dedicated to educating and inspiring young women to pursue careers in oceanography through her lectures and innovative marine technologies', + 'link' => 'https://www.linkedin.com/in/paraskevi-nomikou-325393203/', + 'video' => 'https://www.youtube.com/embed/7M5Gie2rRt4?si=mqaOkY9C29KxgRUO', + 'pathway_map_link' => '', + 'country' => 'gr', + ], + ]; + + foreach ($defaults as $index => $row) { + DreamJobRoleModel::updateOrCreate( + ['slug' => $row['slug']], + [ + 'first_name' => $row['first_name'], + 'last_name' => $row['last_name'], + 'role' => $row['role'], + 'image' => $row['image'], + 'country' => $row['country'], + 'description1' => $row['description1'], + 'description2' => $row['description2'], + 'link' => $row['link'], + 'video' => $row['video'], + 'pathway_map_link' => $row['pathway_map_link'] !== '' ? $row['pathway_map_link'] : null, + 'position' => $index, + 'active' => true, + ] + ); + } + + $this->command->info('Dream Jobs role models seeded/updated. You can now manage them in Nova.'); + } +} diff --git a/database/seeders/DreamJobsPageSeeder.php b/database/seeders/DreamJobsPageSeeder.php new file mode 100644 index 000000000..455027393 --- /dev/null +++ b/database/seeders/DreamJobsPageSeeder.php @@ -0,0 +1,81 @@ + 1], + [ + 'hero_dynamic' => false, + 'about_dynamic' => false, + 'role_models_dynamic' => false, + 'resources_dynamic' => false, + 'hero_intro' => __('dream-jobs-in-digital.landing_header'), + 'hero_cta_text' => __('dream-jobs-in-digital.get_involved'), + 'hero_cta_link' => '#dream-job-resources', + 'about_title' => __('dream-jobs-in-digital.about_title'), + 'about_description' => __('dream-jobs-in-digital.about_description'), + 'about_video_url' => 'https://www.youtube.com/embed/pzP-kToeym4?si=FzutCQGW4rO5M_5A', + 'role_models_title' => __('dream-jobs-in-digital.our_role_models'), + 'resources_title' => __('dream-jobs-in-digital.resources'), + ] + ); + + $defaults = [ + [ + 'title' => __('dream-jobs-in-digital.resource_title_1'), + 'description' => __('dream-jobs-in-digital.resource_description_1'), + 'button_text' => __('dream-jobs-in-digital.resource_button_1'), + 'button_link' => '/docs/dream-jobs/C4E WP4 Careers in Digital Guide Toolkit.pdf', + 'image' => '/images/dream-jobs/career-guide.png', + ], + [ + 'title' => __('dream-jobs-in-digital.resource_title_2'), + 'description' => __('dream-jobs-in-digital.resource_description_2'), + 'button_text' => __('dream-jobs-in-digital.resource_button_2'), + 'button_link' => '/docs/dream-jobs/C4E WP4 Career Day Toolkit.pdf', + 'image' => '/images/dream-jobs/inspiration-day.png', + ], + [ + 'title' => __('dream-jobs-in-digital.resource_title_3'), + 'description' => __('dream-jobs-in-digital.resource_description_3'), + 'button_text' => __('dream-jobs-in-digital.resource_button_3'), + 'button_link' => '/docs/dream-jobs/Practical Skills – VET Toolkit.pdf', + 'image' => '/images/dream-jobs/vet-activities.png', + ], + [ + 'title' => __('dream-jobs-in-digital.resource_title_4'), + 'description' => __('dream-jobs-in-digital.resource_description_4'), + 'button_text' => __('dream-jobs-in-digital.resource_button_4'), + 'button_link' => 'https://www.techskills.org/careers/quiz/', + 'image' => '/images/dream-jobs/skills-test.png', + ], + ]; + + foreach ($defaults as $index => $row) { + DreamJobsResource::updateOrCreate( + ['page_id' => $page->id, 'button_link' => $row['button_link']], + [ + 'title' => $row['title'], + 'description' => $row['description'], + 'button_text' => $row['button_text'], + 'image' => $row['image'], + 'position' => $index, + 'active' => true, + ] + ); + } + } +} diff --git a/database/seeders/HackathonsPageSeeder.php b/database/seeders/HackathonsPageSeeder.php new file mode 100644 index 000000000..09642122d --- /dev/null +++ b/database/seeders/HackathonsPageSeeder.php @@ -0,0 +1,39 @@ + 1], + [ + 'dynamic_content' => false, + 'hero_title' => 'Hackathons', + 'hero_subtitle' => 'Bring your ideas to life!', + 'intro_title' => 'Hackathons', + 'intro_paragraph_1' => "A hackathon is an event where participants with diverse skills collaborate to tackle global challenges. Participants form teams to brainstorm, design, and code, aiming to produce a working solution or prototype by the event's conclusion. Beyond fostering innovation and teamwork, EU Code Week hackathons offer a platform for young enthusiasts to learn, showcase their talents, and connect with like-minded peers.", + 'intro_paragraph_2' => 'Adapting the traditional hackathon format, the EU Code Week Hackathons take into consideration the age of the participants and cater to the unique skills, insights, and interests of adolescents. The aim of the EU Code Week Hackathons is to inspire young people to develop their coding and problem-solving skills by engaging them in collaborative, creative, and innovative projects.', + 'details_title' => 'EU Code Week Hackathons 2025-26', + 'details_paragraph_1' => 'EU Code Week Hackathons share a common theme that strengthens connection and belonging among young innovators across Europe. The central theme for the 2025 edition is From Code to Community: Bridging Digital Skills and Social Impact..', + 'details_paragraph_2' => 'The ten national hackathons — Italy (Florence), Italy (Turin), Greece, Croatia, Ukraine, Turkey, Spain, Lithuania, Slovenia, and France — mark a vibrant journey of creativity and collaboration. Italy (Florence) opened the series with its event in October 2025, while all other national hackathons are taking place from now until the end of January 2026. Each event invites teams of young people aged 15 to 19 to learn, innovate, and develop digital solutions that tackle real societal challenges.', + 'details_paragraph_3' => 'Join us online for the EU Finals on 11 March 2026, where all national finalists will present their projects and celebrate their shared achievements. Expect inspiring ideas, expert jury insights, and plenty of positive energy — a celebration of how young people use technology to make a difference.', + 'details_paragraph_4' => 'Be part of the excitement as we honour the outstanding teams shaping the future of digital innovation!', + 'video_url' => 'https://www.youtube.com/embed/fx0zJCpUTa8', + 'recap_button_text' => 'Hackathons Final 2024 Recap', + 'recap_button_link' => 'https://eventornado.com/event/eu-codeweek-hackathon2024#Finals%20-%20EU%20Code%20Week%20Hackathon%202024', + 'toolkit_button_text' => 'Hackathon 2025 Toolkit', + 'toolkit_button_link' => '/docs/C4EU_D2.7 Code Week Event Hackathon Design & Toolkit Final 18.06.2025.pdf', + ] + ); + } +} diff --git a/resources/views/hackathons/index.blade.php b/resources/views/hackathons/index.blade.php index fa2feec09..b0b1e4647 100644 --- a/resources/views/hackathons/index.blade.php +++ b/resources/views/hackathons/index.blade.php @@ -5,6 +5,10 @@ (object) ['label' => 'Educational Resources', 'href' => '/educational-resources'], (object) ['label' => 'Hackathons', 'href' => ''], ]; + + $hasHackathonsPageTable = \Illuminate\Support\Facades\Schema::hasTable('hackathons_page'); + $page = $hasHackathonsPageTable ? \App\HackathonsPage::config() : null; + $dynamic = $page && $page->dynamic_content; @endphp @section('layout.breadcrumb') @@ -22,10 +26,18 @@

- Hackathons + @if($dynamic && $page && $page->contentForLocale('hero_title')) + {{ $page->contentForLocale('hero_title') }} + @else + Hackathons + @endif

- Bring your ideas to life! + @if($dynamic && $page && $page->contentForLocale('hero_subtitle')) + {!! $page->contentForLocale('hero_subtitle') !!} + @else + Bring your ideas to life! + @endif

@@ -52,14 +64,26 @@ class="absolute top-0 right-0 h-full max-w-[calc(70vw)] object-cover hidden md:b

- Hackathons + @if($dynamic && $page && $page->contentForLocale('intro_title')) + {{ $page->contentForLocale('intro_title') }} + @else + Hackathons + @endif

- A hackathon is an event where participants with diverse skills collaborate to tackle global challenges. Participants form teams to brainstorm, design, and code, aiming to produce a working solution or prototype by the event's conclusion. Beyond fostering innovation and teamwork, EU Code Week hackathons offer a platform for young enthusiasts to learn, showcase their talents, and connect with like-minded peers. + @if($dynamic && $page && $page->contentForLocale('intro_paragraph_1')) + {!! $page->contentForLocale('intro_paragraph_1') !!} + @else + A hackathon is an event where participants with diverse skills collaborate to tackle global challenges. Participants form teams to brainstorm, design, and code, aiming to produce a working solution or prototype by the event's conclusion. Beyond fostering innovation and teamwork, EU Code Week hackathons offer a platform for young enthusiasts to learn, showcase their talents, and connect with like-minded peers. + @endif

- Adapting the traditional hackathon format, the EU Code Week Hackathons take into consideration the age of the participants and cater to the unique skills, insights, and interests of adolescents. - The aim of the EU Code Week Hackathons is to inspire young people to develop their coding and problem-solving skills by engaging them in collaborative, creative, and innovative projects. + @if($dynamic && $page && $page->contentForLocale('intro_paragraph_2')) + {!! $page->contentForLocale('intro_paragraph_2') !!} + @else + Adapting the traditional hackathon format, the EU Code Week Hackathons take into consideration the age of the participants and cater to the unique skills, insights, and interests of adolescents. + The aim of the EU Code Week Hackathons is to inspire young people to develop their coding and problem-solving skills by engaging them in collaborative, creative, and innovative projects. + @endif

@@ -85,38 +109,68 @@ class="animation-element move-background duration-[1.5s] absolute z-0 lg:-bottom
@include('layout.video-player', [ 'id' => 'hackathons-video', - 'src' => 'https://www.youtube.com/embed/fx0zJCpUTa8', + 'src' => ($dynamic && $page && $page->video_url) ? $page->video_url : 'https://www.youtube.com/embed/fx0zJCpUTa8', ])

- EU Code Week Hackathons 2025-26 + @if($dynamic && $page && $page->contentForLocale('details_title')) + {{ $page->contentForLocale('details_title') }} + @else + EU Code Week Hackathons 2025-26 + @endif

- EU Code Week Hackathons share a common theme that strengthens connection and belonging among young innovators across Europe. The central theme for the 2025 edition is From Code to Community: Bridging Digital Skills and Social Impact.. + @if($dynamic && $page && $page->contentForLocale('details_paragraph_1')) + {!! $page->contentForLocale('details_paragraph_1') !!} + @else + EU Code Week Hackathons share a common theme that strengthens connection and belonging among young innovators across Europe. The central theme for the 2025 edition is From Code to Community: Bridging Digital Skills and Social Impact.. + @endif

- The ten national hackathons — Italy (Florence), Italy (Turin), Greece, Croatia, Ukraine, Turkey, Spain, Lithuania, Slovenia, and France — mark a vibrant journey of creativity and collaboration. Italy (Florence) opened the series with its event in October 2025, while all other national hackathons are taking place from now until the end of January 2026. Each event invites teams of young people aged 15 to 19 to learn, innovate, and develop digital solutions that tackle real societal challenges. + @if($dynamic && $page && $page->contentForLocale('details_paragraph_2')) + {!! $page->contentForLocale('details_paragraph_2') !!} + @else + The ten national hackathons — Italy (Florence), Italy (Turin), Greece, Croatia, Ukraine, Turkey, Spain, Lithuania, Slovenia, and France — mark a vibrant journey of creativity and collaboration. Italy (Florence) opened the series with its event in October 2025, while all other national hackathons are taking place from now until the end of January 2026. Each event invites teams of young people aged 15 to 19 to learn, innovate, and develop digital solutions that tackle real societal challenges. + @endif

- Join us online for the EU Finals on 11 March 2026, where all national finalists will present their projects and celebrate their shared achievements. Expect inspiring ideas, expert jury insights, and plenty of positive energy — a celebration of how young people use technology to make a difference. + @if($dynamic && $page && $page->contentForLocale('details_paragraph_3')) + {!! $page->contentForLocale('details_paragraph_3') !!} + @else + Join us online for the EU Finals on 11 March 2026, where all national finalists will present their projects and celebrate their shared achievements. Expect inspiring ideas, expert jury insights, and plenty of positive energy — a celebration of how young people use technology to make a difference. + @endif

- Be part of the excitement as we honour the outstanding teams shaping the future of digital innovation!! + @if($dynamic && $page && $page->contentForLocale('details_paragraph_4')) + {!! $page->contentForLocale('details_paragraph_4') !!} + @else + Be part of the excitement as we honour the outstanding teams shaping the future of digital innovation!! + @endif

- Hackathons Final 2024 Recap + @if($dynamic && $page && $page->contentForLocale('recap_button_text')) + {{ $page->contentForLocale('recap_button_text') }} + @else + Hackathons Final 2024 Recap + @endif - Hackathon 2025 Toolkit + + @if($dynamic && $page && $page->contentForLocale('toolkit_button_text')) + {{ $page->contentForLocale('toolkit_button_text') }} + @else + Hackathon 2025 Toolkit + @endif +
diff --git a/resources/views/static/dream-jobs-in-digital-role.blade.php b/resources/views/static/dream-jobs-in-digital-role.blade.php index fc24161d6..4b1f1bfac 100644 --- a/resources/views/static/dream-jobs-in-digital-role.blade.php +++ b/resources/views/static/dream-jobs-in-digital-role.blade.php @@ -3,7 +3,7 @@ @php $slug = request()->segment(2); - $results = [ + $fallbackResults = [ [ 'id' => '1', 'first_name' => 'Anny', @@ -188,14 +188,49 @@ ], ]; - $item = collect($results)->firstWhere('slug', $slug); + $hasRoleModelsTable = \Illuminate\Support\Facades\Schema::hasTable('dream_job_role_models'); + $hasDbRoleModels = $hasRoleModelsTable + ? \App\DreamJobRoleModel::query()->where('active', true)->exists() + : false; + + if ($hasDbRoleModels) { + $dbItem = \App\DreamJobRoleModel::query() + ->where('active', true) + ->where('slug', $slug) + ->first(); + + abort_if(! $dbItem, 404); + + $item = [ + 'id' => (string) $dbItem->id, + 'first_name' => (string) $dbItem->first_name, + 'last_name' => (string) $dbItem->last_name, + 'slug' => (string) $dbItem->slug, + 'role' => (string) $dbItem->role, + 'image' => (string) $dbItem->image, + 'description1' => (string) ($dbItem->description1 ?? ''), + 'description2' => (string) ($dbItem->description2 ?? ''), + 'link' => (string) ($dbItem->link ?? ''), + 'video' => (string) ($dbItem->video ?? ''), + 'pathway_map_link' => (string) ($dbItem->pathway_map_link ?? ''), + 'country' => (string) $dbItem->country, + ]; + } else { + $item = collect($fallbackResults)->firstWhere('slug', $slug); + abort_if(! $item, 404); + } $list = [ (object) ['label' => 'Careers in Digital', 'href' => '/dream-jobs-in-digital'], (object) ['label' => $item['first_name'] . ' ' . $item['last_name'], 'href' => ''], ]; - $resources = [ + $hasDreamJobsPageTable = \Illuminate\Support\Facades\Schema::hasTable('dream_jobs_page'); + $hasDreamJobsResourcesTable = \Illuminate\Support\Facades\Schema::hasTable('dream_jobs_resources'); + $page = $hasDreamJobsPageTable ? \App\DreamJobsPage::config() : null; + $resourcesDynamic = $page && $page->resources_dynamic; + + $fallbackResources = [ (object) [ 'title' => __('dream-jobs-in-digital.resource_title_1'), 'description' => __('dream-jobs-in-digital.resource_description_1'), @@ -225,6 +260,9 @@ 'image' => '/images/dream-jobs/skills-test.png', ], ]; + + $dynamicResources = ($page && $hasDreamJobsResourcesTable) ? $page->resourcesForLocale() : []; + $resources = ($resourcesDynamic && !empty($dynamicResources)) ? $dynamicResources : $fallbackResources; @endphp @section('layout.breadcrumb') @include('layout.breadcrumb', ['list' => $list]) @@ -298,7 +336,11 @@ class="animation-element move-background duration-[1.5s] absolute z-0 lg:-bottom

- @lang('dream-jobs-in-digital.resources') + @if($resourcesDynamic && $page && $page->contentForLocale('resources_title')) + {{ $page->contentForLocale('resources_title') }} + @else + @lang('dream-jobs-in-digital.resources') + @endif

@foreach($resources as $resource) diff --git a/resources/views/static/dream-jobs-in-digital.blade.php b/resources/views/static/dream-jobs-in-digital.blade.php index 9c9dd9c4d..527cef628 100644 --- a/resources/views/static/dream-jobs-in-digital.blade.php +++ b/resources/views/static/dream-jobs-in-digital.blade.php @@ -5,7 +5,15 @@ (object) ['label' => 'Careers in Digital', 'href' => ''], ]; - $roles = [ + $hasDreamJobsPageTable = \Illuminate\Support\Facades\Schema::hasTable('dream_jobs_page'); + $hasDreamJobsResourcesTable = \Illuminate\Support\Facades\Schema::hasTable('dream_jobs_resources'); + $page = $hasDreamJobsPageTable ? \App\DreamJobsPage::config() : null; + $heroDynamic = $page && $page->hero_dynamic; + $aboutDynamic = $page && $page->about_dynamic; + $roleModelsDynamic = $page && $page->role_models_dynamic; + $resourcesDynamic = $page && $page->resources_dynamic; + + $fallbackRoles = [ [ 'id' => '1', 'first_name' => 'Anny', @@ -125,10 +133,33 @@ ], ]; - $result = collect($roles); - $sortedResults = $result->sortBy('first_name'); + $hasRoleModelsTable = \Illuminate\Support\Facades\Schema::hasTable('dream_job_role_models'); + $hasDbRoleModels = $hasRoleModelsTable + ? \App\DreamJobRoleModel::query()->where('active', true)->exists() + : false; + + if ($hasDbRoleModels) { + $sortedResults = \App\DreamJobRoleModel::query() + ->where('active', true) + ->orderBy('position') + ->orderBy('first_name') + ->get() + ->map(function ($item) { + return [ + 'id' => (string) $item->id, + 'first_name' => (string) $item->first_name, + 'last_name' => (string) $item->last_name, + 'slug' => (string) $item->slug, + 'role' => (string) $item->role, + 'image' => (string) $item->image, + 'country' => (string) $item->country, + ]; + }); + } else { + $sortedResults = collect($fallbackRoles)->sortBy('first_name'); + } - $resources = [ + $fallbackResources = [ (object) [ 'title' => __('dream-jobs-in-digital.resource_title_1'), 'description' => __('dream-jobs-in-digital.resource_description_1'), @@ -176,7 +207,10 @@ ]; }); - $resources = $dbResources->concat(collect($resources))->all(); + $fallbackResources = $dbResources->concat(collect($fallbackResources))->all(); + + $dynamicResources = ($page && $hasDreamJobsResourcesTable) ? $page->resourcesForLocale() : []; + $resources = ($resourcesDynamic && !empty($dynamicResources)) ? $dynamicResources : $fallbackResources; @endphp @section('layout.breadcrumb') @include('layout.breadcrumb', ['list' => $list]) @@ -194,13 +228,23 @@ class="mb-4 max-w-full" src="/images/dream-jobs/dream_jobs_logo.svg" />

- @lang('dream-jobs-in-digital.landing_header') + @if($heroDynamic && $page && $page->contentForLocale('hero_intro')) + {!! $page->contentForLocale('hero_intro') !!} + @else + @lang('dream-jobs-in-digital.landing_header') + @endif

- @lang('dream-jobs-in-digital.get_involved') + + @if($heroDynamic && $page && $page->contentForLocale('hero_cta_text')) + {{ $page->contentForLocale('hero_cta_text') }} + @else + @lang('dream-jobs-in-digital.get_involved') + @endif +

- @lang('dream-jobs-in-digital.about_title') + @if($aboutDynamic && $page && $page->contentForLocale('about_title')) + {{ $page->contentForLocale('about_title') }} + @else + @lang('dream-jobs-in-digital.about_title') + @endif

- @lang('dream-jobs-in-digital.about_description') + @if($aboutDynamic && $page && $page->contentForLocale('about_description')) + {!! $page->contentForLocale('about_description') !!} + @else + @lang('dream-jobs-in-digital.about_description') + @endif

@include('layout.video-player', [ 'id' => 'careers-about', - 'src' => 'https://www.youtube.com/embed/pzP-kToeym4?si=FzutCQGW4rO5M_5A', + 'src' => ($aboutDynamic && $page && $page->about_video_url) ? $page->about_video_url : 'https://www.youtube.com/embed/pzP-kToeym4?si=FzutCQGW4rO5M_5A', ])
@@ -247,7 +299,11 @@ class="w-full rounded-2xl object-cover object-center h-[calc(80vw-40px)] sm:h-au

- @lang('dream-jobs-in-digital.our_role_models') + @if($roleModelsDynamic && $page && $page->contentForLocale('role_models_title')) + {{ $page->contentForLocale('role_models_title') }} + @else + @lang('dream-jobs-in-digital.our_role_models') + @endif

@foreach($sortedResults as $result) @@ -314,7 +370,11 @@ class="animation-element move-background duration-[1.5s] absolute z-0 lg:-bottom

- @lang('dream-jobs-in-digital.resources') + @if($resourcesDynamic && $page && $page->contentForLocale('resources_title')) + {{ $page->contentForLocale('resources_title') }} + @else + @lang('dream-jobs-in-digital.resources') + @endif

@foreach($resources as $resource)