diff --git a/app/Http/Controllers/Bo/ProductController.php b/app/Http/Controllers/Bo/ProductController.php new file mode 100644 index 00000000..631beb80 --- /dev/null +++ b/app/Http/Controllers/Bo/ProductController.php @@ -0,0 +1,42 @@ +authorizeResource(KeyNumber::class); + } + + /** + * Get a paginate resource. + * + * @param \Illuminate\Http\Request $request + * @return \Illuminate\Http\JsonResponse + */ + public function jsonPaginate(Request $request): \Illuminate\Http\JsonResponse + { + /** @var \App\Models\Product $productModels */ + $productModels = $request->targetModel::query() + ->orderBy('label', 'ASC') + ->paginate($request->input('paginate') ?? 10); + return \response()->json($productModels); + } +} diff --git a/app/Http/Controllers/Bo/VideoController.php b/app/Http/Controllers/Bo/VideoController.php index c4954dac..3522ca03 100644 --- a/app/Http/Controllers/Bo/VideoController.php +++ b/app/Http/Controllers/Bo/VideoController.php @@ -79,7 +79,9 @@ class VideoController extends Controller */ public function create(Video $video): \Illuminate\Contracts\View\View { - return view('bo.pages.videos.create', ['videoModel' => $video]); + $productModels = Product::query()->orderBy('label', 'ASC')->paginate(10); + + return view('bo.pages.videos.create', ['videoModel' => $video, 'productModels' => $productModels]); } /** @@ -115,7 +117,7 @@ class VideoController extends Controller */ public function edit(Video $video): \Illuminate\Contracts\View\View { - $productModels = Product::query()->get(); + $productModels = Product::query()->orderBy('label', 'ASC')->paginate(10); return view('bo.pages.videos.edit', ['videoModel' => $video, 'productModels' => $productModels]); } @@ -180,4 +182,16 @@ class VideoController extends Controller { return $this->create($video->replicate()); } + + /** + * Duplicate the specified resource. + * + * @param \App\Models\Video $video + * @return \Illuminate\Http\RedirectResponse + */ + public function changeHomepage(Video $video): \Illuminate\Http\RedirectResponse + { + $video->update(['is_on_homepage' => !$video->getOriginal('is_on_homepage')]); + return redirect()->back()->with('success', trans(__('crud.messages.publish_status_saved'))); + } } diff --git a/app/Http/Requests/Bo/Videos/StoreVideoRequest.php b/app/Http/Requests/Bo/Videos/StoreVideoRequest.php index df9db378..6c5c828c 100644 --- a/app/Http/Requests/Bo/Videos/StoreVideoRequest.php +++ b/app/Http/Requests/Bo/Videos/StoreVideoRequest.php @@ -29,7 +29,8 @@ class StoreVideoRequest extends FormRequest protected function prepareForValidation(): void { $this->merge([ - 'published' => $this->boolean('published') + 'published' => $this->boolean('published'), + 'is_on_homepage' => $this->boolean('is_on_homepage'), ]); } @@ -43,6 +44,7 @@ class StoreVideoRequest extends FormRequest return [ 'title' => 'required|string|between:3,255', 'video_id' => 'required|string|between:3,255', + 'product_id' => 'sometimes|nullable|integer|exists:products,id', 'is_on_homepage' => 'required|boolean', 'published' => 'required|boolean', ]; @@ -57,9 +59,9 @@ class StoreVideoRequest extends FormRequest { return [ 'title' => trans('validation.attributes.title'), - 'video_id' => trans('validation.attributes.source'), - 'is_on_homepage' => trans('validation.attributes.homepage'), - 'published' => trans('validation.attributes.published') + 'video_id' => trans('validation.custom.video_id'), + 'is_on_homepage' => trans('validation.custom.on_homepage'), + 'published' => trans('validation.custom.published') ]; } } diff --git a/app/Models/Video.php b/app/Models/Video.php index f0eb4f65..d5bb9b7b 100644 --- a/app/Models/Video.php +++ b/app/Models/Video.php @@ -9,6 +9,7 @@ use Illuminate\Database\Eloquent\Model; /** * @property string $title Video title. * @property string $video_id Youtube/Vimeo video ID. + * @property \App\Models\Product|null $product_id Associated product. * @property integer $order Change order request. * @property boolean $published Video publish status. * @property boolean $is_on_homepage Is the video on the home page? @@ -30,11 +31,22 @@ class Video extends Model protected $fillable = [ 'title', 'video_id', + 'product_id', 'is_on_homepage', 'order', 'published', ]; + /** + * The attributes that should be cast. + * + * @var array + */ + protected $casts = [ + 'published' => 'boolean', + 'is_on_homepage' => 'boolean', + ]; + /** * Perform any actions required after the model boots. * diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php index 753089a0..50df2738 100644 --- a/app/Providers/RouteServiceProvider.php +++ b/app/Providers/RouteServiceProvider.php @@ -14,6 +14,7 @@ use App\Models\Range; use App\Models\Submeat; use App\Models\Subrange; use App\Models\User; +use App\Models\Video; use Illuminate\Cache\RateLimiting\Limit; use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider; use Illuminate\Http\Request; @@ -64,6 +65,7 @@ class RouteServiceProvider extends ServiceProvider Route::model('submeat', Submeat::class); Route::model('approach', Approach::class); Route::model('consumption_moment', ConsumptionMoment::class); + Route::model('video', Video::class); } /** diff --git a/database/migrations/2024_04_29_164213_create_video_table.php b/database/migrations/2024_04_29_164213_create_video_table.php index bb7559a2..6da0ba0d 100644 --- a/database/migrations/2024_04_29_164213_create_video_table.php +++ b/database/migrations/2024_04_29_164213_create_video_table.php @@ -18,7 +18,7 @@ return new class extends Migration $table->string('title')->comment('Video title.'); $table->string('video_id')->comment('The video ID.'); $table->boolean('is_on_homepage')->comment('Is display on homepage ?'); - $table->foreignId('product_id')->comment('Associated product')->constrained() + $table->foreignId('product_id')->nullable()->comment('Associated product')->constrained() ->onUpdate('no action')->onDelete('cascade'); $table->integer('order')->comment('Video order.'); $table->boolean('published')->comment('Video publish status.'); diff --git a/resources/ts/bo/utilities/fontawesome.ts b/resources/ts/bo/utilities/fontawesome.ts index affbe3d1..1e108b35 100644 --- a/resources/ts/bo/utilities/fontawesome.ts +++ b/resources/ts/bo/utilities/fontawesome.ts @@ -32,6 +32,7 @@ import { faEye, faEyeSlash, faFile, + faFilm, faFolderOpen, faHouse, faKey, @@ -109,5 +110,6 @@ library.add(faArrowRightArrowLeft); library.add(faBuildingWheat); library.add(faClock); library.add(faDrumstickBite); +library.add(faFilm); dom.watch(); diff --git a/resources/views/bo/layouts/sidemenu.blade.php b/resources/views/bo/layouts/sidemenu.blade.php index a4eac2c8..d3a342ee 100644 --- a/resources/views/bo/layouts/sidemenu.blade.php +++ b/resources/views/bo/layouts/sidemenu.blade.php @@ -186,7 +186,7 @@ 'btn btn-primary w-100 text-start mt-1', 'fw-bold active' => request()->routeIs('bo.videos.*'), ])> -  {{ str(__('models.classes.video'))->ucFirst() }} +  {{ str(__('models.classes.video'))->ucFirst() }} diff --git a/resources/views/bo/pages/videos/form.blade.php b/resources/views/bo/pages/videos/form.blade.php index 15586a45..54bd7011 100644 --- a/resources/views/bo/pages/videos/form.blade.php +++ b/resources/views/bo/pages/videos/form.blade.php @@ -41,20 +41,16 @@ @include('bo.partials.inputs.varchar', [ 'id' => 'videoId', 'label' => str( - __(':field :inter:model', [ - 'field' => __('validation.custom.video_id'), - 'inter' => __('validation.custom.of_the.female'), - 'model' => __('models.classes.video'), + __(':field', [ + 'field' => __('validation.custom.video_id') ]))->ucfirst(), 'hasWordCounter' => true, 'name' => 'video_id', 'required' => true, 'model' => $videoModel ?? null, 'helper' => __('validation.between.string', [ - 'attribute' => __(':field :inter:model', [ - 'field' => __('validation.custom.video_id'), - 'inter' => __('validation.custom.of_the.female'), - 'model' => __('models.classes.video'), + 'attribute' => __(':field', [ + 'field' => __('validation.custom.video_id') ]), 'min' => 3, 'max' => 255, @@ -71,21 +67,26 @@ {{ str(__('models.classes.product'))->ucfirst() }}
- @include('bo.partials.inputs.bigint-foreign-id', [ - 'id' => 'videoProductId', - 'label' => __(':field :inter:model', [ - 'field' => str(__('models.classes.product'))->ucfirst(), - 'inter' => __('validation.custom.of_the.female'), - 'model' => __('models.classes.video'), + @include('bo.partials.inputs.search-belongs-to-dropdown', [ + 'id' => 'videoProduct', + 'label' => __(':model associée', [ + 'model' => str(__('models.classes.product'))->ucFirst(), ]), 'name' => 'product_id', - 'required' => true, - 'model' => $videoModel ?? null, - 'associatedModels' => $productModels ?? null, - 'foreignLabel' => 'label', - 'helper' => __('crud.minicrud.associated_entity', [ - 'entity' => __('models.classes.product'), + 'targetClass' => 'App\Models\Product', + 'modelListPaginate' => $productModels ?? [], + 'modelSelected' => $videoModel->product, + 'routeName' => 'bo.products.json-paginate', + 'multiple' => false, + 'fieldName' => 'label', + 'required' => false, + 'disabled' => false, + 'placeholder' => __('Rechercher un :model', [ + 'model' => __('models.classes.product'), ]), + 'helper' => __( + 'Sélectionner un :model qui sera associée parmis la liste (un seul et unique :model doit être choisi).', + ['model' => __('models.classes.product')]), ])
diff --git a/resources/views/bo/pages/videos/index-list.blade.php b/resources/views/bo/pages/videos/index-list.blade.php index 43f23f40..44958682 100644 --- a/resources/views/bo/pages/videos/index-list.blade.php +++ b/resources/views/bo/pages/videos/index-list.blade.php @@ -5,28 +5,43 @@ 'cols' => [ 'title' => str(__('validation.attributes.title'))->ucfirst(), 'is_on_homepage' => str(__('validation.custom.on_homepage'))->ucfirst(), - 'order' => str(__('validation.custom.order'))->ucfirst(), 'published' => str(__('validation.custom.activated'))->ucfirst(), 'updated_at' => str(__('validation.attributes.updated_at'))->ucfirst(), + 'order' => str(__('validation.custom.order'))->ucfirst(), ], ]) @foreach ($videoModels as $videoModel) - {{ $videoModel->title }} - {{-- FIXME: modifier l'affichage de bool is on homepage, peut etre voir pour faire comme publish --}} - {{ $videoModel->is_on_homepage ? 'oui' : 'non' }} @php $routeName = request()->route()->getName(); @endphp - @if (empty(request()->search) && session()->get("$routeName.sort_col") === 'order') - @include('bo.modules.change-model-order', [ - 'routeName' => 'videos', - 'model' => $videoModel, - 'models' => $videoModels, - ]) - @endif + {{ $videoModel->title }} + + @can('changePublished', $videoModel) +
+ @csrf + @method('PATCH') + +
+ @else + + + + @endcan + @include('bo.modules.change-published-status', [ 'routeName' => 'videos', 'model' => $videoModel, @@ -34,6 +49,13 @@ {{ $videoModel->updated_at->isoFormat('LLLL') }} + @if (empty(request()->search) && session()->get("$routeName.sort_col") === 'order') + @include('bo.modules.change-model-order', [ + 'routeName' => 'videos', + 'model' => $videoModel, + 'models' => $videoModels, + ]) + @endif @canAny(['view', 'duplicate', 'update', 'delete'], $videoModel)
'MENU_NODE_DATA']) }}'; window.__SYSTEM._routes['bo.approaches.json-paginate'] = '{{ route('bo.approaches.json-paginate') }}'; + window.__SYSTEM._routes['bo.products.json-paginate'] = '{{ route('bo.products.json-paginate') }}'; diff --git a/routes/breadcrumbs-bo.php b/routes/breadcrumbs-bo.php index fe27effb..03fe91cb 100644 --- a/routes/breadcrumbs-bo.php +++ b/routes/breadcrumbs-bo.php @@ -16,6 +16,7 @@ use App\Models\StaticPage; use App\Models\Submeat; use App\Models\Subrange; use App\Models\User; +use App\Models\Video; use Diglactic\Breadcrumbs\Breadcrumbs; use Diglactic\Breadcrumbs\Generator; use Illuminate\Support\Str; @@ -364,3 +365,27 @@ Breadcrumbs::for('bo.consumption_moments.duplicate', function (Generator $trail) $trail->parent('bo.consumption_moments.index'); $trail->push(Str::ucfirst(trans('crud.actions.duplicate'))); }); + +// * VIDEOS +Breadcrumbs::for('bo.videos.index', function (Generator $trail) { + $trail->push( + Str::of(trans('models.classes.video'))->plural()->ucfirst(), + route('bo.videos.index') + ); +}); +Breadcrumbs::for('bo.videos.show', function (Generator $trail, Video $video) { + $trail->parent('bo.videos.index'); + $trail->push(Str::ucfirst(trans('crud.actions.view')), route('bo.videos.show', $video)); +}); +Breadcrumbs::for('bo.videos.create', function (Generator $trail) { + $trail->parent('bo.videos.index'); + $trail->push(Str::ucfirst(trans('crud.actions.create')), route('bo.videos.create')); +}); +Breadcrumbs::for('bo.videos.edit', function (Generator $trail, Video $video) { + $trail->parent('bo.videos.index'); + $trail->push(Str::ucfirst(trans('crud.actions.edit')), route('bo.videos.edit', $video)); +}); +Breadcrumbs::for('bo.videos.duplicate', function (Generator $trail) { + $trail->parent('bo.videos.index'); + $trail->push(Str::ucfirst(trans('crud.actions.duplicate'))); +}); diff --git a/routes/web-bo.php b/routes/web-bo.php index 0648f55c..a0e3246e 100644 --- a/routes/web-bo.php +++ b/routes/web-bo.php @@ -11,6 +11,7 @@ use App\Http\Controllers\Bo\KeyNumberController; use App\Http\Controllers\Bo\MainSlideController; use App\Http\Controllers\Bo\MenuNodeDataController; use App\Http\Controllers\Bo\MeatController; +use App\Http\Controllers\Bo\ProductController; use App\Http\Controllers\Bo\ProfessionController; use App\Http\Controllers\Bo\RangeController; use App\Http\Controllers\Bo\StaticPageController; @@ -238,12 +239,17 @@ Route::prefix('bo') Route::patch('/videos/{video}/change-published', [ VideoController::class, 'changePublished' ])->name('videos.change-published'); + Route::patch('/videos/{video}/change-homepage', [ + VideoController::class, 'changeHomepage' + ])->name('videos.change-homepage'); Route::patch('/videos/{video}/change-order/{direction}', [ VideoController::class, 'changeOrder' ])->where('direction', 'up|down')->name('videos.change-order'); Route::get('/videos/{video}/duplicate', [ VideoController::class, 'duplicate' ])->name('videos.duplicate'); + Route::get('/products/json/paginate', [ProductController::class, 'jsonPaginate']) + ->name('products.json-paginate'); Route::middleware('can:isConceptor')->group(function () { // * ACTIVITY LOGS.