Tasks CRUD

This commit is contained in:
PovilasKorop 2024-09-12 20:11:53 +03:00
parent 282ef63cf2
commit f112cf2ea5
21 changed files with 693 additions and 5 deletions

View File

@ -0,0 +1,11 @@
<?php
namespace App\Enums;
enum PermissionEnum: string
{
case MANAGE_USERS = 'manage_users';
case DELETE_CLIENTS = 'delete_clients';
case DELETE_PROJECTS = 'delete_projects';
case DELETE_TASKS = 'delete_tasks';
}

13
app/Enums/TaskStatus.php Normal file
View File

@ -0,0 +1,13 @@
<?php
namespace App\Enums;
enum TaskStatus: string
{
case OPEN = 'open';
case IN_PROGRESS = 'in progress';
case PENDING = 'pending';
case WAITING_CLIENT = 'waiting client';
case BLOCKED = 'blocked';
case CLOSED = 'closed';
}

View File

@ -2,7 +2,9 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Enums\PermissionEnum;
use App\Models\Client; use App\Models\Client;
use Illuminate\Support\Facades\Gate;
use Illuminate\View\View; use Illuminate\View\View;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use App\Http\Requests\StoreClientRequest; use App\Http\Requests\StoreClientRequest;
@ -43,6 +45,8 @@ public function update(UpdateClientRequest $request, Client $client): RedirectRe
public function destroy(Client $client): RedirectResponse public function destroy(Client $client): RedirectResponse
{ {
Gate::authorize(PermissionEnum::DELETE_CLIENTS->value);
$client->delete(); $client->delete();
return redirect()->route('clients.index'); return redirect()->route('clients.index');

View File

@ -2,9 +2,11 @@
namespace App\Http\Controllers; namespace App\Http\Controllers;
use App\Enums\PermissionEnum;
use App\Models\User; use App\Models\User;
use App\Models\Client; use App\Models\Client;
use App\Models\Project; use App\Models\Project;
use Illuminate\Support\Facades\Gate;
use Illuminate\View\View; use Illuminate\View\View;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use App\Http\Requests\StoreProjectRequest; use App\Http\Requests\StoreProjectRequest;
@ -51,6 +53,8 @@ public function update(UpdateProjectRequest $request, Project $project): Redirec
public function destroy(Project $project): RedirectResponse public function destroy(Project $project): RedirectResponse
{ {
Gate::authorize(PermissionEnum::DELETE_PROJECTS->value);
$project->delete(); $project->delete();
return redirect()->route('projects.index'); return redirect()->route('projects.index');

View File

@ -0,0 +1,65 @@
<?php
namespace App\Http\Controllers;
use App\Enums\PermissionEnum;
use App\Models\Task;
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\StoreTaskRequest;
use App\Http\Requests\UpdateTaskRequest;
class TaskController extends Controller
{
public function index(): View
{
$tasks = Task::with(['user', 'client', 'project'])->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');
}
}

View File

@ -0,0 +1,36 @@
<?php
namespace App\Http\Requests;
use App\Enums\TaskStatus;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class StoreTaskRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|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)],
];
}
}

View File

@ -0,0 +1,36 @@
<?php
namespace App\Http\Requests;
use App\Enums\TaskStatus;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class UpdateTaskRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|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)],
];
}
}

46
app/Models/Task.php Normal file
View File

@ -0,0 +1,46 @@
<?php
namespace App\Models;
use App\Enums\TaskStatus;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
class Task extends Model
{
use HasFactory, SoftDeletes;
protected $fillable = [
'title',
'description',
'user_id',
'client_id',
'project_id',
'deadline_at',
'status',
];
protected function casts(): array
{
return [
'status' => 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);
}
}

View File

