diff --git a/app/Enums/PermissionEnum.php b/app/Enums/PermissionEnum.php new file mode 100644 index 0000000..ee8a90f --- /dev/null +++ b/app/Enums/PermissionEnum.php @@ -0,0 +1,11 @@ +value); + $client->delete(); return redirect()->route('clients.index'); diff --git a/app/Http/Controllers/ProjectController.php b/app/Http/Controllers/ProjectController.php index 84b1e0d..fa3ce99 100644 --- a/app/Http/Controllers/ProjectController.php +++ b/app/Http/Controllers/ProjectController.php @@ -2,9 +2,11 @@ namespace App\Http\Controllers; +use App\Enums\PermissionEnum; use App\Models\User; use App\Models\Client; use App\Models\Project; +use Illuminate\Support\Facades\Gate; use Illuminate\View\View; use Illuminate\Http\RedirectResponse; use App\Http\Requests\StoreProjectRequest; @@ -51,6 +53,8 @@ public function update(UpdateProjectRequest $request, Project $project): Redirec public function destroy(Project $project): RedirectResponse { + Gate::authorize(PermissionEnum::DELETE_PROJECTS->value); + $project->delete(); return redirect()->route('projects.index'); diff --git a/app/Http/Controllers/TaskController.php b/app/Http/Controllers/TaskController.php new file mode 100644 index 0000000..fd07a86 --- /dev/null +++ b/app/Http/Controllers/TaskController.php @@ -0,0 +1,65 @@ +paginate(20); + + return view('tasks.index', compact('tasks')); + } + + public function create(): View + { + $users = User::select(['id', 'first_name', 'last_name'])->get(); + $clients = Client::select(['id', 'company_name'])->get(); + $projects = Project::select(['id', 'title'])->get(); + + return view('tasks.create', compact('users', 'clients', 'projects')); + } + + public function store(StoreTaskRequest $request): RedirectResponse + { + Task::create($request->validated()); + + return redirect()->route('tasks.index'); + } + + public function edit(Task $task): View + { + $users = User::select(['id', 'first_name', 'last_name'])->get(); + $clients = Client::select(['id', 'company_name'])->get(); + $projects = Project::select(['id', 'title'])->get(); + + return view('tasks.edit', compact('task', 'users', 'clients', 'projects')); + } + + public function update(UpdateTaskRequest $request, Task $task): RedirectResponse + { + $task->update($request->validated()); + + return redirect()->route('tasks.index'); + } + + public function destroy(Task $task): RedirectResponse + { + Gate::authorize(PermissionEnum::DELETE_TASKS->value); + + $task->delete(); + + return redirect()->route('tasks.index'); + } +} diff --git a/app/Http/Requests/StoreTaskRequest.php b/app/Http/Requests/StoreTaskRequest.php new file mode 100644 index 0000000..580f59e --- /dev/null +++ b/app/Http/Requests/StoreTaskRequest.php @@ -0,0 +1,36 @@ +|string> + */ + public function rules(): array + { + return [ + 'title' => ['required'], + 'description' => ['required'], + 'user_id' => ['required', Rule::exists('users', 'id')], + 'client_id' => ['required', Rule::exists('clients', 'id')], + 'project_id' => ['required', Rule::exists('projects', 'id')], + 'deadline_at' => ['required', 'date', 'after:yesterday'], + 'status' => ['required', Rule::enum(TaskStatus::class)], + ]; + } +} diff --git a/app/Http/Requests/UpdateTaskRequest.php b/app/Http/Requests/UpdateTaskRequest.php new file mode 100644 index 0000000..d8483b7 --- /dev/null +++ b/app/Http/Requests/UpdateTaskRequest.php @@ -0,0 +1,36 @@ +|string> + */ + public function rules(): array + { + return [ + 'title' => ['required'], + 'description' => ['required'], + 'user_id' => ['required', Rule::exists('users', 'id')], + 'client_id' => ['required', Rule::exists('clients', 'id')], + 'project_id' => ['required', Rule::exists('projects', 'id')], + 'deadline_at' => ['required', 'date', 'after:yesterday'], + 'status' => ['required', Rule::enum(TaskStatus::class)], + ]; + } +} diff --git a/app/Models/Task.php b/app/Models/Task.php new file mode 100644 index 0000000..3012b8c --- /dev/null +++ b/app/Models/Task.php @@ -0,0 +1,46 @@ + TaskStatus::class, + ]; + } + + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } + + public function client(): BelongsTo + { + return $this->belongsTo(Client::class); + } + + public function project(): BelongsTo + { + return $this->belongsTo(Project::class); + } +} diff --git a/app/Policies/TaskPolicy.php b/app/Policies/TaskPolicy.php new file mode 100644 index 0000000..99b9108 --- /dev/null +++ b/app/Policies/TaskPolicy.php @@ -0,0 +1,66 @@ + + */ +class TaskFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + $users = User::pluck('id'); + $clients = Client::pluck('id'); + $projects = Project::pluck('id'); + + return [ + 'title' => fake()->sentence(), + 'description' => fake()->paragraph(), + 'user_id' => $users->random(), + 'client_id' => $clients->random(), + 'project_id' => $projects->random(), + 'deadline_at' => fake()->dateTimeBetween('+1 month', '+6 month'), + 'status' => fake()->randomElement(TaskStatus::cases())->value, + ]; + } +} diff --git a/database/migrations/2024_09_10_091006_create_tasks_table.php b/database/migrations/2024_09_10_091006_create_tasks_table.php new file mode 100644 index 0000000..b8e217f --- /dev/null +++ b/database/migrations/2024_09_10_091006_create_tasks_table.php @@ -0,0 +1,35 @@ +id(); + $table->string('title'); + $table->text('description'); + $table->foreignId('user_id')->constrained(); + $table->foreignId('client_id')->constrained(); + $table->foreignId('project_id')->constrained(); + $table->date('deadline_at'); + $table->string('status'); + $table->timestamps(); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('tasks'); + } +}; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 4a67273..f42b529 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -14,9 +14,11 @@ public function run(): void { $this->call([ RoleSeeder::class, + PermissionSeeder::class, UserSeeder::class, ClientSeeder::class, ProjectSeeder::class, + TaskSeeder::class, ]); } } diff --git a/database/seeders/PermissionSeeder.php b/database/seeders/PermissionSeeder.php new file mode 100644 index 0000000..7d49c9e --- /dev/null +++ b/database/seeders/PermissionSeeder.php @@ -0,0 +1,32 @@ + PermissionEnum::MANAGE_USERS]); + Permission::create(['name' => PermissionEnum::DELETE_CLIENTS]); + Permission::create(['name' => PermissionEnum::DELETE_PROJECTS]); + Permission::create(['name' => PermissionEnum::DELETE_TASKS]); + + $role = Role::findByName(RoleEnum::ADMIN->value); + $role->givePermissionTo([ + PermissionEnum::MANAGE_USERS, + PermissionEnum::DELETE_CLIENTS, + PermissionEnum::DELETE_PROJECTS, + PermissionEnum::DELETE_TASKS, + ]); + } +} diff --git a/database/seeders/TaskSeeder.php b/database/seeders/TaskSeeder.php new file mode 100644 index 0000000..4c58715 --- /dev/null +++ b/database/seeders/TaskSeeder.php @@ -0,0 +1,18 @@ +create(); + } +} diff --git a/resources/views/clients/index.blade.php b/resources/views/clients/index.blade.php index cdda2e0..179ca8a 100644 --- a/resources/views/clients/index.blade.php +++ b/resources/views/clients/index.blade.php @@ -42,6 +42,7 @@ Edit + @can(\App\Enums\PermissionEnum::DELETE_CLIENTS->value) |
@csrf
+ @endcan @endforeach diff --git a/resources/views/layouts/navigation.blade.php b/resources/views/layouts/navigation.blade.php index 0d1d8d6..718f3a8 100644 --- a/resources/views/layouts/navigation.blade.php +++ b/resources/views/layouts/navigation.blade.php @@ -15,17 +15,20 @@ {{ __('Dashboard') }} - @role(\App\RoleEnum::ADMIN) + @can(\App\Enums\PermissionEnum::MANAGE_USERS->value) {{ __('Users') }} - @endrole + @endcan {{ __('Clients') }} {{ __('Projects') }} + + {{ __('Tasks') }} + @@ -81,17 +84,20 @@ {{ __('Dashboard') }} - @role(\App\RoleEnum::ADMIN) + @can(\App\Enums\PermissionEnum::MANAGE_USERS->value) {{ __('Users') }} - @endrole + @endcan {{ __('Clients') }} {{ __('Projects') }} + + {{ __('Tasks') }} + diff --git a/resources/views/projects/index.blade.php b/resources/views/projects/index.blade.php index 0f86142..4066958 100644 --- a/resources/views/projects/index.blade.php +++ b/resources/views/projects/index.blade.php @@ -54,6 +54,7 @@ Edit + @can(\App\Enums\PermissionEnum::DELETE_PROJECTS->value) |
@csrf
+ @endcan @endforeach diff --git a/resources/views/tasks/create.blade.php b/resources/views/tasks/create.blade.php new file mode 100644 index 0000000..020d0f8 --- /dev/null +++ b/resources/views/tasks/create.blade.php @@ -0,0 +1,94 @@ + + +

