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 @@
+
+
+
+
+ {{ $t(item.label) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 @@
+
+
+
+
+ {{ $t(item.label) }}
+
+
+
+
+
+
+
+
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 @@
},
]"
>
+
+
+ {{ $t(item.label) }}
+
+
+
@@ -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([