feature(Project addition):

Added:
- Functionality to add projects

Fixed:
- Todo Creation Functionality
- Todo Update Functionality
This commit is contained in:
devoalda 2023-08-06 23:03:34 +08:00
parent 3af59cfaba
commit fcbd9e21ae
10 changed files with 122 additions and 200 deletions

View File

@ -37,12 +37,20 @@ class ProjectController extends Controller
}
/**
* TODO: Complete this method
* Store a newly created project in storage.
*/
public function store(Request $request)
{
//
$user = User::find(auth()->user()->id);
$data = $request->validate([
'name' => 'required|unique:projects|max:255',
'description' => 'nullable|max:255',
]);
$user->projects()->save(new Project($data));
return redirect()->route('project.index');
}
/**
@ -51,7 +59,7 @@ class ProjectController extends Controller
*/
public function show(Project $project)
{
//
return redirect()->route('project.index');
}
/**
@ -68,7 +76,18 @@ class ProjectController extends Controller
*/
public function update(Request $request, Project $project)
{
//
$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',
]);
$project->update($data);
return back()->with('status', 'Project updated!');
}
/**

View File

@ -77,6 +77,7 @@ class ProjectTodoController extends Controller
$user = User::find(auth()->user()->id);
$project = $user->projects->find($project_id);
// Add the Todo to the Project
$project->todos()->save($todo);
return redirect()->route('project.todo.index', $project_id)
@ -140,8 +141,7 @@ class ProjectTodoController extends Controller
$todo->update($data);
return redirect()->route('project.todo.index', $project_id)
->with('success', 'Todo updated successfully');
return back()->with('success', 'Todo updated successfully');
}
/**

View File

@ -1,181 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\StoreTodoRequest;
use App\Http\Requests\UpdateTodoRequest;
use App\Models\Todo;
use App\Models\User;
use Illuminate\Contracts\View\Factory;
use Illuminate\Contracts\View\View;
use Illuminate\Foundation\Application;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\Request;
use Illuminate\Support\Carbon;
class TodoController extends Controller
{
private string $timezone = "Asia/Singapore";
/**
* Display a listing of the resource.
*/
public function index(): View|Application
{
$user = User::find(auth()->user()->id);
$projects = $user->projects;
$todos = collect();
foreach ($projects as $project) {
$todos = $todos->merge($project->todos);
}
return view('todo.index', [
'todos' => $todos->whereNull('completed_at')->values(),
'completed' => $todos->whereNotNull('completed_at')->values(),
'projects' => $projects,
]);
}
/**
* Show the form for creating a new resource.
*/
public function create(): Factory|View|Application
{
return view('todo.create');
}
/**
* Store a newly created resource in storage.
*/
public function store(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',
]);
$due_end = match (true) {
isset($validatedData['due_end']) => strtotime($validatedData['due_end']),
isset($validatedData['due_start']) => strtotime($validatedData['due_start']),
default => null,
};
$validatedData = array_merge($validatedData, [
// due_end = due_start if due_end is not provided and due_start is provided or null
'due_end' => $due_end,
'user_id' => auth()->user()->id,
]);
// Modify all dates to unix timestamp
if (isset($validatedData['due_start']))
$validatedData['due_start'] = strtotime($validatedData['due_start']);
if (isset($validatedData['due_end']))
$validatedData['due_end'] = strtotime($validatedData['due_end']);
$todo = new Todo($validatedData);
$user = User::find(auth()->user()->id);
$project = $user->projects->first();
$project->todos()->save($todo);
// Set flash message
session()->flash('success', 'Todo created successfully.');
return redirect()->route('todo.index');
}
/**
* Display the specified resource.
*/
public function show(Todo $todo): Factory|View|Application
{
$user = User::find(auth()->user()->id);
$projects = $user->projects;
$todo = $projects->first()
->todos
->where('id', $todo->id)
->first();
return view('todo.show', compact('todo'));
}
/**
* Show the form for editing the specified resource.
*/
public function edit(Todo $todo): Factory|View|Application
{
$user = User::find(auth()->user()->id);
$projects = $user->projects;
$todo = $projects->first()
->todos
->where('id', $todo->id)
->first();
return view('todo.edit', compact('todo'));
}
/**
* Update the specified resource in storage.
* Can either update completed_at (Checkbox) or other fields
*
* @param UpdateTodoRequest $request UpdateTodoRequest
* @param Todo $todo Todo Object
* @return RedirectResponse Redirect to todo.index
*/
public function update(UpdateTodoRequest $request, Todo $todo): RedirectResponse
{
$data = $request->only(['title', 'description', 'due_start', 'due_end', 'completed_at']);
if (Request::filled('completed_at')) {
$todo->completed_at = Request::input('completed_at') === 'on' ? strtotime(now($this->timezone)) : null;
$todo->save();
return redirect()->route('todo.index')->with('success', 'Good job! Todo completed.');
} else {
// If 'completed_at' is not provided, toggle its value (only if the request is empty)
if (empty($data))
$todo->completed_at = $todo->completed_at ? null : strtotime(now($this->timezone));
else
// Continue to update other fields
unset($data['completed_at']);
}
if (Request::filled('due_start')) {
$data['due_start'] = strtotime(Request::input('due_start'));
}
if (Request::filled('due_end')) {
$data['due_end'] = strtotime(Request::input('due_end'));
} elseif (isset($data['due_start'])) {
// If 'due_end' is not provided, set it to 'due_start' value
$data['due_end'] = strtotime(Request::input('due_start'));
}
$todo->update($data);
return redirect()->route('todo.index')->with('success', 'Todo updated successfully.');
}
/**
* Remove the specified resource from storage.
*/
public function destroy(Todo $todo): RedirectResponse
{
$todo = Todo::where('user_id', auth()->user()->id)
->where('id', $todo->id)
->firstOrFail();
$todo->delete();
// Set flash message
session()->flash('success', 'Todo deleted successfully.');
return redirect()->route('todo.index');
}
}