+ {{ __('Create Task') }} +

+
+ +
+
+
+
+ +
+ @csrf + + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + + {{ __('Save') }} + +
+ +
+
+
+
+
diff --git a/resources/views/tasks/edit.blade.php b/resources/views/tasks/edit.blade.php new file mode 100644 index 0000000..099faa4 --- /dev/null +++ b/resources/views/tasks/edit.blade.php @@ -0,0 +1,95 @@ + + +

+ {{ __('Edit Task') }} +

+
+ +
+
+
+
+ +
+ @csrf + @method('PUT') + + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + +
+ + + +
+ + + {{ __('Save') }} + +
+ +
+
+
+
+
diff --git a/resources/views/tasks/index.blade.php b/resources/views/tasks/index.blade.php new file mode 100644 index 0000000..8ebc7e0 --- /dev/null +++ b/resources/views/tasks/index.blade.php @@ -0,0 +1,81 @@ + + +

+ {{ __('Tasks') }} +

+
+ +
+
+
+
+ Add new task + + + + + + + + + + + + + + + @foreach($tasks as $task) + + + + + + + + + @endforeach + +
+ Title + + Assigned To + + Client + + Deadline + + Status + +
+ {{ $task->title }} + + {{ $task->user->first_name }} {{ $task->user->last_name }} + + {{ $task->client->company_name }} + + {{ $task->deadline_at }} + + {{ $task->status }} + + Edit + @can(\App\Enums\PermissionEnum::DELETE_TASKS->value) + | +
+ @method('DELETE') + @csrf + +
+ @endcan +
+ +
+ {{ $tasks->links() }} +
+
+
+
+
+
diff --git a/routes/web.php b/routes/web.php index 8d0e88f..896f8f8 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,8 +1,10 @@ group(function () { Route::resource('users', UserController::class) - ->middleware(['role:' . RoleEnum::ADMIN->value]); + ->middleware('can:' . PermissionEnum::MANAGE_USERS->value); Route::resource('clients', ClientController::class); Route::resource('projects', ProjectController::class); + Route::resource('tasks', TaskController::class); Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit'); Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');