diff --git a/app/HomeSlide.php b/app/HomeSlide.php new file mode 100644 index 000000000..a5a526d57 --- /dev/null +++ b/app/HomeSlide.php @@ -0,0 +1,53 @@ + 'boolean', + 'show_countdown' => 'boolean', + 'countdown_target' => 'datetime', + 'position' => 'integer', + ]; + + public function scopeActive($query) + { + return $query->where('active', true); + } + + public function scopeOrdered($query) + { + return $query->orderBy('position')->orderBy('id'); + } + + /** + * Full URL for image (prefix with base URL if stored as path). + */ + public function getImageUrlAttribute(): ?string + { + if (empty($this->image)) { + return null; + } + if (str_starts_with($this->image, 'http://') || str_starts_with($this->image, 'https://')) { + return $this->image; + } + return url(ltrim($this->image, '/')); + } +} diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 23f229fac..97d98c648 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -2,58 +2,97 @@ namespace App\Http\Controllers; +use App\HomeSlide; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Schema; use Illuminate\View\View; class HomeController extends Controller { public function index(Request $request): View { - $activities = collect([ - /* [ - 'title' => 'home.banner1_title', - 'description' => 'home.banner1_description', - 'url' => '/dream-jobs-in-digital', - 'style_color' => 'background-image: linear-gradient(36.92deg, #1C4DA1 20.32%, #0040AE 28.24%);', - 'btn_lang' => 'home.get_involved', - ], */ + $activities = $this->getActivities(); + + return view('static.home', compact('activities')); + } + + /** + * Homepage slider slides: from Nova (home_slides) when present, else default hardcoded set. + */ + private function getActivities() + { + if (Schema::hasTable('home_slides')) { + $slides = HomeSlide::active()->ordered()->get(); + if ($slides->isNotEmpty()) { + return $slides->map(function (HomeSlide $slide) { + return [ + 'title' => $slide->title, + 'description' => $slide->description ?? '', + 'url' => $slide->url, + 'btn_lang' => $slide->button_text, + 'url2' => $slide->url2, + 'btn2_lang' => $slide->button2_text, + 'image' => $slide->image_url, + 'show_countdown' => $slide->show_countdown, + 'countdown_target' => $slide->countdown_target?->toIso8601String(), + ]; + })->values(); + } + } + + $defaultImages = [ + asset('/images/dream-jobs/dream_jobs_bg.png'), + asset('/images/digital-girls/banner_bg.png'), + asset('images/homepage/slide1.png'), + asset('images/search/search_bg_lg_2.jpeg'), + asset('/images/homepage/festive_acts_of_digital_kindness.png'), + ]; + + return collect([ [ 'title' => 'home.banner5_title', 'description' => 'home.banner5_description', 'url' => '/future-ready-csr', - 'style_color' => 'background: linear-gradient(36.92deg, rgb(51, 194, 233) 20.32%, rgb(0, 179, 227) 28.24%);', 'btn_lang' => 'home.learn_more', 'url2' => null, - 'btn2_lang' => null + 'btn2_lang' => null, + 'image' => $defaultImages[0] ?? null, + 'show_countdown' => false, + 'countdown_target' => null, ], - [ + [ 'title' => 'home.banner4_title', 'description' => 'home.banner4_description', 'url' => 'https://codeweek.eu/blog/code-week-digital-educator-awards-2025/', - 'style_color' => 'background-image: linear-gradient(36.92deg, #1C4DA1 20.32%, #0040AE 28.24%);', 'btn_lang' => 'home.view_winners', - 'url2' => 'https://codeweek.eu/blog/code-week-digital-educator-awards-2025/ ', - // 'btn2_lang' => 'home.download_brochure_btn', + 'url2' => 'https://codeweek.eu/blog/code-week-digital-educator-awards-2025/', + 'btn2_lang' => null, + 'image' => $defaultImages[1] ?? null, + 'show_countdown' => false, + 'countdown_target' => null, ], [ 'title' => 'home.banner6_title', 'description' => 'home.banner6_description', 'url' => 'https://airtable.com/appW5W6DJ6CI6SVdH/pagLDrU2NQja9F2vu/form', - 'style_color' => 'background: linear-gradient(36.92deg, rgb(51, 194, 233) 20.32%, rgb(0, 179, 227) 28.24%);', - 'btn_lang' => 'home.register_here', + 'btn_lang' => 'home.register_here', 'url2' => null, - 'btn2_lang' => null + 'btn2_lang' => null, + 'image' => $defaultImages[2] ?? null, + 'show_countdown' => false, + 'countdown_target' => null, ], [ 'title' => 'home.banner3_title', - 'description' => __('home.when_text'), + 'description' => 'home.when_text', 'url' => '/guide', - 'style_color' => 'background-image: linear-gradient(36.92deg, #1C4DA1 20.32%, #0040AE 28.24%);', 'btn_lang' => 'home.get_involved', 'url2' => '/blog/code-week-25-programme/', - // 'btn2_lang' => 'home.download_brochure_btn', - ] + 'btn2_lang' => null, + 'image' => $defaultImages[3] ?? null, + 'show_countdown' => false, + 'countdown_target' => null, + ], ]); - return view('static.home', compact('activities')); } } diff --git a/app/Nova/HomeSlide.php b/app/Nova/HomeSlide.php new file mode 100644 index 000000000..7cef72a37 --- /dev/null +++ b/app/Nova/HomeSlide.php @@ -0,0 +1,77 @@ +sortable(), + Text::make('Title', 'title') + ->rules('required') + ->help('Lang key (e.g. home.banner1_title) or plain text.'), + Textarea::make('Description', 'description') + ->nullable() + ->help('Lang key or plain text.'), + Text::make('Primary button URL', 'url')->rules('required')->hideFromIndex(), + Text::make('Primary button label', 'button_text') + ->rules('required') + ->help('Lang key (e.g. home.learn_more) or plain text.'), + Text::make('Second button URL', 'url2')->nullable()->hideFromIndex(), + Text::make('Second button label', 'button2_text') + ->nullable() + ->help('Leave empty to hide second button.'), + Text::make('Image', 'image') + ->nullable() + ->help('Path e.g. /images/homepage/slide1.png or full URL. Used as slide background.'), + Number::make('Position', 'position') + ->min(0) + ->default(0) + ->help('Lower = first. Order of slides on homepage.'), + Boolean::make('Active', 'active'), + Boolean::make('Show countdown', 'show_countdown') + ->help('Show countdown timer on this slide (usually first slide).'), + DateTime::make('Countdown target', 'countdown_target') + ->nullable() + ->help('Target date/time for countdown (e.g. Code Week start). Only used if Show countdown is on.'), + ]; + } + + public static function indexQuery(NovaRequest $request, $query) + { + return $query->orderBy('position')->orderBy('id'); + } +} diff --git a/database/migrations/2026_02_09_140000_create_home_slides_table.php b/database/migrations/2026_02_09_140000_create_home_slides_table.php new file mode 100644 index 000000000..a5512989e --- /dev/null +++ b/database/migrations/2026_02_09_140000_create_home_slides_table.php @@ -0,0 +1,38 @@ +id(); + $table->string('title'); // Lang key (e.g. home.banner1_title) or plain text + $table->text('description')->nullable(); // Lang key or plain text + $table->string('url'); + $table->string('button_text'); // Primary button label (lang key or text) + $table->string('url2')->nullable(); + $table->string('button2_text')->nullable(); // Second button label (optional) + $table->string('image')->nullable(); // Path e.g. /images/homepage/slide1.png or full URL + $table->unsignedInteger('position')->default(0); + $table->boolean('active')->default(true); + $table->boolean('show_countdown')->default(false); + $table->dateTime('countdown_target')->nullable(); // e.g. 2025-10-14 00:00:00 + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('home_slides'); + } +}; diff --git a/resources/views/static/home.blade.php b/resources/views/static/home.blade.php index c833cdd36..6b352347d 100644 --- a/resources/views/static/home.blade.php +++ b/resources/views/static/home.blade.php @@ -35,33 +35,25 @@ class="flex items-end duration-1000 home-activity codeweek-container-lg md:items style="opacity: {{ $index === 0 ? 1 : 0 }}">