@ -0,0 +1,66 @@
<?php
namespace App\Policies;
use App\Models\Task;
use App\Models\User;
use Illuminate\Auth\Access\Response;
class TaskPolicy
{
/**
* Determine whether the user can view any models.
*/
public function viewAny(User $user): bool
{
//
}
/**
* Determine whether the user can view the model.
*/
public function view(User $user, Task $task): bool
{
//
}
/**
* Determine whether the user can create models.
*/
public function create(User $user): bool
{
//
}
/**
* Determine whether the user can update the model.
*/
public function update(User $user, Task $task): bool
{
//
}
/**
* Determine whether the user can delete the model.
*/
public function delete(User $user, Task $task): bool
{
//
}
/**
* Determine whether the user can restore the model.
*/
public function restore(User $user, Task $task): bool
{
//
}
/**
* Determine whether the user can permanently delete the model.
*/
public function forceDelete(User $user, Task $task): bool
{
//
}
}

View File

@ -0,0 +1,37 @@
<?php
namespace Database\Factories;
use App\Enums\TaskStatus;
use App\Models\Client;
use App\Models\Project;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\Task>
*/
class TaskFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
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,
];
}
}

View File

@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('tasks', function (Blueprint $table) {
$table->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');
}
};

View File

@ -14,9 +14,11 @@ public function run(): void
{ {
$this->call([ $this->call([
RoleSeeder::class, RoleSeeder::class,
PermissionSeeder::class,
UserSeeder::class, UserSeeder::class,
ClientSeeder::class, ClientSeeder::class,
ProjectSeeder::class, ProjectSeeder::class,
TaskSeeder::class,
]); ]);
} }
} }

View File

@ -0,0 +1,32 @@
<?php
namespace Database\Seeders;
use App\Enums\PermissionEnum;
use App\RoleEnum;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
class PermissionSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
Permission::create(['name' => 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,
]);
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace Database\Seeders;
use App\Models\Task;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class TaskSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
Task::factory(100)->create();
}
}

View File

@ -42,6 +42,7 @@
</td> </td>
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900"> <td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900">
<a href="{{ route('clients.edit', $client) }}" class="underline">Edit</a> <a href="{{ route('clients.edit', $client) }}" class="underline">Edit</a>
@can(\App\Enums\PermissionEnum::DELETE_CLIENTS->value)
| |
<form action="{{ route('clients.destroy', $client) }}" <form action="{{ route('clients.destroy', $client) }}"
method="POST" method="POST"
@ -51,6 +52,7 @@ class="inline-block">
@csrf @csrf
<button type="submit" class="text-red-500 underline">Delete</button> <button type="submit" class="text-red-500 underline">Delete</button>
</form> </form>
@endcan
</td> </td>
</tr> </tr>
@endforeach @endforeach

View File

