Tasks CRUD
This commit is contained in:
parent
282ef63cf2
commit
f112cf2ea5
11
app/Enums/PermissionEnum.php
Normal file
11
app/Enums/PermissionEnum.php
Normal 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
13
app/Enums/TaskStatus.php
Normal 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';
|
||||
}
|
||||
@ -2,7 +2,9 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Enums\PermissionEnum;
|
||||
use App\Models\Client;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\View\View;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use App\Http\Requests\StoreClientRequest;
|
||||
@ -43,6 +45,8 @@ public function update(UpdateClientRequest $request, Client $client): RedirectRe
|
||||
|
||||
public function destroy(Client $client): RedirectResponse
|
||||
{
|
||||
Gate::authorize(PermissionEnum::DELETE_CLIENTS->value);
|
||||
|
||||
$client->delete();
|
||||
|
||||
return redirect()->route('clients.index');
|
||||
|
||||
@ -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');
|
||||
|
||||
65
app/Http/Controllers/TaskController.php
Normal file
65
app/Http/Controllers/TaskController.php
Normal 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');
|
||||
}
|
||||
}
|
||||
36
app/Http/Requests/StoreTaskRequest.php
Normal file
36
app/Http/Requests/StoreTaskRequest.php
Normal 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)],
|
||||
];
|
||||
}
|
||||
}
|
||||
36
app/Http/Requests/UpdateTaskRequest.php
Normal file
36
app/Http/Requests/UpdateTaskRequest.php
Normal 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
46
app/Models/Task.php
Normal 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);
|
||||
}
|
||||
}
|
||||
66
app/Policies/TaskPolicy.php
Normal file
66
app/Policies/TaskPolicy.php
Normal 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
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
37
database/factories/TaskFactory.php
Normal file
37
database/factories/TaskFactory.php
Normal 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,
|
||||
];
|
||||
}
|
||||
}
|
||||
35
database/migrations/2024_09_10_091006_create_tasks_table.php
Normal file
35
database/migrations/2024_09_10_091006_create_tasks_table.php
Normal 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');
|
||||
}
|
||||
};
|
||||
@ -14,9 +14,11 @@ public function run(): void
|
||||
{
|
||||
$this->call([
|
||||
RoleSeeder::class,
|
||||
PermissionSeeder::class,
|
||||
UserSeeder::class,
|
||||
ClientSeeder::class,
|
||||
ProjectSeeder::class,
|
||||
TaskSeeder::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
32
database/seeders/PermissionSeeder.php
Normal file
32
database/seeders/PermissionSeeder.php
Normal 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,
|
||||
]);
|
||||
}
|
||||
}
|
||||
18
database/seeders/TaskSeeder.php
Normal file
18
database/seeders/TaskSeeder.php
Normal 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();
|
||||
}
|
||||
}
|
||||
@ -42,6 +42,7 @@
|
||||
</td>
|
||||
<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>
|
||||
@can(\App\Enums\PermissionEnum::DELETE_CLIENTS->value)
|
||||
|
|
||||
<form action="{{ route('clients.destroy', $client) }}"
|
||||
method="POST"
|
||||
@ -51,6 +52,7 @@ class="inline-block">
|
||||
@csrf
|
||||
<button type="submit" class="text-red-500 underline">Delete</button>
|
||||
</form>
|
||||
@endcan
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
|
||||
@ -15,17 +15,20 @@
|
||||
<x-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">
|
||||
{{ __('Dashboard') }}
|
||||
</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.*')">
|
||||
{{ __('Users') }}
|
||||
</x-nav-link>
|
||||
@endrole
|
||||
@endcan
|
||||
<x-nav-link :href="route('clients.index')" :active="request()->routeIs('clients.*')">
|
||||
{{ __('Clients') }}
|
||||
</x-nav-link>
|
||||
<x-nav-link :href="route('projects.index')" :active="request()->routeIs('projects.*')">
|
||||
{{ __('Projects') }}
|
||||
</x-nav-link>
|
||||
<x-nav-link :href="route('tasks.index')" :active="request()->routeIs('tasks.*')">
|
||||
{{ __('Tasks') }}
|
||||
</x-nav-link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -81,17 +84,20 @@
|
||||
<x-responsive-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">
|
||||
{{ __('Dashboard') }}
|
||||
</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.*')">
|
||||
{{ __('Users') }}
|
||||
</x-responsive-nav-link>
|
||||
@endrole
|
||||
@endcan
|
||||
<x-responsive-nav-link :href="route('clients.index')" :active="request()->routeIs('clients.*')">
|
||||
{{ __('Clients') }}
|
||||
</x-responsive-nav-link>
|
||||
<x-responsive-nav-link :href="route('projects.index')" :active="request()->routeIs('projects.*')">
|
||||
{{ __('Projects') }}
|
||||
</x-responsive-nav-link>
|
||||
<x-responsive-nav-link :href="route('tasks.index')" :active="request()->routeIs('tasks.*')">
|
||||
{{ __('Tasks') }}
|
||||
</x-responsive-nav-link>
|
||||
</div>
|
||||
|
||||
<!-- Responsive Settings Options -->
|
||||
|
||||
@ -54,6 +54,7 @@
|
||||
</td>
|
||||
<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>
|
||||
@can(\App\Enums\PermissionEnum::DELETE_PROJECTS->value)
|
||||
|
|
||||
<form action="{{ route('projects.destroy', $project) }}"
|
||||
method="POST"
|
||||
@ -63,6 +64,7 @@ class="inline-block">
|
||||
@csrf
|
||||
<button type="submit" class="text-red-500 underline">Delete</button>
|
||||
</form>
|
||||
@endcan
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
|
||||
94
resources/views/tasks/create.blade.php
Normal file
94
resources/views/tasks/create.blade.php
Normal 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>
|
||||
95
resources/views/tasks/edit.blade.php
Normal file
95
resources/views/tasks/edit.blade.php
Normal 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>
|
||||
81
resources/views/tasks/index.blade.php
Normal file
81
resources/views/tasks/index.blade.php
Normal 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>
|
||||
@ -1,8 +1,10 @@
|
||||
<?php
|
||||
|
||||
use App\Enums\PermissionEnum;
|
||||
use App\Http\Controllers\ClientController;
|
||||
use App\Http\Controllers\ProfileController;
|
||||
use App\Http\Controllers\ProjectController;
|
||||
use App\Http\Controllers\TaskController;
|
||||
use App\Http\Controllers\UserController;
|
||||
use App\RoleEnum;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
@ -17,9 +19,10 @@
|
||||
|
||||
Route::middleware('auth')->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');
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user