From 4d8d5c551d13ede9497b7f724e54d940fbb88764 Mon Sep 17 00:00:00 2001 From: bernardhanna Date: Mon, 9 Feb 2026 15:48:27 +0000 Subject: [PATCH] make adding slides dynamic --- app/HomeSlide.php | 36 ++++++++- app/Http/Controllers/HomeController.php | 11 +-- app/Nova/HomeSlide.php | 30 +++++--- ...160000_add_translations_to_home_slides.php | 73 +++++++++++++++++++ resources/views/static/home.blade.php | 4 +- 5 files changed, 131 insertions(+), 23 deletions(-) create mode 100644 database/migrations/2026_02_09_160000_add_translations_to_home_slides.php diff --git a/app/HomeSlide.php b/app/HomeSlide.php index 5cc5c8a8d..64476fea1 100644 --- a/app/HomeSlide.php +++ b/app/HomeSlide.php @@ -7,13 +7,13 @@ class HomeSlide extends Model { protected $fillable = [ - 'title', - 'description', + 'title_translations', + 'description_translations', 'url', - 'button_text', + 'button_text_translations', 'open_primary_new_tab', 'url2', - 'button2_text', + 'button2_text_translations', 'open_second_new_tab', 'image', 'position', @@ -23,6 +23,10 @@ class HomeSlide extends Model ]; protected $casts = [ + 'title_translations' => 'array', + 'description_translations' => 'array', + 'button_text_translations' => 'array', + 'button2_text_translations' => 'array', 'active' => 'boolean', 'show_countdown' => 'boolean', 'countdown_target' => 'datetime', @@ -31,6 +35,30 @@ class HomeSlide extends Model 'open_second_new_tab' => 'boolean', ]; + /** + * Get translated string for a key (title_translations, description_translations, etc.). + * Uses current app locale with fallback to 'en' then first available. + */ + public function getTranslation(string $key, ?string $locale = null): string + { + $locale = $locale ?? app()->getLocale(); + $translations = $this->getAttribute($key); + if (! is_array($translations)) { + return ''; + } + $value = $translations[$locale] ?? $translations['en'] ?? null; + if ($value !== null && $value !== '') { + return (string) $value; + } + return (string) (array_values($translations)[0] ?? ''); + } + + /** For Nova index/detail: show title in current locale. */ + public function getTitleAttribute(): string + { + return $this->getTranslation('title_translations'); + } + public function scopeActive($query) { return $query->where('active', true); diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php index 9a37bd753..79f60a0e6 100644 --- a/app/Http/Controllers/HomeController.php +++ b/app/Http/Controllers/HomeController.php @@ -24,15 +24,16 @@ private function getActivities() if (Schema::hasTable('home_slides')) { $slides = HomeSlide::active()->ordered()->get(); if ($slides->isNotEmpty()) { - return $slides->map(function (HomeSlide $slide) { + $locale = app()->getLocale(); + return $slides->map(function (HomeSlide $slide) use ($locale) { return [ - 'title' => $slide->title, - 'description' => $slide->description ?? '', + 'title' => $slide->getTranslation('title_translations', $locale), + 'description' => $slide->getTranslation('description_translations', $locale), 'url' => $slide->url, - 'btn_lang' => $slide->button_text, + 'btn_lang' => $slide->getTranslation('button_text_translations', $locale), 'open_primary_new_tab' => $slide->open_primary_new_tab ?? false, 'url2' => $slide->url2, - 'btn2_lang' => $slide->button2_text, + 'btn2_lang' => $slide->getTranslation('button2_text_translations', $locale), 'open_second_new_tab' => $slide->open_second_new_tab ?? false, 'image' => $slide->image_url, 'show_countdown' => $slide->show_countdown, diff --git a/app/Nova/HomeSlide.php b/app/Nova/HomeSlide.php index d70df9bbf..413d6707e 100644 --- a/app/Nova/HomeSlide.php +++ b/app/Nova/HomeSlide.php @@ -6,9 +6,9 @@ use Laravel\Nova\Fields\Boolean; use Laravel\Nova\Fields\DateTime; use Laravel\Nova\Fields\ID; +use Laravel\Nova\Fields\KeyValue; use Laravel\Nova\Fields\Number; use Laravel\Nova\Fields\Text; -use Laravel\Nova\Fields\Textarea; use Laravel\Nova\Http\Requests\NovaRequest; class HomeSlide extends Resource @@ -19,7 +19,7 @@ class HomeSlide extends Resource public static $title = 'title'; - public static $search = ['title', 'description']; + public static $search = []; public static function label() { @@ -40,22 +40,28 @@ public function fields(Request $request): array { return [ ID::make()->sortable(), - Text::make('Title', 'title') + KeyValue::make('Title', 'title_translations') + ->keyLabel('Locale (e.g. en, fr, de)') + ->valueLabel('Text') ->rules('required') - ->help('Use a lang key (e.g. home.banner1_title) for translated content, or type plain text.'), - Textarea::make('Description', 'description') - ->nullable() - ->help('Use a lang key for translated content, or plain text.'), + ->help('Add one row per language. Key = locale code (en, fr, de, …), Value = translated title.'), + KeyValue::make('Description', 'description_translations') + ->keyLabel('Locale') + ->valueLabel('Text') + ->help('Add one row per language.'), Text::make('Primary button URL', 'url')->rules('required')->hideFromIndex(), - Text::make('Primary button label', 'button_text') + KeyValue::make('Primary button label', 'button_text_translations') + ->keyLabel('Locale') + ->valueLabel('Text') ->rules('required') - ->help('Lang key (e.g. home.learn_more) for translation, or plain text.'), + ->help('Add one row per language.'), Boolean::make('Open primary link in new tab', 'open_primary_new_tab') ->help('Open the primary button link in a new window/tab.'), Text::make('Second button URL', 'url2')->nullable()->hideFromIndex(), - Text::make('Second button label', 'button2_text') - ->nullable() - ->help('Leave empty to hide second button. Use lang key for translation.'), + KeyValue::make('Second button label', 'button2_text_translations') + ->keyLabel('Locale') + ->valueLabel('Text') + ->help('Add one row per language. Leave empty to hide second button.'), Boolean::make('Open second link in new tab', 'open_second_new_tab') ->help('Open the second button link in a new window/tab.'), Text::make('Image', 'image') diff --git a/database/migrations/2026_02_09_160000_add_translations_to_home_slides.php b/database/migrations/2026_02_09_160000_add_translations_to_home_slides.php new file mode 100644 index 000000000..e91292e71 --- /dev/null +++ b/database/migrations/2026_02_09_160000_add_translations_to_home_slides.php @@ -0,0 +1,73 @@ +json('title_translations')->nullable()->after('id'); + $table->json('description_translations')->nullable()->after('title_translations'); + $table->json('button_text_translations')->nullable()->after('url'); + $table->json('button2_text_translations')->nullable()->after('button2_text'); + }); + + $slides = DB::table('home_slides')->get(); + foreach ($slides as $row) { + DB::table('home_slides')->where('id', $row->id)->update([ + 'title_translations' => json_encode(['en' => $row->title ?? '']), + 'description_translations' => json_encode(['en' => $row->description ?? '']), + 'button_text_translations' => json_encode(['en' => $row->button_text ?? '']), + 'button2_text_translations' => json_encode(['en' => $row->button2_text ?? '']), + ]); + } + + Schema::table('home_slides', function (Blueprint $table) { + $table->dropColumn(['title', 'description', 'button_text', 'button2_text']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('home_slides', function (Blueprint $table) { + $table->string('title')->nullable()->after('id'); + $table->text('description')->nullable()->after('title'); + $table->string('button_text')->nullable()->after('url'); + $table->string('button2_text')->nullable()->after('open_second_new_tab'); + }); + + $slides = DB::table('home_slides')->get(); + foreach ($slides as $row) { + $titleTrans = $row->title_translations ? json_decode($row->title_translations, true) : []; + $descTrans = $row->description_translations ? json_decode($row->description_translations, true) : []; + $btnTrans = $row->button_text_translations ? json_decode($row->button_text_translations, true) : []; + $btn2Trans = $row->button2_text_translations ? json_decode($row->button2_text_translations, true) : []; + DB::table('home_slides')->where('id', $row->id)->update([ + 'title' => $titleTrans['en'] ?? '', + 'description' => $descTrans['en'] ?? null, + 'button_text' => $btnTrans['en'] ?? '', + 'button2_text' => $btn2Trans['en'] ?? null, + ]); + } + + Schema::table('home_slides', function (Blueprint $table) { + $table->dropColumn([ + 'title_translations', + 'description_translations', + 'button_text_translations', + 'button2_text_translations', + ]); + }); + } +}; diff --git a/resources/views/static/home.blade.php b/resources/views/static/home.blade.php index 59db15a21..a3fbb1923 100644 --- a/resources/views/static/home.blade.php +++ b/resources/views/static/home.blade.php @@ -94,7 +94,7 @@ class="text-[#1C4DA1] text-[30px] md:text-[60px] leading-9 md:leading-[72px] fon

- {{ strip_tags(__($activity['description'] ?? '')) }} + {!! strip_tags(__($activity['description'] ?? '')) !!}