diff --git a/app/Http/Controllers/Bo/AisleController.php b/app/Http/Controllers/Bo/AisleController.php index 21d7193..acbffb9 100644 --- a/app/Http/Controllers/Bo/AisleController.php +++ b/app/Http/Controllers/Bo/AisleController.php @@ -5,9 +5,9 @@ namespace App\Http\Controllers\Bo; use App\Http\Controllers\Controller; use App\Http\Requests\Bo\Aisles\StoreAisleRequest; use App\Http\Requests\Bo\Aisles\UpdateAisleRequest; -use App\Models\Logo; use App\Models\Aisle; use App\Models\Network; +use App\Traits\Controllers\UpdateModelOrder; use App\Traits\Controllers\UpdateModelPublished; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; @@ -15,6 +15,7 @@ use Illuminate\Support\Str; class AisleController extends Controller { + use UpdateModelOrder; use UpdateModelPublished; /** @@ -35,6 +36,13 @@ class AisleController extends Controller { /** @var \Illuminate\Database\Eloquent\Builder $aisleModels */ $aisleModels = Aisle::query()->with('network'); + $networkModels = Network::query()->where('published', true)->get(); + + $selectedNetwork = intval($request->network); + + $aisleModels = $aisleModels->when($selectedNetwork, function ($query) use ($selectedNetwork) { + $query->where('network_id', $selectedNetwork); + }); /** @var string $search Search field */ $search = $request->input('search'); @@ -56,10 +64,9 @@ class AisleController extends Controller /** Custom pagination */ $aisleModels = $this->paginate($aisleModels); - return view('bo.pages.aisles.index', compact('aisleModels', 'search', 'searchFields')); + return view('bo.pages.aisles.index', compact('aisleModels', 'networkModels', 'selectedNetwork', 'search', 'searchFields')); } - /** * Show the form for creating a new resource. * diff --git a/app/Http/Requests/Bo/Aisles/UpdateAisleRequest.php b/app/Http/Requests/Bo/Aisles/UpdateAisleRequest.php index 35af4ff..769e82a 100644 --- a/app/Http/Requests/Bo/Aisles/UpdateAisleRequest.php +++ b/app/Http/Requests/Bo/Aisles/UpdateAisleRequest.php @@ -2,7 +2,6 @@ namespace App\Http\Requests\Bo\Aisles; - class UpdateAisleRequest extends StoreAisleRequest { } diff --git a/app/Http/Requests/Bo/Brands/StoreBrandRequest.php b/app/Http/Requests/Bo/Brands/StoreBrandRequest.php index 9a47751..37b9891 100644 --- a/app/Http/Requests/Bo/Brands/StoreBrandRequest.php +++ b/app/Http/Requests/Bo/Brands/StoreBrandRequest.php @@ -45,7 +45,7 @@ class StoreBrandRequest extends FormRequest 'text' => [ 'required', 'string', - new WysiwygLength(3, 255), + new WysiwygLength(3, 500), ], 'picture_alt' => 'required|string|between:3,255', 'picture_title' => 'required|string|between:3,255', diff --git a/app/Http/Requests/Bo/Networks/StoreNetworkRequest.php b/app/Http/Requests/Bo/Networks/StoreNetworkRequest.php index 40d6d01..1d12848 100644 --- a/app/Http/Requests/Bo/Networks/StoreNetworkRequest.php +++ b/app/Http/Requests/Bo/Networks/StoreNetworkRequest.php @@ -47,6 +47,7 @@ class StoreNetworkRequest extends FormRequest 'subtitle' => 'required|string|between:3,255', 'picture_alt' => 'required|string|between:3,255', 'picture_title' => 'required|string|between:3,255', + 'button_text' => 'required|string|between:3,255', 'published' => 'required|boolean', ]; diff --git a/app/Http/Requests/Bo/UpdateOrderRequest.php b/app/Http/Requests/Bo/UpdateOrderRequest.php new file mode 100644 index 0000000..cc998fb --- /dev/null +++ b/app/Http/Requests/Bo/UpdateOrderRequest.php @@ -0,0 +1,60 @@ +check(); + } + + /** + * Prepare the data for validation. + * + * @return void + */ + protected function prepareForValidation(): void + { + $this->orderableModel = collect($this->route()->parameters()) + ->first(fn($param) => $param instanceof Model); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules(): array + { + return [ + 'order' => [ + 'required', + 'integer', + 'min:1', + sprintf('max:%s', $this->orderableModel->max('order')), + function ($attribute, $value, $fail) { + /** @phpstan-ignore-next-line */ + if ($this->orderableModel && intval($value) === $this->orderableModel->order) { + $fail("La valeur de {$attribute} doit être différente de la valeur actuelle."); + } + }, + ], + ]; + } +} diff --git a/app/Models/Aisle.php b/app/Models/Aisle.php index 1221356..4f0f897 100644 --- a/app/Models/Aisle.php +++ b/app/Models/Aisle.php @@ -4,6 +4,7 @@ namespace App\Models; use App\Lib\Helpers\FileStorageHelper; use App\Traits\Models\ActivityLog; +use App\Traits\Models\Orderable; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Validation\ValidationException; @@ -12,6 +13,7 @@ class Aisle extends Model { use ActivityLog; use HasFactory; + use Orderable; /** * The attributes that are mass assignable. @@ -27,6 +29,7 @@ class Aisle extends Model 'picture_title', 'published', 'network_id', + 'order', ]; /** @@ -46,6 +49,7 @@ class Aisle extends Model protected static function booted(): void { static::creating(function (self $model) { + self::setOrder($model); self::manageFields($model); }); static::updating(function (self $model) { @@ -59,6 +63,7 @@ class Aisle extends Model }); static::deleted(function (self $model) { FileStorageHelper::removeFile($model, 'picture'); + self::setOrder($model, true); }); } @@ -99,6 +104,16 @@ class Aisle extends Model } } + /** + * Group ordering by network_id. + * + * @return string + */ + public function getOrderGroupField(): string + { + return 'network_id'; + } + // * RELATIONS /** diff --git a/app/Models/Network.php b/app/Models/Network.php index 3fd0c6e..95ae75c 100644 --- a/app/Models/Network.php +++ b/app/Models/Network.php @@ -30,6 +30,7 @@ class Network extends Model 'picture', 'picture_alt', 'picture_title', + 'button_text', 'order', 'published', ]; diff --git a/app/Traits/Controllers/UpdateModelOrder.php b/app/Traits/Controllers/UpdateModelOrder.php new file mode 100644 index 0000000..3a283a5 --- /dev/null +++ b/app/Traits/Controllers/UpdateModelOrder.php @@ -0,0 +1,47 @@ +validated('order'); + + return DB::transaction(function () use ($model, $newOrder): RedirectResponse { + /** @phpstan-ignore-next-line */ + $groupField = $model->getOrderGroupField(); + + $query = $model::query() + ->whereKeyNot($model) + /** @phpstan-ignore-next-line */ + ->whereBetween('order', [min($model->order, $newOrder), max($model->order, $newOrder)]); + + if ($groupField) { + $query->where($groupField, $model->{$groupField}); + } + + /** @phpstan-ignore-next-line */ + $model->order < $newOrder ? $query->decrement('order', 1) : $query->increment('order', 1); + + $model->order = $newOrder; + if (!$model->saveOrFail()) { + return back()->with('error', trans('crud.messages.order_not_changed')); + }; + + return back()->with('success', trans('crud.messages.order_changed')); + }); + } +} diff --git a/app/Traits/Models/Orderable.php b/app/Traits/Models/Orderable.php new file mode 100644 index 0000000..b060503 --- /dev/null +++ b/app/Traits/Models/Orderable.php @@ -0,0 +1,56 @@ +getOrderGroupField(); + $maxOrderQuery = $model::query(); + + if ($groupField) { + $maxOrderQuery->where($groupField, $model->{$groupField}); + } + + $maxOrder = \intval($maxOrderQuery->max('order')); + + if ($isDeletion) { + $query = $model::query() + ->whereKeyNot($model) + /** @phpstan-ignore-next-line */ + ->whereBetween('order', [$model->order, $maxOrder]); + + if ($groupField) { + $query->where($groupField, $model->{$groupField}); + } + + $query->decrement('order', 1); + return; + } + + /** @phpstan-ignore-next-line */ + $model->order = $maxOrder + 1; + } +} diff --git a/database/migrations/2026_03_06_113426_alter_networks_table.php b/database/migrations/2026_03_06_113426_alter_networks_table.php new file mode 100644 index 0000000..b4a7c47 --- /dev/null +++ b/database/migrations/2026_03_06_113426_alter_networks_table.php @@ -0,0 +1,30 @@ +string('button_text')->default('On produit')->comment('Button text')->after('picture_title'); + $table->dropUnique(['order']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('networks', function (Blueprint $table) { + $table->unique('order'); + $table->dropColumn('button_text'); + }); + } +}; diff --git a/database/migrations/2026_03_06_125234_alter_aisles_table.php b/database/migrations/2026_03_06_125234_alter_aisles_table.php new file mode 100644 index 0000000..a1063b0 --- /dev/null +++ b/database/migrations/2026_03_06_125234_alter_aisles_table.php @@ -0,0 +1,37 @@ +integer('order')->comment('Position of the aisles')->after('network_id'); + }); + + Network::query()->with('aisles')->get()->map(function ($network) { + $network->aisles->map(function ($aisle, $aisleIndex) { + $aisle->order = $aisleIndex + 1; + $aisle->saveQuietly(); + }); + }); + + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('aisles', function (Blueprint $table) { + $table->dropColumn('order'); + }); + } +}; diff --git a/resources/sass/fo/fo.scss b/resources/sass/fo/fo.scss index a3b749f..6546a50 100644 --- a/resources/sass/fo/fo.scss +++ b/resources/sass/fo/fo.scss @@ -24,6 +24,7 @@ @import "./pages/unveil"; @import "./pages/news"; @import "./pages/hire"; +@import "./pages/networks"; // * PARTIALS @import "./partials/slider"; diff --git a/resources/sass/fo/pages/_networks.scss b/resources/sass/fo/pages/_networks.scss new file mode 100644 index 0000000..51060ad --- /dev/null +++ b/resources/sass/fo/pages/_networks.scss @@ -0,0 +1,30 @@ +@use "sass:color"; + +.swiper-networks { + .swiper-networks-pagination { + bottom: 2rem; + left: 5%; + + @include media-breakpoint-up(lg) { + top: auto; + bottom: .5rem; + left: 0; + width: 100%; + } + } + + .swiper-pagination-bullet { + width: 3rem; + max-width: 10rem; + height: .5rem; + background-color: color.adjust($color-dark, $alpha: -0.8); + + &:hover { + cursor: pointer; + } + } + + .swiper-pagination-bullet-active { + background-color: $color-primary; + } +} diff --git a/resources/ts/bo/utilities/fontawesome.ts b/resources/ts/bo/utilities/fontawesome.ts index a07fa0a..7db27b6 100644 --- a/resources/ts/bo/utilities/fontawesome.ts +++ b/resources/ts/bo/utilities/fontawesome.ts @@ -41,7 +41,8 @@ import { faTrash, faUndoAlt, faWandMagic, - faXmark + faXmark, + faChevronDown, } from "@fortawesome/free-solid-svg-icons"; config.autoReplaceSvg = false; @@ -88,5 +89,6 @@ library.add(faChevronRight); library.add(faCircleXmark); library.add(faCircleNotch); library.add(faSliders); +library.add(faChevronDown); dom.watch(); diff --git a/resources/ts/fo/base/common.ts b/resources/ts/fo/base/common.ts index 4d26a9a..1e2a948 100644 --- a/resources/ts/fo/base/common.ts +++ b/resources/ts/fo/base/common.ts @@ -53,7 +53,7 @@ document.addEventListener("DOMContentLoaded", function () { slidesPerView: 1, loop: true, autoplay: { - delay: 3000, + delay: 20000, disableOnInteraction: false, }, pagination: { diff --git a/resources/views/bo/modules/change-model-order-select.blade.php b/resources/views/bo/modules/change-model-order-select.blade.php new file mode 100644 index 0000000..313e09c --- /dev/null +++ b/resources/views/bo/modules/change-model-order-select.blade.php @@ -0,0 +1,22 @@ +
| {{ $aisleModel->name }} | {{ $aisleModel->network->name }} | @include('bo.modules.change-published-status', [ diff --git a/resources/views/bo/pages/aisles/index.blade.php b/resources/views/bo/pages/aisles/index.blade.php index 986b46b..22a7477 100644 --- a/resources/views/bo/pages/aisles/index.blade.php +++ b/resources/views/bo/pages/aisles/index.blade.php @@ -6,15 +6,37 @@ @section('content')