From 5b74ca5e78c5e99724edcaf2abaadfa89be1cb4e Mon Sep 17 00:00:00 2001 From: Corentin Huet Date: Thu, 5 Mar 2026 15:31:37 +0100 Subject: [PATCH] feat: :sparkles: allow choosing between picto or image and slow down strip animation by 30% --- app/Http/Controllers/Bo/StripController.php | 10 ++ .../Requests/Bo/Strips/StoreStripRequest.php | 47 ++++++--- app/Models/Strip.php | 30 ++++-- .../2026_03_05_121429_alter_stripes_table.php | 34 +++++++ resources/lang/fr/validation.php | 1 + resources/sass/fo/pages/_home.scss | 24 ++--- resources/ts/bo/bo.ts | 4 + resources/ts/bo/pages/strips.ts | 40 ++++++++ .../bo/pages/strips/form-media-part.blade.php | 97 +++++++++++++++++++ .../views/bo/pages/strips/form.blade.php | 18 +--- .../bo/pages/strips/index-list.blade.php | 14 ++- .../fo/partials/strips/strip-item.blade.php | 9 +- 12 files changed, 278 insertions(+), 50 deletions(-) create mode 100644 database/migrations/2026_03_05_121429_alter_stripes_table.php create mode 100644 resources/ts/bo/pages/strips.ts create mode 100644 resources/views/bo/pages/strips/form-media-part.blade.php diff --git a/app/Http/Controllers/Bo/StripController.php b/app/Http/Controllers/Bo/StripController.php index 5d741c9..1a9e22a 100644 --- a/app/Http/Controllers/Bo/StripController.php +++ b/app/Http/Controllers/Bo/StripController.php @@ -9,6 +9,7 @@ use App\Models\News; use App\Models\Strip; use App\Traits\Controllers\UpdateModelPublished; use Illuminate\Http\Request; +use Illuminate\Support\Arr; use Illuminate\Support\Facades\DB; use Illuminate\Support\Str; @@ -131,6 +132,15 @@ class StripController extends Controller if ($request->news_id === null) { $strip->news()->dissociate(); } + + if (!Arr::exists($request->validated(), 'picture')) { + $strip->picture = null; + $strip->picture_alt = null; + $strip->picture_title = null; + } elseif (!Arr::exists($request->validated(), 'picto')) { + $strip->picto = null; + } + if ($strip->saveOrFail()) { return redirect()->route('bo.strips.edit', $strip) ->with('success', trans('crud.messages.has_been_updated', [ diff --git a/app/Http/Requests/Bo/Strips/StoreStripRequest.php b/app/Http/Requests/Bo/Strips/StoreStripRequest.php index 2ecb5ba..0498415 100644 --- a/app/Http/Requests/Bo/Strips/StoreStripRequest.php +++ b/app/Http/Requests/Bo/Strips/StoreStripRequest.php @@ -31,6 +31,9 @@ class StoreStripRequest extends FormRequest $this->merge([ 'published' => $this->boolean('published'), ]); + if ($this->input('picture')) { + $this->mergePicture('picture'); + } } /** @@ -40,11 +43,7 @@ class StoreStripRequest extends FormRequest */ public function rules(): array { - return [ - 'picto' => [ - 'required', - new Enum(\App\Enums\Strips\PictoEnum::class) - ], + $rules = [ 'text' => [ 'required', 'string', @@ -55,6 +54,29 @@ class StoreStripRequest extends FormRequest 'publish_at' => 'nullable|date', 'unpublish_at' => 'nullable|date|after:publish_at', ]; + + if ($this->input('picture')) { + $rules = array_merge( + $rules, + [ + 'picture_alt' => 'required_with|string|between:3,255', + 'picture_title' => 'required_with|string|between:3,255', + ], + $this->pictureRules('picture', 100, 65, 100, 65, true), + ); + } else { + $rules = array_merge( + $rules, + [ + 'picto' => [ + 'required', + new Enum(\App\Enums\Strips\PictoEnum::class) + ], + ], + ); + } + + return $rules; } /** @@ -65,12 +87,15 @@ class StoreStripRequest extends FormRequest public function attributes(): array { return [ - 'picto' => trans('validation.custom.picto'), - 'text' => trans('validation.attributes.content'), - 'news_id' => trans('models.classes.news'), - 'published' => trans('validation.custom.activated'), - 'publish_at' => trans('validation.custom.publish_at'), - 'unpublish_at' => trans('validation.custom.unpublish_at'), + 'picto' => trans('validation.custom.picto'), + 'picture' => trans('validation.custom.picture'), + 'picture_alt' => trans('validation.custom.alt_attribute'), + 'picture_title' => trans('validation.custom.title_attribute'), + 'text' => trans('validation.attributes.content'), + 'news_id' => trans('models.classes.news'), + 'published' => trans('validation.custom.activated'), + 'publish_at' => trans('validation.custom.publish_at'), + 'unpublish_at' => trans('validation.custom.unpublish_at'), ]; } } diff --git a/app/Models/Strip.php b/app/Models/Strip.php index 98215b5..2bc1f68 100644 --- a/app/Models/Strip.php +++ b/app/Models/Strip.php @@ -3,19 +3,23 @@ namespace App\Models; use App\Enums\Strips\PictoEnum; +use App\Lib\Helpers\FileStorageHelper; use App\Traits\Models\ActivityLog; use App\Traits\Models\PlanificatedStatus; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; /** - * @property App\Enums\Strips\PictoEnum $picto Picto. - * @property string $text WYSIWYG restrained (blod and underline only). Text. - * @property boolean $published Publication status. - * @property \Carbon\Carbon|null $publish_at Publication date. - * @property \Carbon\Carbon|null $unpublish_at Unpublication status update date. - * @property-read \Carbon\Carbon $created_at Created date. - * @property-read \Carbon\Carbon $updated_at Updated date. + * @property App\Enums\Strips\PictoEnum $picto Picto. + * @property string $picture Strip picture path. + * @property string $picture_alt Strip picture alt attribute. + * @property string $picture_title Strip picture title attribute. + * @property string $text WYSIWYG restrained (blod and underline only). Text. + * @property boolean $published Publication status. + * @property \Carbon\Carbon|null $publish_at Publication date. + * @property \Carbon\Carbon|null $unpublish_at Unpublication status update date. + * @property-read \Carbon\Carbon $created_at Created date. + * @property-read \Carbon\Carbon $updated_at Updated date. * * @property-read \App\Models\News $news News Belongs to relationship. * @property-read \App\Models\ActivityLog $activityLogs Activities logs One-to-many relationship. @@ -33,6 +37,9 @@ class Strip extends Model */ protected $fillable = [ 'picto', + 'picture', + 'picture_alt', + 'picture_title', 'text', 'published', 'publish_at', @@ -65,6 +72,12 @@ class Strip extends Model static::updating(function (self $model) { self::manageFields($model); }); + static::updated(function (self $model) { + FileStorageHelper::removeOldFile($model, 'picture'); + }); + static::deleted(function (self $model) { + FileStorageHelper::removeFile($model, 'picture'); + }); } // * METHODS @@ -78,6 +91,9 @@ class Strip extends Model private static function manageFields(self $model): void { self::makeStatusSense($model); + if ($model->picture) { + $model->picture = FileStorageHelper::storeFile($model, $model->picture, true); + } } // * RELATIONS diff --git a/database/migrations/2026_03_05_121429_alter_stripes_table.php b/database/migrations/2026_03_05_121429_alter_stripes_table.php new file mode 100644 index 0000000..99cef08 --- /dev/null +++ b/database/migrations/2026_03_05_121429_alter_stripes_table.php @@ -0,0 +1,34 @@ +string('picto')->nullable()->comment('Picto file path')->change(); + $table->string('picture')->nullable()->comment('Picture file path')->after('picto'); + $table->string('picture_title')->nullable()->comment('Picture title')->after('picture'); + $table->string('picture_alt')->nullable()->comment('Picture alt text')->after('picture_title'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('strips', function (Blueprint $table) { + $table->string('picto')->nullable(false)->change(); + $table->dropColumn('picture'); + $table->dropColumn('picture_title'); + $table->dropColumn('picture_alt'); + }); + } +}; diff --git a/resources/lang/fr/validation.php b/resources/lang/fr/validation.php index a25b754..dfb5e68 100644 --- a/resources/lang/fr/validation.php +++ b/resources/lang/fr/validation.php @@ -302,6 +302,7 @@ return [ 'title' => 'titre de l\'ancre', 'link' => 'lien de l\'ancre', ], + 'media' => 'média', 'anchor' => 'ancre' ], ]; diff --git a/resources/sass/fo/pages/_home.scss b/resources/sass/fo/pages/_home.scss index 263ca77..6edd77a 100644 --- a/resources/sass/fo/pages/_home.scss +++ b/resources/sass/fo/pages/_home.scss @@ -134,51 +134,51 @@ @media (prefers-reduced-motion: no-preference) { // Adapt the animation duration according to the number of element + 2 (the 2 duplicated) .moving-strip:has(> :nth-child(3)) { - animation: marquee 8s linear infinite; + animation: marquee 10s linear infinite; } .moving-strip:has(> :nth-child(4)) { - animation: marquee 16s linear infinite; + animation: marquee 21s linear infinite; } .moving-strip:has(> :nth-child(5)) { - animation: marquee 24s linear infinite; + animation: marquee 27s linear infinite; } .moving-strip:has(> :nth-child(6)) { - animation: marquee 32s linear infinite; + animation: marquee 42s linear infinite; } .moving-strip:has(> :nth-child(7)) { - animation: marquee 40s linear infinite; + animation: marquee 52s linear infinite; } .moving-strip:has(> :nth-child(8)) { - animation: marquee 48s linear infinite; + animation: marquee 62s linear infinite; } .moving-strip:has(> :nth-child(9)) { - animation: marquee 56s linear infinite; + animation: marquee 73s linear infinite; } .moving-strip:has(> :nth-child(10)) { - animation: marquee 64s linear infinite; + animation: marquee 83s linear infinite; } .moving-strip:has(> :nth-child(11)) { - animation: marquee 72s linear infinite; + animation: marquee 94s linear infinite; } .moving-strip:has(> :nth-child(12)) { - animation: marquee 80s linear infinite; + animation: marquee 104s linear infinite; } .moving-strip:has(> :nth-child(13)) { - animation: marquee 88s linear infinite; + animation: marquee 114s linear infinite; } .moving-strip:has(> :nth-child(14)) { - animation: marquee 96s linear infinite; + animation: marquee 125s linear infinite; } } } diff --git a/resources/ts/bo/bo.ts b/resources/ts/bo/bo.ts index cd94d30..878e8a6 100644 --- a/resources/ts/bo/bo.ts +++ b/resources/ts/bo/bo.ts @@ -13,3 +13,7 @@ import "./../modules/sweetalert"; // CUSTOM import "./base/common"; +// PAGES +import "./pages/strips"; + + diff --git a/resources/ts/bo/pages/strips.ts b/resources/ts/bo/pages/strips.ts new file mode 100644 index 0000000..a5631f0 --- /dev/null +++ b/resources/ts/bo/pages/strips.ts @@ -0,0 +1,40 @@ +document.addEventListener("DOMContentLoaded", function () { + const stripMedia = document.getElementById("stripItemMedia"); + if (stripMedia === null) { + return; + } + + const mediaPicto = document.getElementById("mediaSelectorPicto") as HTMLInputElement, + mediaImage = document.getElementById("mediaSelectorImage") as HTMLInputElement; + + mediaPicto?.addEventListener("change", function() { + hideFormPart(".stripItemPicture"); + showFormPart(".stripItemPicto"); + }); + + mediaImage?.addEventListener("change", function() { + hideFormPart(".stripItemPicto"); + showFormPart(".stripItemPicture"); + }); +}); + +function hideFormPart(id: string) { + const parts = document.querySelectorAll(id); + parts.forEach(partToHide => { + partToHide.classList.add("d-none"); + const inputs = partToHide.querySelectorAll("input"); + for (const input of inputs) { + input.disabled = true; + } + }); +} +function showFormPart(id: string) { + const parts = document.querySelectorAll(id); + parts.forEach(partToShow => { + partToShow.classList.remove("d-none"); + const inputs = partToShow.querySelectorAll("input"); + for (const input of inputs) { + input.disabled = false; + } + }); +} diff --git a/resources/views/bo/pages/strips/form-media-part.blade.php b/resources/views/bo/pages/strips/form-media-part.blade.php new file mode 100644 index 0000000..2fa2f36 --- /dev/null +++ b/resources/views/bo/pages/strips/form-media-part.blade.php @@ -0,0 +1,97 @@ +
+ {{-- BUTTONS SELECTOR --}} +
+
+ picto))> + + picture))> + +
+
+ + {{-- PICTO --}} +
empty($stripModel->picto), + ])> + @include('bo.partials.inputs.tinyint-enum', [ + 'id' => 'stripPicto', + 'label' => __('Associer un pictogramme'), + 'name' => 'picto', + 'model' => $stripModel ?? null, + 'required' => false, + 'associatedModels' => \App\Enums\Strips\PictoEnum::toArray(), + 'helper' => __('Sélectionnez un pictogramme dans la liste déroulante.'), + ]) +
+ {{-- PICTURE --}} +
empty($stripModel->picture), + ])> + @include('bo.partials.inputs.varchar-picture', [ + 'id' => "stripItemPicture", + 'name' => "picture", + 'width' => 100, + 'height' => 65, + 'model' => $stripModel ?? null, + 'value' => $stripModel->picture ?? null, + 'helper' => __('crud.minicrud.dimensions_must_be', [ + 'width' => 100, + 'height' => 65, + ]), + 'required' => true, + 'preview' => false, + ]) +
+ {{-- PICTURE_ALT --}} +
empty($stripModel->picture), + ])> + @include('bo.partials.inputs.varchar', [ + 'id' => "stripItemPictureAlt", + 'label' => __(':field de l\':model', [ + 'field' => str(__('validation.custom.alt_attribute'))->ucFirst(), + 'model' => trans_choice('models.classes.news', 1), + ]), + 'hasWordCounter' => true, + 'name' => "picture_alt", + 'required' => true, + 'model' => $stripModel ?? null, + 'disabled' => empty($stripModel->picture_alt), + 'value' => $stripModel->picture_alt ?? '', + 'helper' => __('crud.minicrud.picture_alt_helper'), + 'placeholder' => __(':field de l\':model', [ + 'field' => str(__('validation.custom.alt_attribute'))->ucFirst(), + 'model' => trans_choice('models.classes.news', 1), + ]), + ]) +
+ {{-- PICTURE_TITLE --}} +
empty($stripModel->picture), + ])> + @include('bo.partials.inputs.varchar', [ + 'id' => "stripItemPictureTitle", + 'label' => __(':field de l\':model', [ + 'field' => str(__('validation.custom.title_attribute'))->ucFirst(), + 'model' => trans_choice('models.classes.news', 1), + ]), + 'hasWordCounter' => true, + 'name' => "picture_title", + 'required' => true, + 'model' => $stripModel ?? null, + 'disabled' => empty($stripModel->picture_title), + 'value' => $stripModel->picture_title ?? '', + 'helper' => __('crud.minicrud.picture_title_helper'), + 'placeholder' => __(':field de l\':model', [ + 'field' => str(__('validation.custom.title_attribute'))->ucFirst(), + 'model' => trans_choice('models.classes.news', 1), + ]), + ]) +
+
diff --git a/resources/views/bo/pages/strips/form.blade.php b/resources/views/bo/pages/strips/form.blade.php index 8b15161..8bd742f 100644 --- a/resources/views/bo/pages/strips/form.blade.php +++ b/resources/views/bo/pages/strips/form.blade.php @@ -1,25 +1,17 @@
- {{-- PICTO --}} -
+ {{-- MEDIA --}} +
- {{ Str::ucfirst(__('validation.custom.picto')) }} + {{ str(__('validation.custom.media'))->ucFirst()->plural() }} *
- @include('bo.partials.inputs.tinyint-enum', [ - 'id' => 'stripPicto', - 'label' => __('Associer un pictogramme'), - 'name' => 'picto', - 'model' => $stripModel ?? null, - 'required' => true, - 'associatedModels' => \App\Enums\Strips\PictoEnum::toArray(), - 'helper' => __('Sélectionnez un pictogramme dans la liste déroulante.'), - ]) + @include('bo.pages.strips.form-media-part')
{{-- NEWS_ID --}} -
+
{{ Str::ucfirst(trans_choice('models.classes.news', 1)) }}
diff --git a/resources/views/bo/pages/strips/index-list.blade.php b/resources/views/bo/pages/strips/index-list.blade.php index b81d78f..dba0c03 100644 --- a/resources/views/bo/pages/strips/index-list.blade.php +++ b/resources/views/bo/pages/strips/index-list.blade.php @@ -3,21 +3,25 @@ @include('bo.modules.table-col-sorter', [ 'cols' => [ - 'picto' => Str::ucfirst(__('validation.custom.picto')), + 'media' => Str::ucfirst(__('validation.custom.media')), 'text' => Str::ucfirst(__('validation.attributes.content')), 'news_id' => Str::ucfirst(trans_choice('models.classes.news', 1)), 'published' => Str::ucfirst(__('validation.custom.activated')), 'updated_at' => Str::ucfirst(__('validation.attributes.updated_at')), ], + 'ignore' => ['media'], ]) @foreach ($stripModels as $stripModel) - + - @if ($stripModel->picto !== \App\Enums\Strips\PictoEnum::none) - {{ $stripModel->picto->alt() }} + @if ($stripModel->picto && $stripModel->picto !== \App\Enums\Strips\PictoEnum::none) + {{ $stripModel->picto->alt() }} + @elseif ($stripModel->picture) + {{ $stripModel->picture_alt }} @else {{ __('Aucun') }} @endif diff --git a/resources/views/fo/partials/strips/strip-item.blade.php b/resources/views/fo/partials/strips/strip-item.blade.php index e69d5f8..ffb7353 100644 --- a/resources/views/fo/partials/strips/strip-item.blade.php +++ b/resources/views/fo/partials/strips/strip-item.blade.php @@ -1,7 +1,12 @@
- {{ $stripModel->picto->alt() }} + @if ($stripModel->picto) + {{ $stripModel->picto->alt() }} + @else + {{ $stripModel->picture_alt }} + @endif

{!! clean($stripModel->text) !!}