From 967cad330921a0f857bf28c0497de84d7b04fd80 Mon Sep 17 00:00:00 2001 From: Kamil Date: Sat, 23 Sep 2023 09:47:28 +0200 Subject: [PATCH 01/13] #8 - semester crud --- .env.ci | 1 - app/Enums/SemesterStatus.php | 19 ++++++ .../Dashboard/SemesterController.php | 62 ++++++++++++++++++ app/Http/Requests/SemesterRequest.php | 17 +++++ app/Http/Resources/SemesterResource.php | 24 +++++++ app/Models/Semester.php | 32 ++++++++++ app/Models/Student.php | 9 +++ app/Models/User.php | 6 ++ database/factories/SemesterFactory.php | 17 +++++ ...23_09_23_075139_create_semesters_table.php | 24 +++++++ resources/js/Layouts/DashboardLayout.vue | 2 +- .../js/Pages/Dashboard/Semester/Create.vue | 48 ++++++++++++++ .../js/Pages/Dashboard/Semester/Edit.vue | 52 +++++++++++++++ .../js/Pages/Dashboard/Semester/Index.vue | 63 ++++++++++++++++++ routes/web.php | 9 +++ tests/Feature/SemesterTest.php | 64 +++++++++++++++++++ tests/Feature/StudentTest.php | 2 +- 17 files changed, 448 insertions(+), 3 deletions(-) create mode 100644 app/Enums/SemesterStatus.php create mode 100644 app/Http/Controllers/Dashboard/SemesterController.php create mode 100644 app/Http/Requests/SemesterRequest.php create mode 100644 app/Http/Resources/SemesterResource.php create mode 100644 app/Models/Semester.php create mode 100644 database/factories/SemesterFactory.php create mode 100644 database/migrations/2023_09_23_075139_create_semesters_table.php create mode 100644 resources/js/Pages/Dashboard/Semester/Create.vue create mode 100644 resources/js/Pages/Dashboard/Semester/Edit.vue create mode 100644 resources/js/Pages/Dashboard/Semester/Index.vue create mode 100644 tests/Feature/SemesterTest.php diff --git a/.env.ci b/.env.ci index c590d6f..849585b 100644 --- a/.env.ci +++ b/.env.ci @@ -21,4 +21,3 @@ DB_PORT=5432 DB_DATABASE=keating DB_USERNAME=keating DB_PASSWORD=password -DB_ROOT_PASSWORD=example diff --git a/app/Enums/SemesterStatus.php b/app/Enums/SemesterStatus.php new file mode 100644 index 0000000..26e7462 --- /dev/null +++ b/app/Enums/SemesterStatus.php @@ -0,0 +1,19 @@ +value) { + "active" => "aktywny", + "inactive" => "nieaktywny", + }; + } +} diff --git a/app/Http/Controllers/Dashboard/SemesterController.php b/app/Http/Controllers/Dashboard/SemesterController.php new file mode 100644 index 0000000..d6c3f78 --- /dev/null +++ b/app/Http/Controllers/Dashboard/SemesterController.php @@ -0,0 +1,62 @@ + SemesterResource::collection($semesters), + ]); + } + + public function create(): Response + { + return inertia("Dashboard/Semester/Create"); + } + + public function store(SemesterRequest $request): RedirectResponse + { + Semester::query()->create($request->validated()); + + return redirect() + ->route("semesters.index") + ->with("success", "Dodano semestr"); + } + + public function edit(Semester $semester): Response + { + return inertia("Dashboard/Semester/Edit", [ + "semester" => $semester, + ]); + } + + public function update(SemesterRequest $request, Semester $semester): RedirectResponse + { + $semester->update($request->validated()); + + return redirect() + ->route("semesters.index") + ->with("success", "Zaktualizowano semestr"); + } + + public function destroy(Semester $semester): RedirectResponse + { + $semester->delete(); + + return redirect()->back() + ->with("success", "Usunięto semestr"); + } +} diff --git a/app/Http/Requests/SemesterRequest.php b/app/Http/Requests/SemesterRequest.php new file mode 100644 index 0000000..9848c9f --- /dev/null +++ b/app/Http/Requests/SemesterRequest.php @@ -0,0 +1,17 @@ + ["required", "max:255"], + ]; + } +} diff --git a/app/Http/Resources/SemesterResource.php b/app/Http/Resources/SemesterResource.php new file mode 100644 index 0000000..c3d12d7 --- /dev/null +++ b/app/Http/Resources/SemesterResource.php @@ -0,0 +1,24 @@ + $semester->name, + "status" => $semester->status, + "status_label" => $semester->status->getLabel(), + ]; + } +} diff --git a/app/Models/Semester.php b/app/Models/Semester.php new file mode 100644 index 0000000..7e3eccd --- /dev/null +++ b/app/Models/Semester.php @@ -0,0 +1,32 @@ + SemesterStatus::class, + ]; +} diff --git a/app/Models/Student.php b/app/Models/Student.php index 004e4f3..a40f402 100644 --- a/app/Models/Student.php +++ b/app/Models/Student.php @@ -4,10 +4,19 @@ namespace App\Models; +use Carbon\Carbon; use Illuminate\Database\Eloquent\Concerns\HasUlids; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +/** + * @property string $id + * @property string $name + * @property string $surname + * @property string $index_number + * @property Carbon $created_at + * @property Carbon $updated_at + */ class Student extends Model { use HasFactory; diff --git a/app/Models/User.php b/app/Models/User.php index 5da4bc2..1321513 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -9,6 +9,12 @@ use Illuminate\Notifications\Notifiable; use Laravel\Sanctum\HasApiTokens; +/** + * @property string $id + * @property string $name + * @property string $email + * @property string $password + */ class User extends Authenticatable { use HasApiTokens; diff --git a/database/factories/SemesterFactory.php b/database/factories/SemesterFactory.php new file mode 100644 index 0000000..914628c --- /dev/null +++ b/database/factories/SemesterFactory.php @@ -0,0 +1,17 @@ + "Semestr" . fake()->numberBetween(1, 7), + ]; + } +} diff --git a/database/migrations/2023_09_23_075139_create_semesters_table.php b/database/migrations/2023_09_23_075139_create_semesters_table.php new file mode 100644 index 0000000..e3eb53c --- /dev/null +++ b/database/migrations/2023_09_23_075139_create_semesters_table.php @@ -0,0 +1,24 @@ +ulid("id")->primary(); + $table->string("name"); + $table->enum("status", ["active", "inactive"])->default("inactive"); + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists("semesters"); + } +}; diff --git a/resources/js/Layouts/DashboardLayout.vue b/resources/js/Layouts/DashboardLayout.vue index 830d61e..be9dd9b 100644 --- a/resources/js/Layouts/DashboardLayout.vue +++ b/resources/js/Layouts/DashboardLayout.vue @@ -54,7 +54,7 @@ const navigation = [ elements: [ { name: 'Kursy', href: '#', icon: BriefcaseIcon, current: false }, { name: 'Kierunki i specjalności', href: '#', icon: MagnifyingGlassIcon, current: false }, - { name: 'Semestry', href: '#', icon: ClipboardIcon, current: false }, + { name: 'Semestry', href: '/dashboard/semesters', icon: ClipboardIcon, current: false }, { name: 'Formy zajęć', href: '#', icon: CodeBracketSquareIcon, current: false }, { name: 'Tryby studiów', href: '#', icon: ClockIcon, current: false }, ], diff --git a/resources/js/Pages/Dashboard/Semester/Create.vue b/resources/js/Pages/Dashboard/Semester/Create.vue new file mode 100644 index 0000000..1f1669f --- /dev/null +++ b/resources/js/Pages/Dashboard/Semester/Create.vue @@ -0,0 +1,48 @@ + + + diff --git a/resources/js/Pages/Dashboard/Semester/Edit.vue b/resources/js/Pages/Dashboard/Semester/Edit.vue new file mode 100644 index 0000000..e45cc24 --- /dev/null +++ b/resources/js/Pages/Dashboard/Semester/Edit.vue @@ -0,0 +1,52 @@ + + + diff --git a/resources/js/Pages/Dashboard/Semester/Index.vue b/resources/js/Pages/Dashboard/Semester/Index.vue new file mode 100644 index 0000000..2d13497 --- /dev/null +++ b/resources/js/Pages/Dashboard/Semester/Index.vue @@ -0,0 +1,63 @@ + + + diff --git a/routes/web.php b/routes/web.php index 048cad3..99bb4b0 100644 --- a/routes/web.php +++ b/routes/web.php @@ -3,6 +3,7 @@ declare(strict_types=1); use App\Http\Controllers\Dashboard\DashboardController; +use App\Http\Controllers\Dashboard\SemesterController; use App\Http\Controllers\Dashboard\StudentController; use App\Http\Controllers\Public\HomeController; use App\Http\Controllers\Public\NewsController; @@ -21,4 +22,12 @@ Route::patch("/students/{student}", "update")->name("students.update"); Route::delete("/students/{student}", "destroy")->name("students.destroy"); }); + Route::controller(SemesterController::class)->group(function (): void { + Route::get("/semesters", "index")->name("semesters.index"); + Route::get("/semesters/create", "create")->name("semesters.create"); + Route::post("/semesters", "store")->name("semesters.store"); + Route::get("/semesters/{semester}/edit", "edit")->name("semesters.edit"); + Route::patch("/semesters/{semester}", "update")->name("semesters.update"); + Route::delete("/semesters/{semester}", "destroy")->name("semesters.destroy"); + }); }); diff --git a/tests/Feature/SemesterTest.php b/tests/Feature/SemesterTest.php new file mode 100644 index 0000000..92238bb --- /dev/null +++ b/tests/Feature/SemesterTest.php @@ -0,0 +1,64 @@ +assertDatabaseCount("semesters", 0); + + $this->post("/dashboard/semesters", [ + "name" => "Semester 1", + ])->assertSessionHasNoErrors(); + + $this->assertDatabaseCount("semesters", 1); + } + + public function testSemesterCanBeUpdated(): void + { + $semester = Semester::factory()->create(); + + $this->assertDatabaseMissing("semesters", [ + "name" => "Semester 1", + ]); + + $this->patch("/dashboard/semesters/{$semester->id}", [ + "name" => "Semester 1", + ])->assertSessionHasNoErrors(); + + $this->assertDatabaseHas("semesters", [ + "name" => "Semester 1", + ]); + } + + public function testSemesterCannotBeCreatedWithInvalidData(): void + { + $this->post("/dashboard/semesters", [ + "name" => Str::random(256), + ])->assertSessionHasErrors([ + "name", + ]); + + $this->assertDatabaseCount("semesters", 0); + } + + public function testSemesterCanBeDeleted(): void + { + $semester = Semester::factory()->create(); + $this->assertDatabaseCount("semesters", 1); + + $this->delete("/dashboard/semesters/{$semester->id}"); + + $this->assertDatabaseCount("semesters", 0); + } +} diff --git a/tests/Feature/StudentTest.php b/tests/Feature/StudentTest.php index 7b357f1..1497d31 100644 --- a/tests/Feature/StudentTest.php +++ b/tests/Feature/StudentTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Tests\Feature\Frontend; +namespace Tests\Feature; use App\Models\Student; use Illuminate\Foundation\Testing\RefreshDatabase; From 3c0d79252cbeb4ae4495fc85b00bcab0242e23ef Mon Sep 17 00:00:00 2001 From: Kamil Date: Sat, 23 Sep 2023 17:03:09 +0200 Subject: [PATCH 02/13] #8 - toggle active feature for semesters --- app/Actions/ActivateSemesterAction.php | 17 +++++++++++++++++ .../Dashboard/SemesterController.php | 9 +++++++++ app/Http/Resources/SemesterResource.php | 1 + app/Models/Semester.php | 15 +++++++++++++++ resources/js/Pages/Dashboard/Semester/Index.vue | 6 +++++- routes/web.php | 1 + 6 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 app/Actions/ActivateSemesterAction.php diff --git a/app/Actions/ActivateSemesterAction.php b/app/Actions/ActivateSemesterAction.php new file mode 100644 index 0000000..f1d22da --- /dev/null +++ b/app/Actions/ActivateSemesterAction.php @@ -0,0 +1,17 @@ +update(["status" => SemesterStatus::INACTIVE]); + $semester->update(["status" => SemesterStatus::ACTIVE]); + } +} diff --git a/app/Http/Controllers/Dashboard/SemesterController.php b/app/Http/Controllers/Dashboard/SemesterController.php index d6c3f78..c2718da 100644 --- a/app/Http/Controllers/Dashboard/SemesterController.php +++ b/app/Http/Controllers/Dashboard/SemesterController.php @@ -4,6 +4,7 @@ namespace App\Http\Controllers\Dashboard; +use App\Actions\ActivateSemesterAction; use App\Http\Controllers\Controller; use App\Http\Requests\SemesterRequest; use App\Http\Resources\SemesterResource; @@ -59,4 +60,12 @@ public function destroy(Semester $semester): RedirectResponse return redirect()->back() ->with("success", "Usunięto semestr"); } + + public function toggleActive(Semester $semester, ActivateSemesterAction $activateSemesterAction): RedirectResponse + { + $activateSemesterAction->execute($semester); + + return redirect()->back() + ->with("success", "Semestr aktywny"); + } } diff --git a/app/Http/Resources/SemesterResource.php b/app/Http/Resources/SemesterResource.php index c3d12d7..d5c68c1 100644 --- a/app/Http/Resources/SemesterResource.php +++ b/app/Http/Resources/SemesterResource.php @@ -16,6 +16,7 @@ public function toArray(Request $request): array $semester = $this; return [ + "id" => $semester->id, "name" => $semester->name, "status" => $semester->status, "status_label" => $semester->status->getLabel(), diff --git a/app/Models/Semester.php b/app/Models/Semester.php index 7e3eccd..7b6e5b8 100644 --- a/app/Models/Semester.php +++ b/app/Models/Semester.php @@ -6,6 +6,7 @@ use App\Enums\SemesterStatus; use Carbon\Carbon; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Concerns\HasUlids; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; @@ -16,6 +17,7 @@ * @property SemesterStatus $status * @property Carbon $created_at * @property Carbon $updated_at + * @method static Builder host() */ class Semester extends Model { @@ -29,4 +31,17 @@ class Semester extends Model protected $casts = [ "status" => SemesterStatus::class, ]; + + public function scopeActive(Builder $query): Builder + { + return $query->where("status", SemesterStatus::ACTIVE->value); + } + + /** + * @return ?Semester + */ + public static function getActive(): ?Model + { + return self::query()->active()->first(); + } } diff --git a/resources/js/Pages/Dashboard/Semester/Index.vue b/resources/js/Pages/Dashboard/Semester/Index.vue index 2d13497..33859d1 100644 --- a/resources/js/Pages/Dashboard/Semester/Index.vue +++ b/resources/js/Pages/Dashboard/Semester/Index.vue @@ -8,6 +8,7 @@ import Button from '@/Shared/Components/Buttons/Button.vue' import EmptyState from '@/Shared/Components/EmptyState.vue' import RemoveModal from '@/Shared/Modals/RemoveModal.vue' import { ref } from 'vue' +import {Method} from "@inertiajs/inertia"; const props = defineProps({ semesters: Object, @@ -46,7 +47,10 @@ const semesterToDeleteId = ref(0) {{ semester.status_label }} - + + Aktywuj + + Edycja