diff --git a/app/Concerns/PagesSecondaryNavigation.php b/app/Concerns/PagesSecondaryNavigation.php new file mode 100644 index 00000000..00c54d00 --- /dev/null +++ b/app/Concerns/PagesSecondaryNavigation.php @@ -0,0 +1,22 @@ + 'page.subnav.pages', + 'route' => 'admin.pages.index', + ], + [ + 'label' => 'page.subnav.groups', + 'route' => 'admin.page_groups.index', + ], + ]; + } +} diff --git a/app/Contracts/HasSecondaryNavigation.php b/app/Contracts/HasSecondaryNavigation.php new file mode 100644 index 00000000..499c94bd --- /dev/null +++ b/app/Contracts/HasSecondaryNavigation.php @@ -0,0 +1,10 @@ +filter() ->paginate() ), + 'subnav' => $this->getSecondaryNavigation(), ]); } diff --git a/app/Http/Controllers/Admin/PageGroupController.php b/app/Http/Controllers/Admin/PageGroupController.php new file mode 100644 index 00000000..8badd602 --- /dev/null +++ b/app/Http/Controllers/Admin/PageGroupController.php @@ -0,0 +1,109 @@ + new PageGroupCollection( + PageGroup::query() + ->sort( + defaultColumn: 'created_at', + defaultDirection: 'desc' + ) + ->filter() + ->paginate() + ), + 'subnav' => $this->getSecondaryNavigation(), + ]); + } + + public function create(): Response + { + return Inertia::render('Pages/Groups/Edit', [ + 'subnav' => $this->getSecondaryNavigation(), + ])->model(PageGroup::class); + } + + public function store(PageGroupRequest $request): RedirectResponse + { + $attributes = $request->validated(); + + $pageGroup = PageGroup::create($attributes); + + // $pageGroup->items()->rebuildTree(); + + return redirect()->route('admin.page_groups.edit', $pageGroup) + ->with('success', __('page_group.event.created')); + } + + public function edit(PageGroup $pageGroup): Response + { + return Inertia::render('Pages/Groups/Edit', [ + 'resource' => PageGroupResource::make($pageGroup), + 'pages' => Page::query() + ->withDrafted() + ->get(['id', 'title']), + 'subnav' => $this->getSecondaryNavigation(), + ])->model(PageGroup::class); + } + + public function update(PageGroupRequest $request, PageGroup $pageGroup): RedirectResponse + { + $attributes = $request->validated(); + + $pageGroup->update($attributes); + + return redirect()->route('admin.page_groups.edit', $pageGroup) + ->with('success', __('page_group.event.updated')); + } + + public function duplicate(PageGroup $pageGroup): RedirectResponse + { + $duplicate = $pageGroup->duplicate(); + + return redirect()->route('admin.page_groups.edit', $duplicate) + ->with('success', __('page_group.event.duplicated')); + } + + public function destroy(PageGroup $pageGroup): RedirectResponse + { + $pageGroup->delete(); + + return redirect()->route('admin.page_groups.index') + ->with('success', __('page_group.event.deleted')); + } + + public function restore(PageGroup $pageGroup): RedirectResponse + { + $pageGroup->restore(); + + return redirect()->route('admin.page_groups.edit', $pageGroup) + ->with('success', __('page_group.event.restored')); + } + + public function forceDelete(PageGroup $pageGroup): RedirectResponse + { + $pageGroup->forceDelete(); + + return redirect()->route('admin.page_groups.index') + ->with('success', __('page_group.event.deleted')); + } +} diff --git a/app/Http/Requests/Admin/PageGroupRequest.php b/app/Http/Requests/Admin/PageGroupRequest.php new file mode 100644 index 00000000..bea9953c --- /dev/null +++ b/app/Http/Requests/Admin/PageGroupRequest.php @@ -0,0 +1,24 @@ + ['required', 'string', 'max:200'], + ...$this->nestedRules(3), + ]); + } +} diff --git a/app/Http/Resources/Collections/PageGroupCollection.php b/app/Http/Resources/Collections/PageGroupCollection.php new file mode 100644 index 00000000..c9268e9e --- /dev/null +++ b/app/Http/Resources/Collections/PageGroupCollection.php @@ -0,0 +1,17 @@ + 'index', + 'admin.page_groups.edit' => 'edit', + ]; + + protected function index(Request $request): array + { + return [ + 'id' => $this->id, + 'title' => $this->title, + 'pages_count' => $this->pages_count, + ]; + } + + protected function edit(Request $request): array + { + return [ + 'id' => $this->id, + 'title' => $this->getTranslations('title'), + 'items' => $this->items, + ]; + } + + protected function default(Request $request): array + { + $this->withoutPermissions(); + + return [ + 'id' => $this->id, + 'title' => $this->title, + ]; + } +} diff --git a/app/Models/PageGroup.php b/app/Models/PageGroup.php new file mode 100644 index 00000000..718d5f71 --- /dev/null +++ b/app/Models/PageGroup.php @@ -0,0 +1,42 @@ +belongsToMany(PageGroupItem::class) + ->using(PageGroupItem::class); + } +} diff --git a/app/Models/PageGroupItem.php b/app/Models/PageGroupItem.php new file mode 100644 index 00000000..37b20db8 --- /dev/null +++ b/app/Models/PageGroupItem.php @@ -0,0 +1,37 @@ +belongsTo(Page::class); + } +} diff --git a/app/Policies/PageGroupPolicy.php b/app/Policies/PageGroupPolicy.php new file mode 100644 index 00000000..bfff1135 --- /dev/null +++ b/app/Policies/PageGroupPolicy.php @@ -0,0 +1,67 @@ +isAdmin(); + } + + /** + * Determine whether the user can restore the model. + */ + public function restore(User $user, PageGroup $pageGroup): bool + { + return $user->isAdmin(); + } + + /** + * Determine whether the user can permanently delete the model. + */ + public function forceDelete(User $user, PageGroup $pageGroup): bool + { + return $user->isAdmin(); + } +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index c8be33d6..96c616d7 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -50,6 +50,8 @@ public function boot() 'media' => \App\Models\Media::class, 'menu_item' => \App\Models\MenuItem::class, 'page' => \App\Models\Page::class, + 'page_group' => \App\Models\PageGroup::class, + 'page_group_item' => \App\Models\PageGroupItem::class, 'partner' => \App\Models\Partner::class, 'person' => \App\Models\Person::class, 'post_category' => \App\Models\PostCategory::class, diff --git a/database/migrations/2023_08_14_151802_create_page_groups_table.php b/database/migrations/2023_08_14_151802_create_page_groups_table.php new file mode 100644 index 00000000..883ed281 --- /dev/null +++ b/database/migrations/2023_08_14_151802_create_page_groups_table.php @@ -0,0 +1,41 @@ +id(); + $table->timestamps(); + $table->json('title')->nullable()->fulltext(); + }); + + Schema::create('page_group_items', function (Blueprint $table) { + $table->id(); + $table->foreignId('page_group_id') + ->constrained('page_groups') + ->cascadeOnDelete(); + + $table->foreignId('page_id') + ->constrained('pages') + ->cascadeOnDelete(); + + $table->nestedSet(); + }); + + Schema::table('pages', function (Blueprint $table) { + $table->dropNestedSet(); + }); + } +}; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 5ad40857..a4322057 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -4,6 +4,7 @@ namespace Database\Seeders; +use App\Console\Commands\UpdateTranslationsCommand; use App\Models\Decision; use App\Models\DecisionCategory; use App\Models\Form; @@ -17,6 +18,7 @@ use App\Models\User; use App\Services\Features; use Illuminate\Database\Seeder; +use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Cache; class DatabaseSeeder extends Seeder @@ -43,6 +45,8 @@ public function run() ], ]); + Artisan::call(UpdateTranslationsCommand::class); + $images = Media::query() ->whereImages() ->whereIsOriginal() diff --git a/lang/ro.json b/lang/ro.json index af1c7c0f..e1b7b527 100644 --- a/lang/ro.json +++ b/lang/ro.json @@ -373,6 +373,18 @@ "page.event.created": "Pagina a fost creată.", "page.event.updated": "Pagina a fost actualizată.", "page.event.deleted": "Pagina a fost ștersă.", + "page.subnav.pages": "Pagini", + "page.subnav.groups": "Grupuri de pagini", + + "page_group.label": "Grup de pagini|Grupuri de pagini", + "page_group.action.create": "Adaugă grup de pagini", + "page_group.action.edit": "Editează grup de pagini", + "page_group.action.update": "Actualizează grup de pagini", + "page_group.action.delete": "Șterge grup de pagini", + "page_group.empty.title": "Nu am găsit niciun grup de pagini.", + "page_group.event.created": "Grupul de pagini a fost creat.", + "page_group.event.updated": "Grupul de pagini a fost actualizat.", + "page_group.event.deleted": "Grupul de pagini a fost șters.", "pagination.previous": "Înapoi", "pagination.next": "Înainte", diff --git a/resources/js/pages/Pages/Groups/Edit.vue b/resources/js/pages/Pages/Groups/Edit.vue new file mode 100644 index 00000000..1446f3c3 --- /dev/null +++ b/resources/js/pages/Pages/Groups/Edit.vue @@ -0,0 +1,62 @@ + + + diff --git a/resources/js/pages/Pages/Groups/Index.vue b/resources/js/pages/Pages/Groups/Index.vue new file mode 100644 index 00000000..bee3ed52 --- /dev/null +++ b/resources/js/pages/Pages/Groups/Index.vue @@ -0,0 +1,39 @@ + + + diff --git a/resources/js/pages/Pages/Index.vue b/resources/js/pages/Pages/Index.vue index 4aad0f2a..f36cb75d 100644 --- a/resources/js/pages/Pages/Index.vue +++ b/resources/js/pages/Pages/Index.vue @@ -8,6 +8,19 @@ }, ]" > + + @@ -16,6 +29,7 @@ export default { props: { collection: Object, + subnav: Array, }, }; diff --git a/routes/admin.php b/routes/admin.php index 59282ae1..bee9d983 100644 --- a/routes/admin.php +++ b/routes/admin.php @@ -26,21 +26,38 @@ ->middleware('cache.headers:public;max_age=2628000;etag') ->name('i18n'); -Route::group([ - 'prefix' => 'pages', - 'as' => 'pages.', - 'controller' => Admin\PageController::class, -], function () { - Route::get('/', 'index')->name('index'); - Route::get('/create', 'create')->name('create'); - Route::post('/', 'store')->name('store'); - Route::get('/{page}/edit', 'edit')->name('edit'); - Route::post('/{page}/duplicate', 'duplicate')->name('duplicate'); - Route::post('/{page}/preview', 'preview')->name('preview'); - Route::put('/{page}', 'update')->name('update'); - Route::delete('/{page}', 'destroy')->name('destroy'); - Route::put('/{page}/restore', 'restore')->name('restore')->withTrashed(); - Route::delete('/{page}/force', 'forceDelete')->name('forceDelete')->withTrashed(); +Route::prefix('pages')->group(function () { + Route::group([ + 'prefix' => 'groups', + 'as' => 'page_groups.', + 'controller' => Admin\PageGroupController::class, + ], function () { + Route::get('/', 'index')->name('index'); + Route::get('/create', 'create')->name('create'); + Route::post('/', 'store')->name('store'); + Route::get('/{page_group}/edit', 'edit')->name('edit'); + Route::post('/{page_group}/duplicate', 'duplicate')->name('duplicate'); + Route::put('/{page_group}', 'update')->name('update'); + Route::delete('/{page_group}', 'destroy')->name('destroy'); + Route::put('/{page_group}/restore', 'restore')->name('restore')->withTrashed(); + Route::delete('/{page_group}/force', 'forceDelete')->name('forceDelete')->withTrashed(); + }); + + Route::group([ + 'as' => 'pages.', + 'controller' => Admin\PageController::class, + ], function () { + Route::get('/', 'index')->name('index'); + Route::get('/create', 'create')->name('create'); + Route::post('/', 'store')->name('store'); + Route::get('/{page}/edit', 'edit')->name('edit'); + Route::post('/{page}/duplicate', 'duplicate')->name('duplicate'); + Route::post('/{page}/preview', 'preview')->name('preview'); + Route::put('/{page}', 'update')->name('update'); + Route::delete('/{page}', 'destroy')->name('destroy'); + Route::put('/{page}/restore', 'restore')->name('restore')->withTrashed(); + Route::delete('/{page}/force', 'forceDelete')->name('forceDelete')->withTrashed(); + }); }); Route::group([