From 9df9c8d2f2770ec5272363ca76f005f298717ecb Mon Sep 17 00:00:00 2001 From: devoalda Date: Tue, 8 Aug 2023 15:47:47 +0800 Subject: [PATCH] feature(Pomo Feature): Added Pomo Tables + Features + Test Cases Added Livewire support --- app/Http/Controllers/PomoController.php | 114 ++++++++++++++++++ app/Http/Livewire/Dashboard/PomoCount.php | 34 ++++++ app/Http/Livewire/Dashboard/PomoTime.php | 47 ++++++++ app/Http/Livewire/Pomo/Create.php | 52 ++++++++ app/Http/Livewire/Pomo/Pomos.php | 32 +++++ app/Http/Requests/StorePomoRequest.php | 31 +++++ app/Http/Requests/UpdatePomoRequest.php | 28 +++++ app/Models/Pomo.php | 29 +++++ app/Models/Todo.php | 6 + app/Models/User.php | 26 +++- app/Policies/PomoPolicy.php | 71 +++++++++++ composer.json | 3 +- composer.lock | 75 +++++++++++- database/factories/PomoFactory.php | 26 ++++ .../2023_08_08_004442_create_pomos_table.php | 30 +++++ .../2023_08_08_005541_pomo_timestamps.php | 32 +++++ database/seeders/DatabaseSeeder.php | 11 +- database/seeders/PomoSeeder.php | 17 +++ resources/views/dashboard.blade.php | 3 + resources/views/layouts/app.blade.php | 2 + resources/views/layouts/navigation.blade.php | 11 ++ .../livewire/dashboard/pomo-count.blade.php | 19 +++ .../livewire/dashboard/pomo-time.blade.php | 19 +++ .../views/livewire/pomo/create.blade.php | 78 ++++++++++++ resources/views/livewire/pomo/pomos.blade.php | 62 ++++++++++ resources/views/pomo/create.blade.php | 13 ++ resources/views/pomo/index.blade.php | 12 ++ routes/web.php | 8 ++ tests/Feature/Project/PomoCRUDTest.php | 95 +++++++++++++++ 29 files changed, 975 insertions(+), 11 deletions(-) create mode 100644 app/Http/Controllers/PomoController.php create mode 100644 app/Http/Livewire/Dashboard/PomoCount.php create mode 100644 app/Http/Livewire/Dashboard/PomoTime.php create mode 100644 app/Http/Livewire/Pomo/Create.php create mode 100644 app/Http/Livewire/Pomo/Pomos.php create mode 100644 app/Http/Requests/StorePomoRequest.php create mode 100644 app/Http/Requests/UpdatePomoRequest.php create mode 100644 app/Models/Pomo.php create mode 100644 app/Policies/PomoPolicy.php create mode 100644 database/factories/PomoFactory.php create mode 100644 database/migrations/2023_08_08_004442_create_pomos_table.php create mode 100644 database/migrations/2023_08_08_005541_pomo_timestamps.php create mode 100644 database/seeders/PomoSeeder.php create mode 100644 resources/views/livewire/dashboard/pomo-count.blade.php create mode 100644 resources/views/livewire/dashboard/pomo-time.blade.php create mode 100644 resources/views/livewire/pomo/create.blade.php create mode 100644 resources/views/livewire/pomo/pomos.blade.php create mode 100644 resources/views/pomo/create.blade.php create mode 100644 resources/views/pomo/index.blade.php create mode 100644 tests/Feature/Project/PomoCRUDTest.php diff --git a/app/Http/Controllers/PomoController.php b/app/Http/Controllers/PomoController.php new file mode 100644 index 0000000..595de9e --- /dev/null +++ b/app/Http/Controllers/PomoController.php @@ -0,0 +1,114 @@ +authorize('create', Pomo::class); + + return view('pomo.create', [ + 'todo_id' => $todo_id, + 'pomo' => new Pomo(), + 'editing' => false, + ]); + } + + /** + * Store a newly created resource in storage. + */ + public function store(StorePomoRequest $request) + { +// $this->authorize('create', Pomo::class); + + // Convert due_start and end to unix timestamp and save + $pomo = new Pomo(); + $pomo->todo_id = $request->todo_id; + $pomo->pomo_start = strtotime($request->pomo_start); + $pomo->pomo_end = strtotime($request->pomo_end); + $pomo->notes = $request->notes; + $pomo->save(); + + return redirect()->route('pomo.index') + ->with('success', 'Pomo created successfully.'); + } + + /** + * Display the specified resource. + */ + public function show(Pomo $pomo) + { + $this->authorize('view', $pomo); + + return view('pomo.show', compact('pomo')); + } + + /** + * Show the form for editing the specified resource. + * @throws AuthorizationException + */ + public function edit(Pomo $pomo) + { + $this->authorize('view', $pomo); + + $editing = true; + + return view('pomo.create', compact('pomo', 'editing')); + } + + /** + * Update the specified resource in storage. + */ + public function update(UpdatePomoRequest $request, Pomo $pomo) + { +// $this->authorize('update', $pomo); + + // Convert due_start and end to unix timestamp and save + $pomo->pomo_start = strtotime($request->pomo_start); + $pomo->pomo_end = strtotime($request->pomo_end); + $pomo->notes = $request->notes; + $pomo->save(); + + return redirect()->route('pomo.index') + ->with('success', 'Pomo updated successfully.'); + } + + /** + * Remove the specified resource from storage. + * @throws AuthorizationException + */ + public function destroy(Pomo $pomo) + { + // Validate that the user is authorized to delete the pomo +// $this->authorize('delete', $pomo); + + $pomo->delete(); + + return redirect()->route('pomo.index') + ->with('success', 'Pomo deleted successfully.'); + + } +} diff --git a/app/Http/Livewire/Dashboard/PomoCount.php b/app/Http/Livewire/Dashboard/PomoCount.php new file mode 100644 index 0000000..206a4ac --- /dev/null +++ b/app/Http/Livewire/Dashboard/PomoCount.php @@ -0,0 +1,34 @@ +user()->id); + $todos = $user->todos()->map(function ($todo) { + $todo = \App\Models\Todo::find($todo->id); + $todo->pomos = Pomo::where('todo_id', $todo->id); + return $todo; + }); + + // Get the average pomo count per todo + $ave_pomo_count = $todos->avg(function ($todo) { + return $todo->pomos->count(); + }); + $this->ave_pomo_count = $ave_pomo_count; + } + + public function render() + { + return view('livewire.dashboard.pomo-count', [ + 'ave_pomo_count' => $this->ave_pomo_count, + ]); + } +} diff --git a/app/Http/Livewire/Dashboard/PomoTime.php b/app/Http/Livewire/Dashboard/PomoTime.php new file mode 100644 index 0000000..f29d762 --- /dev/null +++ b/app/Http/Livewire/Dashboard/PomoTime.php @@ -0,0 +1,47 @@ +user()->id); + + // Get all pomos and calculate the average time spent per todo (due_end - due_start)/count/total pomos + $pomos = $user->pomos(); + $pomos = $pomos->map(function ($pomo) { + $pomo->todo = Todo::find($pomo->todo_id); + $pomo->project = Project::find($pomo->todo->project_id); + return $pomo; + }); + + $total_pomos = $pomos->count(); + + $total_time = 0; + + foreach ($pomos as $pomo) { + $total_time += $pomo->pomo_end - $pomo->pomo_start; + } + + $this->ave_pomo_time = $total_time / $total_pomos; + + } + + + public function render() + { + return view('livewire.dashboard.pomo-time', [ + 'ave_pomo_time' => $this->ave_pomo_time, + ]); + } +} diff --git a/app/Http/Livewire/Pomo/Create.php b/app/Http/Livewire/Pomo/Create.php new file mode 100644 index 0000000..9080343 --- /dev/null +++ b/app/Http/Livewire/Pomo/Create.php @@ -0,0 +1,52 @@ +user = User::find(auth()->id()); + $this->projects = $this->user->projects; + $this->load_incomplete_todos(null); + $this->pomo = $pomo; + $this->editing = $editing; + } + + public function load_incomplete_todos($project_id = null, $editing = false) + { + $incomplete_todos = new Collection(); + + foreach ($this->projects as $project) { + $todos = $project->todos()->where('completed_at', null); + if ($project_id) { + $todos = $todos->where('project_id', $project_id); + } + $incomplete_todos = $incomplete_todos->merge($todos->get()); + } + $this->incomplete_todos = $incomplete_todos; + } + + public function render() + { + return view('livewire.pomo.create', [ + 'projects' => $this->projects, + 'incomplete_todos' => $this->incomplete_todos, + 'pomo' => $this->pomo, + 'editing' => $this->editing, + ]); + } +} diff --git a/app/Http/Livewire/Pomo/Pomos.php b/app/Http/Livewire/Pomo/Pomos.php new file mode 100644 index 0000000..d76580d --- /dev/null +++ b/app/Http/Livewire/Pomo/Pomos.php @@ -0,0 +1,32 @@ +id()); + $pomos = $user->pomos(); + + // Convert Pomos from Collection to class + $pomos = $pomos->map(function ($pomo) { + $pomo->todo = Todo::find($pomo->todo_id); + $pomo->project = Project::find($pomo->todo->project_id); + return $pomo; + }); + + + return view('livewire.pomo.pomos', [ + 'pomos' => $pomos, + ]); + } +} diff --git a/app/Http/Requests/StorePomoRequest.php b/app/Http/Requests/StorePomoRequest.php new file mode 100644 index 0000000..28cc5c8 --- /dev/null +++ b/app/Http/Requests/StorePomoRequest.php @@ -0,0 +1,31 @@ +check(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules(): array + { + return [ + 'todo_id' => 'required|exists:todos,id', + 'pomo_start' => 'required|date', + 'pomo_end' => 'required|date', + 'notes' => 'nullable|string', + ]; + } +} diff --git a/app/Http/Requests/UpdatePomoRequest.php b/app/Http/Requests/UpdatePomoRequest.php new file mode 100644 index 0000000..04390aa --- /dev/null +++ b/app/Http/Requests/UpdatePomoRequest.php @@ -0,0 +1,28 @@ +check(); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules(): array + { + return [ + // + ]; + } +} diff --git a/app/Models/Pomo.php b/app/Models/Pomo.php new file mode 100644 index 0000000..8a2f851 --- /dev/null +++ b/app/Models/Pomo.php @@ -0,0 +1,29 @@ +belongsTo(Todo::class); + } + +} diff --git a/app/Models/Todo.php b/app/Models/Todo.php index 4b86744..e01a134 100644 --- a/app/Models/Todo.php +++ b/app/Models/Todo.php @@ -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\HasMany; use Illuminate\Database\Eloquent\Relations\HasManyThrough; use Illuminate\Database\Eloquent\Relations\HasOneThrough; use Illuminate\Database\Eloquent\Relations\MorphTo; @@ -54,4 +55,9 @@ class Todo extends Model return $this->hasOneThrough(Project::class, projectTodo::class, 'todo_id', 'id', 'id', 'project_id'); } + + public function pomo(): HasMany + { + return $this->hasMany(Pomo::class); + } } diff --git a/app/Models/User.php b/app/Models/User.php index b268281..5d72dfd 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -10,6 +10,8 @@ use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasManyThrough; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; +use Illuminate\Support\Collection; +use Illuminate\Support\Facades\DB; use Laravel\Sanctum\HasApiTokens; class User extends Authenticatable @@ -54,8 +56,28 @@ class User extends Authenticatable return $this->belongsToMany(Project::class, projectUser::class, 'user_id', 'project_id'); } - public function todos(): HasManyThrough + public function todos(): Collection { - return $this->hasManyThrough(Todo::class, Project::class, 'owner_id', 'project_id'); + return DB::table('todos') + ->join('project_todo', 'todos.id', '=', 'project_todo.todo_id') + ->join('projects', 'project_todo.project_id', '=', 'projects.id') + ->join('project_user', 'projects.id', '=', 'project_user.project_id') + ->join('users', 'project_user.user_id', '=', 'users.id') + ->where('users.id', '=', $this->id) + ->select('todos.*') + ->get(); + } + + public function pomos(): Collection + { + return DB::table('pomos') + ->join('todos', 'pomos.todo_id', '=', 'todos.id') + ->join('project_todo', 'todos.id', '=', 'project_todo.todo_id') + ->join('projects', 'project_todo.project_id', '=', 'projects.id') + ->join('project_user', 'projects.id', '=', 'project_user.project_id') + ->join('users', 'project_user.user_id', '=', 'users.id') + ->where('users.id', '=', $this->id) + ->select('pomos.*') + ->get(); } } diff --git a/app/Policies/PomoPolicy.php b/app/Policies/PomoPolicy.php new file mode 100644 index 0000000..d2a0475 --- /dev/null +++ b/app/Policies/PomoPolicy.php @@ -0,0 +1,71 @@ +id === $pomo->todo->project->user->id; + } + + /** + * Determine whether the user can create models. + */ + public function create(User $user): bool + { + return true; + } + + /** + * Determine whether the user can update the model. + */ + public function update(User $user, Pomo $pomo): bool + { + return $user->id === $pomo->todo->project->user->id; + } + + /** + * Determine whether the user can delete the model. + */ + public function delete(User $user, Pomo $pomo): bool + { + // Check if the user is the owner of the pomo + return $user->id === $pomo->todo->project->user->id; + } + + /** + * Determine whether the user can restore the model. + */ + public function restore(User $user, Pomo $pomo): bool + { + return $user->id === $pomo->todo->project->user->id; + } + + /** + * Determine whether the user can permanently delete the model. + */ + public function forceDelete(User $user, Pomo $pomo): bool + { + return $user->id === $pomo->todo->project->user->id; + } +} diff --git a/composer.json b/composer.json index 261c38f..862b74d 100644 --- a/composer.json +++ b/composer.json @@ -10,7 +10,8 @@ "guzzlehttp/guzzle": "^7.2", "laravel/framework": "^10.10", "laravel/sanctum": "^3.2", - "laravel/tinker": "^2.8" + "laravel/tinker": "^2.8", + "livewire/livewire": "^2.12" }, "require-dev": { "fakerphp/faker": "^1.9.1", diff --git a/composer.lock b/composer.lock index b55b256..8110104 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c0c3fcbb476f8d39c32adb863b4ed706", + "content-hash": "a640a54c051b539eff089566e9e976ee", "packages": [ { "name": "brick/math", @@ -2151,6 +2151,79 @@ ], "time": "2023-08-03T07:14:11+00:00" }, + { + "name": "livewire/livewire", + "version": "v2.12.5", + "source": { + "type": "git", + "url": "https://github.com/livewire/livewire.git", + "reference": "96a249f5ab51d8377817d802f91d1e440869c1d6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/livewire/livewire/zipball/96a249f5ab51d8377817d802f91d1e440869c1d6", + "reference": "96a249f5ab51d8377817d802f91d1e440869c1d6", + "shasum": "" + }, + "require": { + "illuminate/database": "^7.0|^8.0|^9.0|^10.0", + "illuminate/support": "^7.0|^8.0|^9.0|^10.0", + "illuminate/validation": "^7.0|^8.0|^9.0|^10.0", + "league/mime-type-detection": "^1.9", + "php": "^7.2.5|^8.0", + "symfony/http-kernel": "^5.0|^6.0" + }, + "require-dev": { + "calebporzio/sushi": "^2.1", + "laravel/framework": "^7.0|^8.0|^9.0|^10.0", + "mockery/mockery": "^1.3.1", + "orchestra/testbench": "^5.0|^6.0|^7.0|^8.0", + "orchestra/testbench-dusk": "^5.2|^6.0|^7.0|^8.0", + "phpunit/phpunit": "^8.4|^9.0", + "psy/psysh": "@stable" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Livewire\\LivewireServiceProvider" + ], + "aliases": { + "Livewire": "Livewire\\Livewire" + } + } + }, + "autoload": { + "files": [ + "src/helpers.php" + ], + "psr-4": { + "Livewire\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Caleb Porzio", + "email": "calebporzio@gmail.com" + } + ], + "description": "A front-end framework for Laravel.", + "support": { + "issues": "https://github.com/livewire/livewire/issues", + "source": "https://github.com/livewire/livewire/tree/v2.12.5" + }, + "funding": [ + { + "url": "https://github.com/livewire", + "type": "github" + } + ], + "time": "2023-08-02T06:31:31+00:00" + }, { "name": "monolog/monolog", "version": "3.4.0", diff --git a/database/factories/PomoFactory.php b/database/factories/PomoFactory.php new file mode 100644 index 0000000..31d36df --- /dev/null +++ b/database/factories/PomoFactory.php @@ -0,0 +1,26 @@ + + */ +class PomoFactory extends Factory +{ + /** + * Define the model's default state. + * + * @return array + */ + public function definition(): array + { + return [ + 'todo_id' => $this->faker->uuid(), + 'notes' => $this->faker->text(), + 'pomo_start' => $this->faker->unixTime(), + 'pomo_end' => $this->faker->unixTime(), + ]; + } +} diff --git a/database/migrations/2023_08_08_004442_create_pomos_table.php b/database/migrations/2023_08_08_004442_create_pomos_table.php new file mode 100644 index 0000000..c8f4d13 --- /dev/null +++ b/database/migrations/2023_08_08_004442_create_pomos_table.php @@ -0,0 +1,30 @@ +uuid('id')->primary(); + $table->foreignUuid('todo_id')->constrained('todos')->onDelete('cascade'); + $table->text('notes')->nullable(); + $table->integer('pomo_start')->nullable(); + $table->integer('pomo_end')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('pomos'); + } +}; diff --git a/database/migrations/2023_08_08_005541_pomo_timestamps.php b/database/migrations/2023_08_08_005541_pomo_timestamps.php new file mode 100644 index 0000000..8ff4d76 --- /dev/null +++ b/database/migrations/2023_08_08_005541_pomo_timestamps.php @@ -0,0 +1,32 @@ +integer('created_at')->nullable(); + $table->integer('updated_at')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('pomos', function (Blueprint $table) { + // Drop created_at and updated_at columns from pomos table + $table->dropColumn('created_at'); + $table->dropColumn('updated_at'); + }); + } +}; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 3ba91b1..b89cccf 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -3,6 +3,7 @@ namespace Database\Seeders; // use Illuminate\Database\Console\Seeds\WithoutModelEvents; +use App\Models\User; use Illuminate\Database\Seeder; class DatabaseSeeder extends Seeder @@ -14,13 +15,9 @@ class DatabaseSeeder extends Seeder { \App\Models\User::factory(3) ->has(\App\Models\Project::factory()->count(3) - ->has(\App\Models\Todo::factory()->count(10))) + ->has(\App\Models\Todo::factory()->count(10) + ->has(\App\Models\Pomo::factory()->count(4)) + )) ->create(); - - - // \App\Models\User::factory()->create([ - // 'name' => 'Test User', - // 'email' => 'test@example.com', - // ]); } } diff --git a/database/seeders/PomoSeeder.php b/database/seeders/PomoSeeder.php new file mode 100644 index 0000000..e8a1799 --- /dev/null +++ b/database/seeders/PomoSeeder.php @@ -0,0 +1,17 @@ + + + + diff --git a/resources/views/layouts/app.blade.php b/resources/views/layouts/app.blade.php index 02de404..a7ce9bd 100644 --- a/resources/views/layouts/app.blade.php +++ b/resources/views/layouts/app.blade.php @@ -13,6 +13,7 @@ @vite(['resources/css/app.css', 'resources/js/app.js']) + @livewireStyles
@@ -35,5 +36,6 @@ {{ $slot }}
+ @livewireScripts diff --git a/resources/views/layouts/navigation.blade.php b/resources/views/layouts/navigation.blade.php index becf087..909785d 100644 --- a/resources/views/layouts/navigation.blade.php +++ b/resources/views/layouts/navigation.blade.php @@ -23,6 +23,13 @@ {{ __('Projects') }} + + + @@ -85,6 +92,10 @@ {{ __('Projects') }} + + + {{ __('Pomodoro') }} + diff --git a/resources/views/livewire/dashboard/pomo-count.blade.php b/resources/views/livewire/dashboard/pomo-count.blade.php new file mode 100644 index 0000000..dfdc3d0 --- /dev/null +++ b/resources/views/livewire/dashboard/pomo-count.blade.php @@ -0,0 +1,19 @@ +
+

+ + + + + Average Pomos per Project +

+

+ {{ $ave_pomo_count }} +

+
diff --git a/resources/views/livewire/dashboard/pomo-time.blade.php b/resources/views/livewire/dashboard/pomo-time.blade.php new file mode 100644 index 0000000..37cc69b --- /dev/null +++ b/resources/views/livewire/dashboard/pomo-time.blade.php @@ -0,0 +1,19 @@ +
+

+ + + + + Average Pomos time spent per Project +

+

+ {{ $ave_pomo_time }} +

+
diff --git a/resources/views/livewire/pomo/create.blade.php b/resources/views/livewire/pomo/create.blade.php new file mode 100644 index 0000000..3e63efe --- /dev/null +++ b/resources/views/livewire/pomo/create.blade.php @@ -0,0 +1,78 @@ +
+
+ @csrf + @if($editing) + @method('PUT') + @endif + +
+
+
+ +
+ + + @error('todo_id') +
+ {{ $message }} +
+ @enderror +
+ +
+ + + @error('notes') +
+ {{ $message }} +
+ @enderror +
+ +
+ +
+
+ + + @error('pomo_start') +
+ {{ $message }} +
+ @enderror +
+ +
+ + + @error('pomo_end') +
+ {{ $message }} +
+ @enderror +
+
+ +
+ +
+ +
+
+
+ +
diff --git a/resources/views/livewire/pomo/pomos.blade.php b/resources/views/livewire/pomo/pomos.blade.php new file mode 100644 index 0000000..d394efa --- /dev/null +++ b/resources/views/livewire/pomo/pomos.blade.php @@ -0,0 +1,62 @@ +
+ + + + + + + + + + + + + @foreach ($pomos as $pomo) + + + + + + + + + + + @endforeach + +
TaskStartEndDuration (minutes)NotesActions
+ + {{ $pomo->todo->title }} + + + {{ \Carbon\Carbon::createFromTimestamp($pomo->pomo_start)->format('Y-m-d H:i:s') }} + + {{ \Carbon\Carbon::createFromTimestamp($pomo->pomo_end)->format('Y-m-d H:i:s') }} + + {{ + \Carbon\Carbon::createFromTimestamp($pomo->pomo_start)->diffInMinutes(\Carbon\Carbon::createFromTimestamp($pomo->pomo_end)) + }} + + +
+ {{ $pomo->notes }} +
+
+ +
+
+ Edit +
+
+
+ @csrf + @method('DELETE') + +
+
+
+
+
diff --git a/resources/views/pomo/create.blade.php b/resources/views/pomo/create.blade.php new file mode 100644 index 0000000..1bdb365 --- /dev/null +++ b/resources/views/pomo/create.blade.php @@ -0,0 +1,13 @@ + + +

+ {{ __($editing ? 'Edit' : 'Create') }} {{ __('Pomo') }} + +

+
+ + + +
+ + diff --git a/resources/views/pomo/index.blade.php b/resources/views/pomo/index.blade.php new file mode 100644 index 0000000..3befde2 --- /dev/null +++ b/resources/views/pomo/index.blade.php @@ -0,0 +1,12 @@ + + +

+ {{ __('Pomodoro Dashboard') }} + Create Pomo + +

+
+ + + +
diff --git a/routes/web.php b/routes/web.php index 4a674ee..6cb7bf1 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,6 +1,7 @@ middleware(['auth', 'verified']) ->name('dashboard'); +Route::resource('pomo', PomoController::class) + ->middleware([ + 'auth', + 'verified', + 'web' + ]); + Route::resource('project.todo', ProjectTodoController::class) ->middleware([ 'auth', diff --git a/tests/Feature/Project/PomoCRUDTest.php b/tests/Feature/Project/PomoCRUDTest.php new file mode 100644 index 0000000..97225c0 --- /dev/null +++ b/tests/Feature/Project/PomoCRUDTest.php @@ -0,0 +1,95 @@ +actingAs($user = User::factory()->create()); + $this->user = $user; + $this->assertAuthenticated(); + $this->todo = Todo::factory()->create(); + + } + + public function test_user_can_create_pomo(): void + { + $now = now(); + $end = now()->addMinutes(25); + // Create a pomo through POST and store it in the pomo property + $response = $this->post(route('pomo.store'), [ + 'todo_id' => $this->todo->id, + 'notes' => 'Test Notes', + 'pomo_start' => $now, + 'pomo_end' => $end, + ]); + $response->assertRedirect(route('pomo.index')); + $this->assertDatabaseHas('pomos', [ + 'todo_id' => $this->todo->id, + 'notes' => 'Test Notes', + 'pomo_start' => strtotime($now), + 'pomo_end' => strtotime($end), + ]); + + $this->pomo = Pomo::where('todo_id', $this->todo->id)->first(); + } + + public function test_user_can_view_pomo(): void + { + $this->test_user_can_create_pomo(); + $this->assertDatabaseHas('pomos', [ + 'todo_id' => $this->todo->id, + 'notes' => 'Test Notes', + ]); + + } + + public function test_user_can_update_pomo_with_authorsation(): void + { + $this->test_user_can_create_pomo(); + $now = now(); + $end = now()->addMinutes(25); + $response = $this->put(route('pomo.update', $this->pomo->id), [ + 'todo_id' => $this->todo->id, + 'notes' => 'Test Notes Updated', + 'pomo_start' => $now, + 'pomo_end' => $end, + ]); + $response->assertRedirect(route('pomo.index')); + $this->assertDatabaseHas('pomos', [ + 'todo_id' => $this->todo->id, + 'notes' => 'Test Notes Updated', + 'pomo_start' => strtotime($now), + 'pomo_end' => strtotime($end), + ]); + } + + public function test_user_can_delete_pomo_with_authorsation(): void + { + $this->test_user_can_create_pomo(); + $response = $this->delete(route('pomo.destroy', $this->pomo->id)); + $response->assertRedirect(route('pomo.index')); + $this->assertDatabaseMissing('pomos', [ + 'todo_id' => $this->todo->id, + 'notes' => 'Test Notes', + ]); + } + +}