@ -15,17 +15,20 @@
<x-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')"> <x-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">
{{ __('Dashboard') }} {{ __('Dashboard') }}
</x-nav-link> </x-nav-link>
@role(\App\RoleEnum::ADMIN) @can(\App\Enums\PermissionEnum::MANAGE_USERS->value)
<x-nav-link :href="route('users.index')" :active="request()->routeIs('users.*')"> <x-nav-link :href="route('users.index')" :active="request()->routeIs('users.*')">
{{ __('Users') }} {{ __('Users') }}
</x-nav-link> </x-nav-link>
@endrole @endcan
<x-nav-link :href="route('clients.index')" :active="request()->routeIs('clients.*')"> <x-nav-link :href="route('clients.index')" :active="request()->routeIs('clients.*')">
{{ __('Clients') }} {{ __('Clients') }}
</x-nav-link> </x-nav-link>
<x-nav-link :href="route('projects.index')" :active="request()->routeIs('projects.*')"> <x-nav-link :href="route('projects.index')" :active="request()->routeIs('projects.*')">
{{ __('Projects') }} {{ __('Projects') }}
</x-nav-link> </x-nav-link>
<x-nav-link :href="route('tasks.index')" :active="request()->routeIs('tasks.*')">
{{ __('Tasks') }}
</x-nav-link>
</div> </div>
</div> </div>
@ -81,17 +84,20 @@
<x-responsive-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')"> <x-responsive-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">
{{ __('Dashboard') }} {{ __('Dashboard') }}
</x-responsive-nav-link> </x-responsive-nav-link>
@role(\App\RoleEnum::ADMIN) @can(\App\Enums\PermissionEnum::MANAGE_USERS->value)
<x-responsive-nav-link :href="route('users.index')" :active="request()->routeIs('users.*')"> <x-responsive-nav-link :href="route('users.index')" :active="request()->routeIs('users.*')">
{{ __('Users') }} {{ __('Users') }}
</x-responsive-nav-link> </x-responsive-nav-link>
@endrole @endcan
<x-responsive-nav-link :href="route('clients.index')" :active="request()->routeIs('clients.*')"> <x-responsive-nav-link :href="route('clients.index')" :active="request()->routeIs('clients.*')">
{{ __('Clients') }} {{ __('Clients') }}
</x-responsive-nav-link> </x-responsive-nav-link>
<x-responsive-nav-link :href="route('projects.index')" :active="request()->routeIs('projects.*')"> <x-responsive-nav-link :href="route('projects.index')" :active="request()->routeIs('projects.*')">
{{ __('Projects') }} {{ __('Projects') }}
</x-responsive-nav-link> </x-responsive-nav-link>
<x-responsive-nav-link :href="route('tasks.index')" :active="request()->routeIs('tasks.*')">
{{ __('Tasks') }}
</x-responsive-nav-link>
</div> </div>
<!-- Responsive Settings Options --> <!-- Responsive Settings Options -->

View File

@ -54,6 +54,7 @@
</td> </td>
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900"> <td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900">
<a href="{{ route('projects.edit', $project) }}" class="underline">Edit</a> <a href="{{ route('projects.edit', $project) }}" class="underline">Edit</a>
@can(\App\Enums\PermissionEnum::DELETE_PROJECTS->value)
| |
<form action="{{ route('projects.destroy', $project) }}" <form action="{{ route('projects.destroy', $project) }}"
method="POST" method="POST"
@ -63,6 +64,7 @@ class="inline-block">
@csrf @csrf
<button type="submit" class="text-red-500 underline">Delete</button> <button type="submit" class="text-red-500 underline">Delete</button>
</form> </form>
@endcan
</td> </td>
</tr> </tr>
@endforeach @endforeach

View File

@ -0,0 +1,94 @@
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Create Task') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="overflow-hidden overflow-x-auto p-6 bg-white border-b border-gray-200">
<form method="POST" action="{{ route('tasks.store') }}">
@csrf
<!-- Title -->
<div>
<x-input-label for="title" :value="__('Title')" />
<x-text-input id="title" class="block mt-1 w-full" type="text" name="title" :value="old('title')" required />
<x-input-error :messages="$errors->get('title')" class="mt-2" />
</div>
<!-- Description -->
<div class="mt-4">
<x-input-label for="description" :value="__('Description')" />
<textarea id="description" class="block mt-1 w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm" name="description" required>{{ old('description') }}</textarea>
<x-input-error :messages="$errors->get('description')" class="mt-2" />
</div>
<!-- Deadline At -->
<div class="mt-4">
<x-input-label for="deadline_at" :value="__('Deadline')" />
<x-text-input id="deadline_at" class="block mt-1 w-full" type="date" name="deadline_at" min="{{ today()->format('Y-m-d') }}" :value="old('deadline_at')" required />
<x-input-error :messages="$errors->get('deadline_at')" class="mt-2" />
</div>
<!-- Assigned User -->
<div class="mt-4">
<x-input-label for="user_id" :value="__('Assigned user')" />
<select class="block mt-1 w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm" name="user_id" id="user_id">
@foreach($users as $user)
<option value="{{ $user->id }}"
@selected(old('user_id') == $user->id)>{{ $user->first_name . ' ' . $user->last_name }}</option>
@endforeach
</select>
<x-input-error :messages="$errors->get('user_id')" class="mt-2" />
</div>
<!-- Assigned Client -->
<div class="mt-4">
<x-input-label for="client_id" :value="__('Assigned client')" />
<select class="block mt-1 w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm" name="client_id" id="client_id">
@foreach($clients as $client)
<option value="{{ $client->id }}"
@selected(old('client_id') == $client->id)>{{ $client->company_name }}</option>
@endforeach
</select>
<x-input-error :messages="$errors->get('client_id')" class="mt-2" />
</div>
<!-- Assigned Project -->
<div class="mt-4">
<x-input-label for="project_id" :value="__('Assigned project')" />
<select class="block mt-1 w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm" name="project_id" id="project_id">
@foreach($projects as $project)
<option value="{{ $project->id }}"
@selected(old('project_id') == $project->id)>{{ $project->title }}</option>
@endforeach
</select>
<x-input-error :messages="$errors->get('project_id')" class="mt-2" />
</div>
<!-- Status -->
<div class="mt-4">
<x-input-label for="status" :value="__('Status')" />
<select class="block mt-1 w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm" name="status" id="status">
@foreach(\App\Enums\TaskStatus::cases() as $status)
<option value="{{ $status->value }}"
@selected(old('status') == $status->value)>{{ $status->value }}</option>
@endforeach
</select>
<x-input-error :messages="$errors->get('status')" class="mt-2" />
</div>
<x-primary-button class="mt-4">
{{ __('Save') }}
</x-primary-button>
</form>
</div>
</div>
</div>
</div>
</x-app-layout>

View File

@ -0,0 +1,95 @@
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Edit Task') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="overflow-hidden overflow-x-auto p-6 bg-white border-b border-gray-200">
<form method="POST" action="{{ route('tasks.update', $task) }}">
@csrf
@method('PUT')
<!-- Title -->
<div>
<x-input-label for="title" :value="__('Title')" />
<x-text-input id="title" class="block mt-1 w-full" type="text" name="title" :value="old('title', $task->title)" required />
<x-input-error :messages="$errors->get('title')" class="mt-2" />
</div>
<!-- Description -->
<div class="mt-4">
<x-input-label for="description" :value="__('Description')" />
<textarea id="description" class="block mt-1 w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm" name="description" required>{{ old('description', $task->description) }}</textarea>
<x-input-error :messages="$errors->get('description')" class="mt-2" />
</div>
<!-- Deadline At -->
<div class="mt-4">
<x-input-label for="deadline_at" :value="__('Deadline')" />
<x-text-input id="deadline_at" class="block mt-1 w-full" type="date" name="deadline_at" min="{{ today()->format('Y-m-d') }}" :value="old('deadline_at', $task->deadline_at)" required />
<x-input-error :messages="$errors->get('deadline_at')" class="mt-2" />
</div>
<!-- Assigned User -->
<div class="mt-4">
<x-input-label for="user_id" :value="__('Assigned user')" />
<select class="block mt-1 w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm" name="user_id" id="user_id">
@foreach($users as $user)
<option value="{{ $user->id }}"
@selected(old('user_id', $task->user_id) == $user->id)>{{ $user->first_name . ' ' . $user->last_name }}</option>
@endforeach
</select>
<x-input-error :messages="$errors->get('user_id')" class="mt-2" />
</div>
<!-- Assigned Client -->
<div class="mt-4">
<x-input-label for="client_id" :value="__('Assigned client')" />
<select class="block mt-1 w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm" name="client_id" id="client_id">
@foreach($clients as $client)
<option value="{{ $client->id }}"
@selected(old('client_id', $task->client_id) == $client->id)>{{ $client->company_name }}</option>
@endforeach
</select>
<x-input-error :messages="$errors->get('client_id')" class="mt-2" />
</div>
<!-- Assigned Project -->
<div class="mt-4">
<x-input-label for="project_id" :value="__('Assigned project')" />
<select class="block mt-1 w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm" name="project_id" id="project_id">
@foreach($projects as $project)
<option value="{{ $project->id }}"
@selected(old('project_id', $task->project_id) == $project->id)>{{ $project->title }}</option>
@endforeach
</select>
<x-input-error :messages="$errors->get('project_id')" class="mt-2" />
</div>
<!-- Status -->
<div class="mt-4">
<x-input-label for="status" :value="__('Status')" />
<select class="block mt-1 w-full border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm" name="status" id="status">
@foreach(\App\Enums\TaskStatus::cases() as $status)
<option value="{{ $status->value }}"
@selected(old('status', $task->status->value) == $status->value)>{{ $status->value }}</option>
@endforeach
</select>
<x-input-error :messages="$errors->get('status')" class="mt-2" />
</div>
<x-primary-button class="mt-4">
{{ __('Save') }}
</x-primary-button>
</form>
</div>
</div>
</div>
</div>
</x-app-layout>

View File

@ -0,0 +1,81 @@
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Tasks') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 text-gray-900">
<a href="{{ route('tasks.create') }}" class="underline">Add new task</a>
<table class="min-w-full divide-y divide-gray-200 border mt-4">
<thead>
<tr>
<th class="px-6 py-3 bg-gray-50 text-left">
<span class="text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">Title</span>
</th>
<th class="px-6 py-3 bg-gray-50 text-left">
<span class="text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">Assigned To</span>
</th>
<th class="px-6 py-3 bg-gray-50 text-left">
<span class="text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">Client</span>
</th>
<th class="px-6 py-3 bg-gray-50 text-left">
<span class="text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">Deadline</span>
</th>
<th class="px-6 py-3 bg-gray-50 text-left">
<span class="text-xs leading-4 font-medium text-gray-500 uppercase tracking-wider">Status</span>
</th>
<th class="px-6 py-3 bg-gray-50 text-left">
</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200 divide-solid">
@foreach($tasks as $task)
<tr class="bg-white">
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900">
{{ $task->title }}
</td>
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900">
{{ $task->user->first_name }} {{ $task->user->last_name }}
</td>
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900">
{{ $task->client->company_name }}
</td>
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900">
{{ $task->deadline_at }}
</td>
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900">
{{ $task->status }}
</td>
<td class="px-6 py-4 whitespace-no-wrap text-sm leading-5 text-gray-900">
<a href="{{ route('tasks.edit', $task) }}" class="underline">Edit</a>
@can(\App\Enums\PermissionEnum::DELETE_TASKS->value)
|
<form action="{{ route('tasks.destroy', $task) }}"
method="POST"
onsubmit="return confirm('Are you sure?');"
class="inline-block">
@method('DELETE')
@csrf
<button type="submit" class="text-red-500 underline">Delete</button>
</form>
@endcan
</td>
</tr>
@endforeach
</tbody>
</table>
<div class="mt-4">
{{ $tasks->links() }}
</div>
</div>
</div>
</div>
</div>
</x-app-layout>

View File

@ -1,8 +1,10 @@
<?php <?php
use App\Enums\PermissionEnum;
use App\Http\Controllers\ClientController; use App\Http\Controllers\ClientController;
use App\Http\Controllers\ProfileController; use App\Http\Controllers\ProfileController;
use App\Http\Controllers\ProjectController; use App\Http\Controllers\ProjectController;
use App\Http\Controllers\TaskController;
use App\Http\Controllers\UserController; use App\Http\Controllers\UserController;
use App\RoleEnum; use App\RoleEnum;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
@ -17,9 +19,10 @@
Route::middleware('auth')->group(function () { Route::middleware('auth')->group(function () {
Route::resource('users', UserController::class) Route::resource('users', UserController::class)
->middleware(['role:' . RoleEnum::ADMIN->value]); ->middleware('can:' . PermissionEnum::MANAGE_USERS->value);
Route::resource('clients', ClientController::class); Route::resource('clients', ClientController::class);
Route::resource('projects', ProjectController::class); Route::resource('projects', ProjectController::class);
Route::resource('tasks', TaskController::class);
Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit'); Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update'); Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');