View File

@ -9,6 +9,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Database\Eloquent\Relations\HasOneThrough;
class Project extends Model
{
@ -25,13 +26,13 @@ class Project extends Model
* Relationship with Todo model (one to many)
* @return BelongsToMany
*/
public function todos(): HasManyThrough
public function todos(): BelongsToMany
{
return $this->hasManyThrough(Todo::class, projectTodo::class, 'project_id', 'id', 'id', 'todo_id');
return $this->belongsToMany(Todo::class, 'project_todo', 'project_id', 'todo_id');
}
public function user(): BelongsTo
{
return $this->belongsTo(projectUser::class, 'project_user', 'project_id', 'user_id');
return $this->belongsTo(User::class);
}
}

View File

@ -6,6 +6,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use App\Traits\UuidTrait;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Database\Eloquent\Relations\HasOneThrough;
use Illuminate\Database\Eloquent\Relations\MorphTo;
@ -39,7 +40,7 @@ class Todo extends Model
public function project(): HasOneThrough
{
return $this->hasOneThrough(Project::class, ProjectTodo::class, 'todo_id', 'id', 'id', 'project_id');
return $this->hasOneThrough(Project::class, projectTodo::class, 'todo_id', 'id', 'id', 'project_id');
}
}

View File

@ -5,6 +5,8 @@ namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use App\Traits\UuidTrait;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
@ -45,13 +47,13 @@ class User extends Authenticatable
'password' => 'hashed',
];
public function projects(): HasManyThrough
public function projects(): BelongsToMany
{
return $this->hasManyThrough(Project::class, projectUser::class, 'user_id', 'id', 'id', 'project_id');
return $this->belongsToMany(Project::class, 'project_user', 'user_id', 'project_id');
}
public function todos(): HasManyThrough
{
return $this->hasManyThrough(Todo::class, projectUser::class, 'user_id', 'id', 'id', 'project_id');
}
// public function todos(): HasManyThrough
// {
// return $this->hasManyThrough(Todo::class, projectUser::class, 'user_id', 'id', 'id', 'project_id');
// }
}

View File

@ -17,7 +17,7 @@ class RouteServiceProvider extends ServiceProvider
*
* @var string
*/
public const HOME = '/todo';
public const HOME = '/project';
/**
* Define your route model bindings, pattern filters, and other route configuration.

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

@ -5,6 +5,19 @@
</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">

View File

@ -9,6 +9,18 @@
</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">
@ -83,12 +95,12 @@
<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">:
<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">
<!-- Checkbox to toggle completed at -->
<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