mirror of https://github.com/Devoalda/LaDo.git
fix(Seed + Todo Creation):
Fixed: - Database seeder - Todo creation/edit form + Completed Checkbox - Models - Validation for views
This commit is contained in:
parent
9630a250e3
commit
018e0813d2
|
@ -28,6 +28,9 @@ class ProjectTodoController extends Controller
|
|||
$projects = $user->projects;
|
||||
$project = $projects->find($project_id);
|
||||
|
||||
if (!$project || $project->user->id !== auth()->user()->id)
|
||||
return back()->with('error', 'Project not found');
|
||||
|
||||
$todos = $project->todos;
|
||||
|
||||
return view('todo.index', [
|
||||
|
@ -40,9 +43,12 @@ class ProjectTodoController extends Controller
|
|||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*/
|
||||
public function create(): Factory|View|Application
|
||||
public function create($project_id): Factory|View|Application
|
||||
{
|
||||
return view('todo.create');
|
||||
$project = auth()->user()->projects->find($project_id);
|
||||
return view('todo.create', [
|
||||
'project' => $project,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -90,6 +96,9 @@ class ProjectTodoController extends Controller
|
|||
$projects = $user->projects;
|
||||
$project = $projects->find($project_id);
|
||||
|
||||
if (!$project || $project->user->id !== auth()->user()->id || $todo->user()[0]->id !== auth()->user()->id)
|
||||
return back()->with('error', 'Project/Todo not found');
|
||||
|
||||
return view('todo.show', compact('project', 'todo'));
|
||||
}
|
||||
|
||||
|
@ -102,6 +111,9 @@ class ProjectTodoController extends Controller
|
|||
$projects = $user->projects;
|
||||
$project = $projects->find($project_id);
|
||||
|
||||
if (!$project || $project->user->id !== auth()->user()->id || $todo->user()[0]->id !== auth()->user()->id)
|
||||
return back()->with('error', 'Project/Todo not found');
|
||||
|
||||
// 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');
|
||||
|
|
|
@ -32,8 +32,15 @@ class Project extends Model
|
|||
return $this->belongsToMany(Todo::class, 'project_todo', 'project_id', 'todo_id');
|
||||
}
|
||||
|
||||
public function user(): BelongsTo
|
||||
public function user(): HasOneThrough
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
return $this->hasOneThrough(
|
||||
User::class,
|
||||
projectUser::class,
|
||||
'project_id',
|
||||
'id',
|
||||
'id',
|
||||
'user_id'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|||
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOneThrough;
|
||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class Todo extends Model
|
||||
{
|
||||
|
@ -34,9 +36,17 @@ class Todo extends Model
|
|||
"updated_at" => "integer",
|
||||
];
|
||||
|
||||
public function user(): BelongsTo
|
||||
public function user(): Collection
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
// Select User given Todo
|
||||
return DB::table('users')
|
||||
->join('project_user', 'users.id', '=', 'project_user.user_id')
|
||||
->join('projects', 'project_user.project_id', '=', 'projects.id')
|
||||
->join('project_todo', 'projects.id', '=', 'project_todo.project_id')
|
||||
->join('todos', 'project_todo.todo_id', '=', 'todos.id')
|
||||
->where('todos.id', '=', $this->id)
|
||||
->select('users.*')
|
||||
->get();
|
||||
}
|
||||
|
||||
public function project(): HasOneThrough
|
||||
|
|
|
@ -16,7 +16,6 @@ class User extends Authenticatable
|
|||
{
|
||||
use HasApiTokens, HasFactory, Notifiable, UuidTrait;
|
||||
|
||||
protected $dateFormat = 'U';
|
||||
protected $table = 'users';
|
||||
|
||||
/**
|
||||
|
@ -50,13 +49,13 @@ class User extends Authenticatable
|
|||
'password' => 'hashed',
|
||||
];
|
||||
|
||||
public function projects(): BelongsToMany
|
||||
public function projects(): HasManyThrough
|
||||
{
|
||||
return $this->belongsToMany(Project::class, 'project_user', 'user_id', 'project_id');
|
||||
return $this->hasManyThrough(Project::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');
|
||||
// }
|
||||
public function todos(): HasManyThrough
|
||||
{
|
||||
return $this->hasManyThrough(Todo::class, projectUser::class, 'user_id', 'id', 'id', 'project_id');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,11 @@ class DatabaseSeeder extends Seeder
|
|||
*/
|
||||
public function run(): void
|
||||
{
|
||||
// \App\Models\User::factory(10)->create();
|
||||
\App\Models\User::factory(3)
|
||||
->has(\App\Models\Project::factory()->count(3)
|
||||
->has(\App\Models\Todo::factory()->count(10)))
|
||||
->create();
|
||||
|
||||
|
||||
// \App\Models\User::factory()->create([
|
||||
// 'name' => 'Test User',
|
||||
|
|
|
@ -1,24 +1,23 @@
|
|||
<x-app-layout>
|
||||
<x-slot name="header">
|
||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||
{{ __('Create Project') }}
|
||||
{{ __('Create Todo') }}
|
||||
</h2>
|
||||
</x-slot>
|
||||
|
||||
<!-- Create Project Form (Name, Description) -->
|
||||
<!-- Create Todo Form (title, description, due_start, due_end, completed_at(Checkbox)) -->
|
||||
<div class="py-4">
|
||||
<form method="POST" action="{{ route('project.store') }}" id="project-form">
|
||||
<form method="POST" action="{{ route('project.todo.store', $project) }}" id="todo-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')
|
||||
<label for="title" class="block mb-2 font-semibold">Title</label>
|
||||
<input type="text" name="title" id="title" placeholder="Title"
|
||||
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">
|
||||
@error('title')
|
||||
<div class="text-red-500 mt-2 text-sm">
|
||||
{{ $message }}
|
||||
</div>
|
||||
|
@ -28,7 +27,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') }}</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"></textarea>
|
||||
@error('description')
|
||||
<div class="text-red-500 mt-2 text-sm">
|
||||
{{ $message }}
|
||||
|
@ -36,16 +35,62 @@
|
|||
@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 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">
|
||||
|
||||
@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">
|
||||
@error('due_end')
|
||||
<div class="text-red-900 mt-2 text-sm">
|
||||
{{ $message }}
|
||||
</div>
|
||||
@enderror
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Completed at checkbox -->
|
||||
<div class="mt-4">
|
||||
<label for="completed_at" class="inline-flex items-center">
|
||||
<input type="checkbox" name="completed_at" id="completed_at"
|
||||
class="form-checkbox 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-5 h-5">
|
||||
<span class="ml-2 text-gray-700 dark:text-gray-400">Completed</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end mt-4 space-x-4">
|
||||
<!-- Cancel Button (GET request to index route) -->
|
||||
<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">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M15 19l-7-7 7-7"></path>
|
||||
</svg>
|
||||
Cancel
|
||||
</a>
|
||||
|
||||
<!-- Update Button -->
|
||||
<button type="submit"
|
||||
class="inline-flex items-center px-4 py-2 text-sm font-medium text-white bg-green-600 rounded-r-md hover:bg-green-800 focus:z-10 focus:ring-2 focus:ring-green-500 border border-grey-900 dark:border-white dark:text-white dark:hover:text-white dark:hover:bg-green-700 dark:focus:bg-green-700">
|
||||
<svg class="w-5 h-5 mr-2" aria-hidden="true" fill="none" viewBox="0 0 24 24"
|
||||
stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M5 13l4 4L19 7"></path>
|
||||
</svg>
|
||||
Create
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -62,6 +62,16 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Completed at checkbox -->
|
||||
<div class="mt-4">
|
||||
<label for="completed_at" class="inline-flex items-center">
|
||||
<input type="checkbox" name="completed_at" id="completed_at"
|
||||
class="form-checkbox 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-5 h-5"
|
||||
{{ old('completed_at', $todo->completed_at) ? 'checked' : '' }}>
|
||||
<span class="ml-2 text-gray-700 dark:text-gray-400">Completed</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end mt-4 space-x-4">
|
||||
<!-- Cancel Button (GET request to index route) -->
|
||||
<a href="{{ route('project.todo.index', $project) }}"
|
||||
|
|
|
@ -12,8 +12,13 @@ class ExampleTest extends TestCase
|
|||
*/
|
||||
public function test_the_application_returns_a_successful_response(): void
|
||||
{
|
||||
$response = $this->get('/');
|
||||
|
||||
// Gets redirected to login page if not logged in
|
||||
// Gets redirected to project page if logged in
|
||||
if (auth()->check()) {
|
||||
$response = $this->get(route('project.index'));
|
||||
} else {
|
||||
$response = $this->get(route('login'));
|
||||
}
|
||||
$response->assertStatus(200);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue