feature(Project Update):

Added:
- Ability to Update Project
- Response Validation classes
- Flash Messages stub

Refactored:
- Views for Todo and Project
This commit is contained in:
devoalda 2023-08-07 09:45:21 +08:00
parent fcbd9e21ae
commit 9630a250e3
17 changed files with 187 additions and 285 deletions

View File

@ -2,7 +2,10 @@
namespace App\Http\Controllers;
use App\Http\Requests\Project\StoreProjectRequest;
use App\Http\Requests\Project\UpdateProjectRequest;
use App\Models\Project;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use App\Models\User;
@ -39,18 +42,16 @@ class ProjectController extends Controller
/**
* Store a newly created project in storage.
*/
public function store(Request $request)
public function store(StoreProjectRequest $request): RedirectResponse
{
$user = User::find(auth()->user()->id);
$data = $request->validate([
'name' => 'required|unique:projects|max:255',
'description' => 'nullable|max:255',
]);
$data = $request->validated();
$user->projects()->save(new Project($data));
return redirect()->route('project.index');
return redirect()->route('project.index')
->with('info', 'Project created!');
}
/**
@ -68,26 +69,25 @@ class ProjectController extends Controller
*/
public function edit(Project $project)
{
//
return view('project.edit', [
'project' => $project,
]);
}
/**
* Update the specified Project in storage.
*/
public function update(Request $request, Project $project)
public function update(UpdateProjectRequest $request, Project $project): RedirectResponse
{
$user = User::find(auth()->user()->id);
$projects = $user->projects;
$project = $projects->find($project->id);
$data = $request->validate([
'name' => 'required|unique:projects|max:255',
'description' => 'nullable|max:255',
]);
$data = $request->validated();
$project->update($data);
return back()->with('status', 'Project updated!');
return back()->with('info', 'Project updated!');
}
/**

View File

@ -4,6 +4,8 @@ namespace App\Http\Controllers;
//use App\Http\Requests\StoreTodoRequest;
//use App\Http\Requests\UpdateTodoRequest;
use App\Http\Requests\Project\StoreTodoRequest;
use App\Http\Requests\Project\UpdateTodoRequest;
use App\Models\Todo;
use App\Models\User;
use Illuminate\Contracts\View\Factory;
@ -16,6 +18,7 @@ use Illuminate\Support\Carbon;
class ProjectTodoController extends Controller
{
private string $timezone = 'Asia/Singapore';
/**
* Display a listing of all Todos for a Project.
*/
@ -27,7 +30,7 @@ class ProjectTodoController extends Controller
$todos = $project->todos;
return view('project.todo', [
return view('todo.index', [
'todos' => $todos->whereNull('completed_at')->values(),
'completed' => $todos->whereNotNull('completed_at')->values(),
'project' => $project,
@ -39,21 +42,15 @@ class ProjectTodoController extends Controller
*/
public function create(): Factory|View|Application
{
return view('project.create');
return view('todo.create');
}
/**
* Store a newly created Todo in storage.
*/
public function store($project_id, Request $request)
public function store($project_id, StoreTodoRequest $request): RedirectResponse
{
$validatedData = Request::validate([
'title' => 'required|max:255',
'description' => 'nullable|max:255',
'due_start' => 'nullable|date',
// due_end is not required, but if it is provided, it must be after due_start
'due_end' => 'nullable|after:due_start',
]);
$validatedData = $request->validated();
$due_end = match (true) {
isset($validatedData['due_end']) => strtotime($validatedData['due_end']),
@ -93,7 +90,7 @@ class ProjectTodoController extends Controller
$projects = $user->projects;
$project = $projects->find($project_id);
return view('project.todo.show', compact('project', 'todo'));
return view('todo.show', compact('project', 'todo'));
}
/**
@ -105,14 +102,22 @@ class ProjectTodoController extends Controller
$projects = $user->projects;
$project = $projects->find($project_id);
return view('project.edit', compact('project', 'todo'));
// Check if the given todo is in the given project (Reverse find with todo's project_id)
if ($todo->project->id !== $project_id)
return back()->with('error', 'Todo not found in the given project');
return view('todo.edit', compact('project', 'todo'));
}
/**
* Update the specified resource in storage.
* Update Todo in storage based on the given project
*/
public function update($project_id, Request $request, Todo $todo)
{
if ($todo->project->id !== $project_id) {
return back()->with('error', 'Todo not found in the given project');
}
$data = Request::only(['title', 'description', 'due_start', 'due_end', 'completed_at']);
if (Request::filled('completed_at')) {
@ -147,7 +152,7 @@ class ProjectTodoController extends Controller
/**
* Remove the specified resource from storage.
*/
public function destroy($project_id, Todo $todo)
public function destroy($project_id, Todo $todo): RedirectResponse
{
$todo->delete();

View File

@ -1,10 +1,11 @@
<?php
namespace App\Http\Requests;
namespace App\Http\Requests\Project;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
class StoreTodoRequest extends FormRequest
class StoreProjectRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
@ -17,12 +18,13 @@ class StoreTodoRequest extends FormRequest
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array|string>
* @return array<string, ValidationRule|array|string>
*/
public function rules(): array
{
return [
//
'name' => 'required|string|max:255',
'description' => 'nullable|string|max:255',
];
}
}

View File

@ -2,9 +2,10 @@
namespace App\Http\Requests\Project;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
class StoreRequest extends FormRequest
class StoreTodoRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
@ -17,7 +18,7 @@ class StoreRequest extends FormRequest
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array|string>
* @return array<string, ValidationRule|array|string>
*/
public function rules(): array
{

View File

@ -1,10 +1,11 @@
<?php
namespace App\Http\Requests;
namespace App\Http\Requests\Project;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
class UpdateTodoRequest extends FormRequest
class UpdateProjectRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
@ -17,12 +18,13 @@ class UpdateTodoRequest extends FormRequest
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array|string>
* @return array<string, ValidationRule|array|string>
*/
public function rules(): array
{
return [
//
'name' => 'required|string|max:255',
'description' => 'nullable|string|max:255',
];
}
}

View File

@ -5,7 +5,7 @@ namespace App\Http\Requests\Project;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Foundation\Http\FormRequest;
class UpdateRequest extends FormRequest
class UpdateTodoRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.

View File

@ -16,6 +16,7 @@ class Project extends Model
use HasFactory, UuidTrait;
protected $dateFormat = 'U';
protected $table = 'projects';
protected $fillable = [
'name',

View File

@ -15,6 +15,7 @@ class Todo extends Model
use HasFactory, UuidTrait;
protected $dateFormat = 'U';
protected $table = 'todos';
protected $fillable = [
'title',

View File

@ -16,6 +16,9 @@ class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable, UuidTrait;
protected $dateFormat = 'U';
protected $table = 'users';
/**
* The attributes that are mass assignable.
*

View File

@ -0,0 +1,43 @@
@if ($message = Session::get('success'))
<div class="bg-green-500 text-white p-4 rounded-lg mb-6 text-center">
<strong>{{ $message }}</strong>
</div>
@endif
@if ($message = Session::get('error'))
<div class="bg-red-500 text-white p-4 rounded-lg mb-6 text-center">
<strong>{{ $message }}</strong>
</div>
@endif
@if ($message = Session::get('warning'))
<div class="bg-yellow-500 text-white p-4 rounded-lg mb-6 text-center">
<strong>{{ $message }}</strong>
</div>
@endif
@if ($message = Session::get('info'))
<div class="bg-blue-500 text-white p-4 rounded-lg mb-6 text-center">
<strong>{{ $message }}</strong>
</div>
@endif
@if ($message = Session::get('status'))
<div class="bg-blue-500 text-white p-4 rounded-lg mb-6 text-center">
<strong>{{ $message }}</strong>
</div>
@endif
@if ($errors->any())
<div class="bg-red-500 text-white p-4 rounded-lg mb-6 text-center">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif

View File

@ -27,6 +27,9 @@
</header>
@endif
<!-- Flash Messages -->
@include('flashView.flashmessages')
<!-- Page Content -->
<main>
{{ $slot }}

View File

@ -1,26 +1,12 @@
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
{{ __('Edit Todo') }}
{{ __('Edit Project') }}
</h2>
</x-slot>
<!-- Success/Error Message in green/red session('success') or session('error') -->
@if(session('success'))
<div class="bg-green-500 text-white p-4 rounded-lg mb-6 text-center">
{{ session('success') }}
</div>
@endif
@if(session('error'))
<div class="bg-red-500 text-white p-4 rounded-lg mb-6 text-center">
{{ session('error') }}
</div>
@endif
<div class="py-4">
<form method="POST" action="{{ route('project.todo.update', [$project, $todo]) }}"
id="todo-form">
<form method="POST" action="{{ route('project.update', $project) }}" id="project-form">
@csrf
@method('PUT')
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
@ -28,10 +14,10 @@
<div class="text-gray-800 dark:text-gray-100">
<div class="mb-4">
<label for="title" class="block mb-2 font-semibold">Title</label>
<input type="text" name="title" id="title" placeholder="Title"
<label for="name" class="block mb-2 font-semibold">Name</label>
<input type="text" name="name" id="name" placeholder="Name"
class="bg-gray-100 dark:bg-gray-700 border border-gray-300 dark:border-gray-700 focus:ring-2 focus:ring-blue-500 rounded-lg w-full p-4 @error('title') border-red-500 @enderror"
value="{{ old('title', $todo->title) }}">
value="{{ old('name', $project->name) }}">
@error('title')
<div class="text-red-500 mt-2 text-sm">
{{ $message }}
@ -42,7 +28,7 @@
<div class="mb-4">
<label for="description" class="block mb-2 font-semibold">Description</label>
<textarea name="description" id="description" placeholder="Description"
class="bg-gray-100 dark:bg-gray-700 border border-gray-300 dark:border-gray-700 focus:ring-2 focus:ring-blue-500 rounded-lg w-full p-4 @error('description') border-red-500 @enderror">{{ old('description', $todo->description) }}</textarea>
class="bg-gray-100 dark:bg-gray-700 border border-gray-300 dark:border-gray-700 focus:ring-2 focus:ring-blue-500 rounded-lg w-full p-4 @error('description') border-red-500 @enderror">{{ old('description', $project->description) }}</textarea>
@error('description')
<div class="text-red-500 mt-2 text-sm">
{{ $message }}
@ -50,29 +36,6 @@
@enderror
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label for="due_start" class="block mb-2 font-semibold">Due Start</label>
<input type="datetime-local" name="due_start" id="due_start" placeholder="Due Start"
class="bg-gray-100 dark:bg-gray-700 border border-gray-300 dark:border-gray-700 focus:ring-2 focus:ring-blue-500 rounded-lg w-full p-4 @error('due_start') border-red-500 @enderror"
value="{{ old('due_start', $todo->due_start ? date('Y-m-d\TH:i', $todo->due_start) : '') }}">
@error('due_start')
<div class="text-red-500 mt-2 text-sm">
{{ $message }}
</div>
@enderror
</div>
<div>
<label for="due_end" class="block mb-2 font-semibold">Due End</label>
<input type="datetime-local" name="due_end" id="due_end" placeholder="Due End"
class="bg-gray-100 dark:bg-gray-700 border border-gray-300 dark:border-gray-700 focus:ring-2 focus:ring-blue-500 rounded-lg w-full p-4 @error('due_end') border-red-500 @enderror"
value="{{ old('due_end', $todo->due_end ? date('Y-m-d\TH:i', $todo->due_end) : '') }}">
@error('due_end')
<div class="text-red-900 mt-2 text-sm">
{{ $message }}
</div>
@enderror
</div>
</div>
<div class="flex justify-end mt-4 space-x-4">
@ -115,7 +78,7 @@
</div>
<!-- Hidden Delete Form -->
<form id="delete-form" action="{{ route('project.todo.destroy', [$project, $todo]) }}" method="POST"
<form id="delete-form" action="{{ route('project.destroy', $project) }}" method="POST"
class="hidden">
@csrf
@method('DELETE')
@ -123,7 +86,7 @@
<script>
function confirmDelete() {
if (confirm('Are you sure you want to delete this item?')) {
if (confirm('Are you sure you want to delete this Project?')) {
document.getElementById('delete-form').submit();
}
}

View File

@ -9,19 +9,6 @@
</h2>
</x-slot>
<!-- Success/Error Message in green/red session('success') or session('error') -->
@if(session('success'))
<div class="bg-green-500 text-white p-4 rounded-lg mb-6 text-center">
{{ session('success') }}
</div>
@endif
@if(session('error'))
<div class="bg-red-500 text-white p-4 rounded-lg mb-6 text-center">
{{ session('error') }}
</div>
@endif
<div class="py-4">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
@ -170,11 +157,12 @@
</h2>
<div class="space-y-4">
@foreach ($completed as $todo)
<a href="{{ route('project.todo.edit', [$project->id, $todo->id]) }}" class="block">
<a href="{{ route('project.todo.edit', [$todo->project->id, $todo->id]) }}"
class="block">
<div
class="bg-white dark:bg-gray-800 shadow-sm rounded-lg p-6 flex items-center justify-between">
<div class="flex items-center">
<form action="{{ route('project.todo.update', [$project->id, $todo->id]) }}"
<form action="{{ route('project.todo.update', [$todo->project->id, $todo->id]) }}"
method="POST"
class="toggle-completed-form">
@csrf

View File

@ -1,155 +0,0 @@
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
{{ __('Todo List for Project:') }} <span class="font-bold text-blue-500 dark:text-blue-400"
>{{ $project->name }}</span>
</h2>
</x-slot>
<!-- Flash data for success and error messages -->
@if(session()->has('success'))
<div class="bg-green-500 text-white p-4 rounded-lg mb-6 text-center">
{{ session('success') }}
</div>
@elseif(session()->has('error'))
<div class="bg-red-500 text-white p-4 rounded-lg mb-6 text-center">
{{ session('error') }}
</div>
@endif
<div class="bg-white dark:bg-gray-800 shadow-sm rounded-lg p-6 mb-3">
<form method="POST" action="{{ route('project.todo.store', $project->id) }}">
@csrf
<div class="text-gray-800 dark:text-gray-100">
<div class="mb-4 flex items-center">
<input type="text" name="title" id="title" placeholder="Your Awesome Todo"
class="bg-gray-100 dark:bg-gray-700 border border-gray-300 dark:border-gray-700 focus:ring-2 focus:ring-blue-500 text-gray-900 dark:text-gray-100 rounded-lg w-full p-4 @error('title') border-red-500 @enderror"
value="{{ old('title') }}" required autofocus>
<!-- Plus button to submit the form or go to todo.create if title is empty -->
<button type="submit"
class="ml-2 bg-blue-500 hover:bg-blue-600 text-white font-semibold rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
onclick="event.preventDefault();
document.getElementById('title').value.trim() === '' ? window.location.href = '{{ route('project.todo.create', $project->id) }}' : this.form.submit();">
<svg xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
class="w-6 h-full inline-block">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 6v6m0 0v6m0-6h6m-6 0H6"></path>
</svg>
</button>
</div>
@error('title')
<div class="text-red-500 mt-2 text-sm">
{{ $message }}
</div>
@enderror
</div>
</form>
</div>
<div class="bg-white dark:bg-gray-800 shadow-sm rounded-lg p-6 mb-3">
<h2 class="text-2xl font-semibold mb-4 text-red-500 dark:text-red-400">
Not Completed ({{ $todos->count() }})
</h2>
<div class="space-y-4">
@foreach ($todos as $todo)
@if (!$todo->completed_at)
<a href="{{ route('project.todo.edit', [$project->id, $todo->id]) }}" class="block">:
<div
class="bg-white dark:bg-gray-800 shadow-sm rounded-lg p-6 flex items-center justify-between">
<div class="flex items-center">
<!-- Checkbox to toggle completed at -->
<form action="{{ route('project.todo.update', [$project->id, $todo->id]) }}"
method="POST"
class="toggle-completed-form">
@csrf
@method('PUT')
<label class="flex items-center cursor-pointer">
<input type="checkbox" name="completed_at"
class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"
onclick="this.form.submit()" {{ $todo->completed_at ? 'checked' : '' }}>
<span class="ml-2 text-sm text-gray-700"></span>
</label>
</form>
<span class="ml-2 text-xl font-bold text-gray-800 dark:text-gray-100">{{ $todo->title }}</span>
</div>
<div>
@php
if ($todo->due_start && $todo->due_end) {
$due = \Carbon\Carbon::parse($todo->due_end);
$now = now();
$timeRemaining = $due->isFuture() ? $now->diffForHumans($due, true) : $due->diffForHumans($now,
true);
} elseif ($todo->due_start) {
$due = \Carbon\Carbon::parse($todo->due_start);
$now = now();
$timeRemaining = $due->isFuture() ? $now->diffForHumans($due, true) : $due->diffForHumans($now,
true);
} elseif ($todo->due_end) {
$due = \Carbon\Carbon::parse($todo->due_end);
$now = now();
$timeRemaining = $due->isFuture() ? $now->diffForHumans($due, true) : $due->diffForHumans($now,
true);
} else {
// If there is no due_start or due_end, set $timeRemaining to null
$timeRemaining = null;
}
@endphp
@if ($timeRemaining !== null)
@if ($due->isFuture())
<p class="text-sm text-green-600">{{ $timeRemaining }} remaining</p>
@else
<p class="text-sm text-red-600">{{ $timeRemaining }} ago</p>
@endif
@endif
</div>
</div>
</a>
@endif
@endforeach
</div>
</div>
<div class="bg-white dark:bg-gray-800 shadow-sm rounded-lg p-6">
<h2 class="text-2xl font-semibold mb-4 text-green-500 dark:text-green-400">
Completed Today
({{ $completed->count() }})
</h2>
<div class="space-y-4">
@foreach ($completed as $todo)
<a href="{{ route('project.todo.edit', [$project->id, $todo->id]) }}" class="block">
<div
class="bg-white dark:bg-gray-800 shadow-sm rounded-lg p-6 flex items-center justify-between">
<div class="flex items-center">
<form action="{{ route('project.todo.update', [$project->id, $todo->id]) }}"
method="POST"
class="toggle-completed-form">
@csrf
@method('PUT')
<label class="flex items-center cursor-pointer">
<input type="checkbox" name="completed_at" id="completed_at_{{ $todo->id }}"
class="w-4 h-4 text-green-600 bg-gray-100 border-gray-300 rounded focus:ring-green-500 dark:focus:ring-green-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"
onchange="this.form.submit()" {{ $todo->completed_at ? 'checked' : ''
}}>
<span class="ml-2 text-sm text-gray-700"></span>
</label>
</form>
<span
class="ml-2 text-2xl font-bold text-gray-800 dark:text-gray-100">
{{ $todo->title }}
</span>
</div>
</div>
</a>
@endforeach
</div>
</div>
</x-app-layout>

View File

@ -0,0 +1,55 @@
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
{{ __('Create Project') }}
</h2>
</x-slot>
<!-- Create Project Form (Name, Description) -->
<div class="py-4">
<form method="POST" action="{{ route('project.store') }}" id="project-form">
@csrf
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white dark:bg-gray-800 shadow-sm rounded-lg p-6">
<div class="text-gray-800 dark:text-gray-100">
<div class="mb-4">
<label for="name" class="block mb-2 font-semibold">Name</label>
<input type="text" name="name" id="name" placeholder="Name"
class="bg-gray-100 dark:bg-gray-700 border border-gray-300 dark:border-gray-700 focus:ring-2 focus:ring-blue-500 rounded-lg w-full p-4 @error('name') border-red-500 @enderror"
value="{{ old('name') }}">
@error('name')
<div class="text-red-500 mt-2 text-sm">
{{ $message }}
</div>
@enderror
</div>
<div class="mb-4">
<label for="description" class="block mb-2 font-semibold">Description</label>
<textarea name="description" id="description" placeholder="Description"
class="bg-gray-100 dark:bg-gray-700 border border-gray-300 dark:border-gray-700 focus:ring-2 focus:ring-blue-500 rounded-lg w-full p-4 @error('description') border-red-500 @enderror">{{ old('description') }}</textarea>
@error('description')
<div class="text-red-500 mt-2 text-sm">
{{ $message }}
</div>
@enderror
</div>
<div>
<button type="submit"
class="bg-blue-500 hover:bg-blue-600 text-white font-semibold px-4 py-2 rounded-lg">
Create Project
</button>
</div>
</div>
</div>
</div>
</form>
</div>
</x-app-layout>

View File

@ -6,7 +6,8 @@
</x-slot>
<div class="py-4">
<form method="POST" action="{{ route('todo.update', $todo) }}" id="todo-form">
<form method="POST" action="{{ route('project.todo.update', [$project, $todo]) }}"
id="todo-form">
@csrf
@method('PUT')
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
@ -63,7 +64,7 @@
<div class="flex justify-end mt-4 space-x-4">
<!-- Cancel Button (GET request to index route) -->
<a href="{{ route('todo.index') }}"
<a href="{{ route('project.todo.index', $project) }}"
class="inline-flex items-center px-4 py-2 text-sm font-medium text-gray-900 bg-transparent border border-gray-900 rounded-l-lg hover:bg-gray-900 hover:text-white focus:z-10 focus:ring-2 focus:ring-gray-500 focus:bg-gray-900 focus:text-white dark:border-white dark:text-white dark:hover:text-white dark:hover:bg-gray-700 dark:focus:bg-gray-700">
<svg class="w-5 h-5 mr-2" aria-hidden="true" fill="none" viewBox="0 0 24 24"
stroke="currentColor">
@ -96,20 +97,16 @@
Update
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</form>
<!-- Hidden Delete Form -->
<form id="delete-form" action="{{ route('todo.destroy', $todo) }}" method="POST">
<form id="delete-form" action="{{ route('project.todo.destroy', [$project, $todo]) }}" method="POST"
class="hidden">
@csrf
@method('DELETE')
</form>
</div>
<script>
function confirmDelete() {

View File

@ -2,23 +2,14 @@
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
{{ __('Todo List') }}
{{ __('Todo List for Project:') }} <span class="font-bold text-blue-500 dark:text-blue-400"
>{{ $project->name }}</span>
</h2>
</x-slot>
<!-- Flash data for success and error messages -->
@if(session()->has('success'))
<div class="bg-green-500 text-white p-4 rounded-lg mb-6 text-center">
{{ session('success') }}
</div>
@elseif(session()->has('error'))
<div class="bg-red-500 text-white p-4 rounded-lg mb-6 text-center">
{{ session('error') }}
</div>
@endif
<div class="bg-white dark:bg-gray-800 shadow-sm rounded-lg p-6 mb-3">
<form method="POST" action="{{ route('todo.store') }}" id="todo-form">
<!-- Same width and design as the bottom todo listing -->
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 bg-white dark:bg-gray-800 shadow-sm rounded-lg p-6 mt-4">
<form method="POST" action="{{ route('project.todo.store', $project->id) }}">
@csrf
<div class="text-gray-800 dark:text-gray-100">
<div class="mb-4 flex items-center">
@ -30,7 +21,7 @@
<button type="submit"
class="ml-2 bg-blue-500 hover:bg-blue-600 text-white font-semibold rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
onclick="event.preventDefault();
document.getElementById('title').value.trim() === '' ? window.location.href = '{{ route('todo.create') }}' : document.getElementById('todo-form').submit();">
document.getElementById('title').value.trim() === '' ? window.location.href = '{{ route('project.todo.create', $project->id) }}' : this.form.submit();">
<svg xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
@ -51,27 +42,27 @@
</div>
<div class="bg-white dark:bg-gray-800 shadow-sm rounded-lg p-6 mb-3">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 bg-white dark:bg-gray-800 shadow-sm rounded-lg p-6 mt-4">
<h2 class="text-2xl font-semibold mb-4 text-red-500 dark:text-red-400">
Not Completed ({{ $todos->count() }})
</h2>
<div class="space-y-4">
@foreach ($todos as $todo)
@if (!$todo->completed_at)
<a href="{{ route('todo.edit', $todo->id) }}" class="block">
<a href="{{ route('project.todo.edit', [$project->id, $todo->id]) }}" class="block">:
<div
class="bg-white dark:bg-gray-800 shadow-sm rounded-lg p-6 flex items-center justify-between">
<div class="flex items-center">
<!-- Checkbox to toggle completed at -->
<form action="{{ route('todo.update', $todo->id) }}" method="POST"
<form action="{{ route('project.todo.update', [$project->id, $todo->id]) }}"
method="POST"
class="toggle-completed-form">
@csrf
@method('PUT')
<label class="flex items-center cursor-pointer">
<input type="checkbox" name="completed_at"
class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"
onclick="this.form.submit()" {{ $todo->completed_at ? 'checked' : ''
}}>
onclick="this.form.submit()" {{ $todo->completed_at ? 'checked' : '' }}>
<span class="ml-2 text-sm text-gray-700"></span>
</label>
</form>
@ -117,18 +108,20 @@
</div>
<div class="bg-white dark:bg-gray-800 shadow-sm rounded-lg p-6">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 bg-white dark:bg-gray-800 shadow-sm rounded-lg p-6 mt-4">
<h2 class="text-2xl font-semibold mb-4 text-green-500 dark:text-green-400">
Completed Today
({{ $completed->count() }})
</h2>
<div class="space-y-4">
@foreach ($completed as $todo)
<a href="{{ route('todo.edit', $todo->id) }}" class="block">
<a href="{{ route('project.todo.edit', [$project->id, $todo->id]) }}" class="block">
<div
class="bg-white dark:bg-gray-800 shadow-sm rounded-lg p-6 flex items-center justify-between">
<div class="flex items-center">
<form action="{{ route('todo.update', $todo->id) }}" method="POST">
<form action="{{ route('project.todo.update', [$project->id, $todo->id]) }}"
method="POST"
class="toggle-completed-form">
@csrf
@method('PUT')
<label class="flex items-center cursor-pointer">