diff --git a/app/Containers/Dashboard/Actions/Schedules/BulkDeleteSchedulesAction.php b/app/Containers/Dashboard/Actions/Schedules/BulkDeleteSchedulesAction.php new file mode 100644 index 0000000..8632291 --- /dev/null +++ b/app/Containers/Dashboard/Actions/Schedules/BulkDeleteSchedulesAction.php @@ -0,0 +1,17 @@ +bulkDeleteSchedulesTask->run($ids); + } +} diff --git a/app/Containers/Dashboard/Tasks/Schedules/BulkDeleteSchedulesTask.php b/app/Containers/Dashboard/Tasks/Schedules/BulkDeleteSchedulesTask.php new file mode 100644 index 0000000..f0a8e7a --- /dev/null +++ b/app/Containers/Dashboard/Tasks/Schedules/BulkDeleteSchedulesTask.php @@ -0,0 +1,13 @@ +delete(); + } +} diff --git a/app/Containers/Dashboard/UI/WEB/Controllers/ScheduleController.php b/app/Containers/Dashboard/UI/WEB/Controllers/ScheduleController.php index 4e6fcf8..e3f2dbf 100644 --- a/app/Containers/Dashboard/UI/WEB/Controllers/ScheduleController.php +++ b/app/Containers/Dashboard/UI/WEB/Controllers/ScheduleController.php @@ -5,6 +5,7 @@ namespace App\Containers\Dashboard\UI\WEB\Controllers; use App\Containers\Dashboard\Actions\Schedules\CreateScheduleAction; use App\Containers\Dashboard\Actions\Schedules\UpdateScheduleAction; use App\Containers\Dashboard\Actions\Schedules\DeleteScheduleAction; +use App\Containers\Dashboard\Actions\Schedules\BulkDeleteSchedulesAction; use App\Containers\Dashboard\Actions\Schedules\ListSchedulesAction; use App\Containers\Dashboard\UI\WEB\Requests\StoreScheduleRequest; use App\Containers\Dashboard\UI\WEB\Requests\UpdateScheduleRequest; @@ -23,6 +24,7 @@ class ScheduleController extends Controller private readonly CreateScheduleAction $createScheduleAction, private readonly UpdateScheduleAction $updateScheduleAction, private readonly DeleteScheduleAction $deleteScheduleAction, + private readonly BulkDeleteSchedulesAction $bulkDeleteSchedulesAction, ) {} /** @@ -150,4 +152,22 @@ class ScheduleController extends Controller ->with('error', 'Ошибка при удалении расписания: ' . $e->getMessage()); } } + + /** + * Массовое удаление расписаний + */ + public function bulkDestroy(Request $request): RedirectResponse + { + $request->validate(['ids' => 'required|array', 'ids.*' => 'integer|exists:schedules,id']); + + try { + $count = $this->bulkDeleteSchedulesAction->run($request->ids); + + return redirect()->back() + ->with('success', "Удалено {$count} расписаний"); + } catch (\Exception $e) { + return back() + ->with('error', 'Ошибка при удалении: ' . $e->getMessage()); + } + } } diff --git a/app/Containers/Dashboard/UI/WEB/Routes/web.php b/app/Containers/Dashboard/UI/WEB/Routes/web.php index 96c5b65..6e44d6c 100755 --- a/app/Containers/Dashboard/UI/WEB/Routes/web.php +++ b/app/Containers/Dashboard/UI/WEB/Routes/web.php @@ -198,6 +198,7 @@ Route::middleware(['access-check', 'dashboard.auth', 'dashboard.permission'])->g Route::get('/', [ScheduleController::class, 'index'])->name('index'); Route::get('/create', [ScheduleController::class, 'create'])->name('create'); Route::post('/', [ScheduleController::class, 'store'])->name('store'); + Route::delete('/bulk-destroy', [ScheduleController::class, 'bulkDestroy'])->name('bulk-destroy'); Route::get('/{schedule}/edit', [ScheduleController::class, 'edit'])->name('edit'); Route::put('/{schedule}', [ScheduleController::class, 'update'])->name('update'); Route::delete('/{schedule}', [ScheduleController::class, 'destroy'])->name('destroy'); diff --git a/app/Ship/Middleware/CheckDashboardPermission.php b/app/Ship/Middleware/CheckDashboardPermission.php index dae4a26..5280124 100644 --- a/app/Ship/Middleware/CheckDashboardPermission.php +++ b/app/Ship/Middleware/CheckDashboardPermission.php @@ -4,6 +4,7 @@ namespace App\Ship\Middleware; use Closure; use Illuminate\Http\Request; +use Illuminate\Support\Str; use Symfony\Component\HttpFoundation\Response; class CheckDashboardPermission @@ -72,7 +73,8 @@ class CheckDashboardPermission // Convert: additional-educations → additional_education // Convert: admission-campaigns → admission_campaign // Convert: main-sections → main_section - $resource = preg_replace('/s$/', '', $resource); // strip trailing 's' + // Convert: faculties → faculty, categories → category + $resource = Str::singular($resource); $resource = str_replace('-', '_', $resource); return 'view_any_' . $resource; diff --git a/resources/js/Pages/Dashboard/Schedules/Index.vue b/resources/js/Pages/Dashboard/Schedules/Index.vue index c5edea4..24f1b56 100644 --- a/resources/js/Pages/Dashboard/Schedules/Index.vue +++ b/resources/js/Pages/Dashboard/Schedules/Index.vue @@ -25,6 +25,34 @@ + + +
+
+ {{ selectedSchedules.length }} выбрано + +
+
+ + +
+
+
+ + + + Название @@ -113,7 +150,16 @@ v-for="schedule in schedules.data" :key="schedule.id" class="group hover:bg-muted-hover/50 transition-all duration-200" + :class="{ 'bg-primary/5': selectedSchedules.includes(schedule.id) }" > + + +
@@ -178,7 +224,7 @@ 0 && this.selectedSchedules.length === this.schedules.data.length; + }, + isIndeterminate() { + return this.selectedSchedules.length > 0 && this.selectedSchedules.length < this.schedules.data.length; + }, + }, + mounted() { this.SET_DOCUMENT_TITLE('Расписание'); }, @@ -335,6 +392,39 @@ export default { }); }, + toggleSelectAll(event) { + if (event.target.checked) { + this.selectedSchedules = [...new Set([...this.selectedSchedules, ...this.schedules.data.map(s => s.id)])]; + } else { + const pageIds = new Set(this.schedules.data.map(s => s.id)); + this.selectedSchedules = this.selectedSchedules.filter(id => !pageIds.has(id)); + } + }, + + selectAllOnPage() { + this.selectedSchedules = [...new Set([...this.selectedSchedules, ...this.schedules.data.map(s => s.id)])]; + }, + + clearSelection() { + this.selectedSchedules = []; + }, + + bulkDelete() { + if (!confirm(`Удалить ${this.selectedSchedules.length} расписаний?`)) return; + this.bulkProcessing = true; + this.$inertia.delete(route('dashboard.schedules.bulk-destroy'), { + data: { ids: this.selectedSchedules }, + preserveScroll: true, + onSuccess: () => { + this.selectedSchedules = []; + this.bulkProcessing = false; + }, + onError: () => { + this.bulkProcessing = false; + }, + }); + }, + refreshPage() { this.$inertia.get(route('dashboard.schedules.index'), { search: this.searchQuery,