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;
|
$projects = $user->projects;
|
||||||
$project = $projects->find($project_id);
|
$project = $projects->find($project_id);
|
||||||
|
|
||||||
|
if (!$project || $project->user->id !== auth()->user()->id)
|
||||||
|
return back()->with('error', 'Project not found');
|
||||||
|
|
||||||
$todos = $project->todos;
|
$todos = $project->todos;
|
||||||
|
|
||||||
return view('todo.index', [
|
return view('todo.index', [
|
||||||
|
@ -40,9 +43,12 @@ class ProjectTodoController extends Controller
|
||||||
/**
|
/**
|
||||||
* Show the form for creating a new resource.
|
* 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;
|
$projects = $user->projects;
|
||||||
$project = $projects->find($project_id);
|
$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'));
|
return view('todo.show', compact('project', 'todo'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,6 +111,9 @@ class ProjectTodoController extends Controller
|
||||||
$projects = $user->projects;
|
$projects = $user->projects;
|
||||||
$project = $projects->find($project_id);
|
$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)
|
// Check if the given todo is in the given project (Reverse find with todo's project_id)
|
||||||
if ($todo->project->id !== $project_id)
|
if ($todo->project->id !== $project_id)
|
||||||
return back()->with('error', 'Todo not found in the given project');
|
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');
|
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\HasManyThrough;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasOneThrough;
|
use Illuminate\Database\Eloquent\Relations\HasOneThrough;
|
||||||
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
use Illuminate\Database\Eloquent\Relations\MorphTo;
|
||||||
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
class Todo extends Model
|
class Todo extends Model
|
||||||
{
|
{
|
||||||
|
@ -34,9 +36,17 @@ class Todo extends Model
|
||||||
"updated_at" => "integer",
|
"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
|
public function project(): HasOneThrough
|
||||||
|
|
|
@ -16,7 +16,6 @@ class User extends Authenticatable
|
||||||
{
|
{
|
||||||
use HasApiTokens, HasFactory, Notifiable, UuidTrait;
|
use HasApiTokens, HasFactory, Notifiable, UuidTrait;
|
||||||
|
|
||||||
protected $dateFormat = 'U';
|
|
||||||
protected $table = 'users';
|
protected $table = 'users';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -50,13 +49,13 @@ class User extends Authenticatable
|
||||||
'password' => 'hashed',
|
'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
|
public function todos(): HasManyThrough
|
||||||
// {
|
{
|
||||||
// return $this->hasManyThrough(Todo::class, projectUser::class, 'user_id', 'id', 'id', 'project_id');
|
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
|
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([
|
// \App\Models\User::factory()->create([
|
||||||
// 'name' => 'Test User',
|
// 'name' => 'Test User',
|
||||||
|
|
|
@ -1,24 +1,23 @@
|
||||||
<x-app-layout>
|
<x-app-layout>
|
||||||
<x-slot name="header">
|
<x-slot name="header">
|
||||||
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
|
||||||
{{ __('Create Project') }}
|
{{ __('Create Todo') }}
|
||||||
</h2>
|
</h2>
|
||||||
</x-slot>
|
</x-slot>
|
||||||
|
|
||||||
<!-- Create Project Form (Name, Description) -->
|
<!-- Create Todo Form (title, description, due_start, due_end, completed_at(Checkbox)) -->
|
||||||
<div class="py-4">
|
<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
|
@csrf
|
||||||
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
|
<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="bg-white dark:bg-gray-800 shadow-sm rounded-lg p-6">
|
||||||
<div class="text-gray-800 dark:text-gray-100">
|
<div class="text-gray-800 dark:text-gray-100">
|
||||||
|
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label for="name" class="block mb-2 font-semibold">Name</label>
|
<label for="title" class="block mb-2 font-semibold">Title</label>
|
||||||
<input type="text" name="name" id="name" placeholder="Name"
|
<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('name') border-red-500 @enderror"
|
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('name') }}">
|
@error('title')
|
||||||
@error('name')
|
|
||||||
<div class="text-red-500 mt-2 text-sm">
|
<div class="text-red-500 mt-2 text-sm">
|
||||||
{{ $message }}
|
{{ $message }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -28,7 +27,7 @@
|
||||||
<div class="mb-4">
|
<div class="mb-4">
|
||||||
<label for="description" class="block mb-2 font-semibold">Description</label>
|
<label for="description" class="block mb-2 font-semibold">Description</label>
|
||||||
<textarea name="description" id="description" placeholder="Description"
|
<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')
|
@error('description')
|
||||||
<div class="text-red-500 mt-2 text-sm">
|
<div class="text-red-500 mt-2 text-sm">
|
||||||
{{ $message }}
|
{{ $message }}
|
||||||
|
@ -36,16 +35,62 @@
|
||||||
@enderror
|
@enderror
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
<button type="submit"
|
<div>
|
||||||
class="bg-blue-500 hover:bg-blue-600 text-white font-semibold px-4 py-2 rounded-lg">
|
<label for="due_start" class="block mb-2 font-semibold">Due Start</label>
|
||||||
Create Project
|
<input type="datetime-local" name="due_start" id="due_start" placeholder="Due Start"
|
||||||
</button>
|
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>
|
<!-- Completed at checkbox -->
|
||||||
</div>
|
<div class="mt-4">
|
||||||
</div>
|
<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>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,16 @@
|
||||||
</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"
|
||||||
|
{{ 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">
|
<div class="flex justify-end mt-4 space-x-4">
|
||||||
<!-- Cancel Button (GET request to index route) -->
|
<!-- Cancel Button (GET request to index route) -->
|
||||||
<a href="{{ route('project.todo.index', $project) }}"
|
<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
|
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);
|
$response->assertStatus(200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue