diff --git a/app/Http/Controllers/VacationLimitController.php b/app/Http/Controllers/VacationLimitController.php index 0f3415d..3d5e6d3 100644 --- a/app/Http/Controllers/VacationLimitController.php +++ b/app/Http/Controllers/VacationLimitController.php @@ -5,7 +5,6 @@ declare(strict_types=1); namespace Toby\Http\Controllers; use Illuminate\Http\RedirectResponse; -use Illuminate\Support\Arr; use Inertia\Response; use Toby\Http\Requests\VacationLimitRequest; use Toby\Http\Resources\VacationLimitResource; @@ -22,14 +21,14 @@ class VacationLimitController extends Controller public function update(VacationLimitRequest $request): RedirectResponse { - foreach ($request->data() as $data) { - $limit = VacationLimit::query()->find($data["id"]); + $data = $request->data(); - $limit->update(Arr::only($data, ["has_vacation", "days"])); - } + foreach ($request->vacationLimits() as $limit) { + $limit->update($data[$limit->id]); + } - return redirect() - ->back() - ->with("success", __("Vacation limits have been updated")); + return redirect() + ->back() + ->with("success", __("Vacation limits have been updated")); } } diff --git a/app/Http/Requests/VacationLimitRequest.php b/app/Http/Requests/VacationLimitRequest.php index 77b0bf5..eb03e12 100644 --- a/app/Http/Requests/VacationLimitRequest.php +++ b/app/Http/Requests/VacationLimitRequest.php @@ -6,6 +6,7 @@ namespace Toby\Http\Requests; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Collection; +use Toby\Models\VacationLimit; class VacationLimitRequest extends FormRequest { @@ -19,12 +20,19 @@ class VacationLimitRequest extends FormRequest ]; } + + public function vacationLimits(): Collection + { + return VacationLimit::query()->find($this->collect("items")->pluck("id")); + } + public function data(): Collection { - return $this->collect("items")->map(fn(array $item): array => [ - "id" => $item["id"], - "has_vacation" => $item["hasVacation"], - "days" => $item["days"], + return $this->collect("items")->mapWithKeys(fn(array $item): array => [ + $item["id"] => [ + "has_vacation" => $item["hasVacation"], + "days" => $item["days"], + ] ]); } } diff --git a/app/Models/User.php b/app/Models/User.php index aa76021..8ab0872 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -6,10 +6,12 @@ namespace Toby\Models; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Illuminate\Support\Carbon; +use Illuminate\Support\Collection; use Toby\Enums\EmploymentForm; /** @@ -19,6 +21,7 @@ use Toby\Enums\EmploymentForm; * @property string $avatar * @property EmploymentForm $employment_form * @property Carbon $employment_date + * @property Collection $vacationLimits */ class User extends Authenticatable { @@ -43,6 +46,11 @@ class User extends Authenticatable "remember_token", ]; + public function vacationLimits(): HasMany + { + return $this->hasMany(VacationLimit::class); + } + public function scopeSearch(Builder $query, ?string $text): Builder { if ($text === null) { @@ -53,4 +61,11 @@ class User extends Authenticatable ->where("name", "LIKE", "%{$text}%") ->orWhere("email", "LIKE", "%{$text}%"); } + + public function saveAvatar(string $path): void + { + $this->avatar = $path; + + $this->save(); + } } diff --git a/app/Observers/UserObserver.php b/app/Observers/UserObserver.php index 32f6f9c..1aa109c 100644 --- a/app/Observers/UserObserver.php +++ b/app/Observers/UserObserver.php @@ -6,20 +6,22 @@ namespace Toby\Observers; use Illuminate\Support\Facades\Storage; use Toby\Helpers\UserAvatarGenerator; +use Toby\Helpers\YearPeriodRetriever; use Toby\Models\User; class UserObserver { public function __construct( protected UserAvatarGenerator $generator, + protected YearPeriodRetriever $yearPeriodRetriever, ) { } public function created(User $user): void { - $user->avatar = $this->generator->generateFor($user); + $user->saveAvatar($this->generator->generateFor($user)); - $user->save(); + $user->vacationLimits()->create(["year_period_id" => $this->yearPeriodRetriever->current()->id]); } public function updating(User $user): void diff --git a/app/Observers/YearPeriodObserver.php b/app/Observers/YearPeriodObserver.php new file mode 100644 index 0000000..006e610 --- /dev/null +++ b/app/Observers/YearPeriodObserver.php @@ -0,0 +1,26 @@ +vacationLimits()->updateOrCreate(["user_id" => $user->id]); + } + } +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 9d37aac..077ea67 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -8,7 +8,9 @@ use Illuminate\Support\Carbon; use Illuminate\Support\ServiceProvider; use Toby\Models\User; use Toby\Models\VacationLimit; +use Toby\Models\YearPeriod; use Toby\Observers\UserObserver; +use Toby\Observers\YearPeriodObserver; use Toby\Scopes\SelectedYearPeriodScope; class AppServiceProvider extends ServiceProvider @@ -16,6 +18,7 @@ class AppServiceProvider extends ServiceProvider public function boot(): void { User::observe(UserObserver::class); + YearPeriod::observe(YearPeriodObserver::class); Carbon::macro("toDisplayString", fn() => $this->translatedFormat("j F Y")); diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 18ab6c5..c90c3ad 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -6,14 +6,23 @@ namespace Database\Seeders; use Illuminate\Database\Seeder; use Illuminate\Support\Carbon; +use Illuminate\Support\Collection; +use Toby\Helpers\UserAvatarGenerator; use Toby\Models\User; use Toby\Models\VacationLimit; use Toby\Models\YearPeriod; class DatabaseSeeder extends Seeder { + public function __construct(protected UserAvatarGenerator $avatarGenerator) + { + } + public function run(): void { + User::unsetEventDispatcher(); + YearPeriod::unsetEventDispatcher(); + User::factory(14)->create(); User::factory([ "email" => env("LOCAL_EMAIL_FOR_LOGIN_VIA_GOOGLE"), @@ -21,6 +30,8 @@ class DatabaseSeeder extends Seeder $users = User::all(); + $this->generateAvatarsForUsers($users); + YearPeriod::factory() ->count(3) ->sequence( @@ -38,4 +49,11 @@ class DatabaseSeeder extends Seeder }) ->create(); } + + protected function generateAvatarsForUsers(Collection $users): void + { + foreach ($users as $user) { + $user->saveAvatar($this->avatarGenerator->generateFor($user)); + } + } } diff --git a/routes/web.php b/routes/web.php index a305827..acd4d43 100644 --- a/routes/web.php +++ b/routes/web.php @@ -16,8 +16,8 @@ Route::middleware("auth")->group(function (): void { Route::resource("users", UserController::class); Route::post("users/{user}/restore", [UserController::class, "restore"])->withTrashed(); - Route::get("/vacation-days", [VacationLimitController::class, "edit"])->name("vacation.days"); - Route::put("/vacation-days", [VacationLimitController::class, "update"]); + Route::get("/vacation-limits", [VacationLimitController::class, "edit"])->name("vacation.limits"); + Route::put("/vacation-limits", [VacationLimitController::class, "update"]); Route::post("year-periods/{yearPeriod}/select", SelectYearPeriodController::class)->name("year-periods.select"); }); diff --git a/tests/Feature/UserTest.php b/tests/Feature/UserTest.php index 8c3d3f3..ce8236f 100644 --- a/tests/Feature/UserTest.php +++ b/tests/Feature/UserTest.php @@ -6,7 +6,6 @@ namespace Tests\Feature; use Illuminate\Foundation\Testing\DatabaseMigrations; use Illuminate\Support\Carbon; -use Illuminate\Support\Facades\Storage; use Inertia\Testing\AssertableInertia as Assert; use Tests\FeatureTestCase; use Toby\Enums\EmploymentForm; @@ -16,13 +15,6 @@ class UserTest extends FeatureTestCase { use DatabaseMigrations; - protected function setUp(): void - { - parent::setUp(); - - Storage::fake(); - } - public function testAdminCanSeeUsersList(): void { User::factory()->count(10)->create(); diff --git a/tests/Feature/VacationLimitTest.php b/tests/Feature/VacationLimitTest.php new file mode 100644 index 0000000..b566f2c --- /dev/null +++ b/tests/Feature/VacationLimitTest.php @@ -0,0 +1,83 @@ +createQuietly(); + + User::factory(10)->create(); + + $this->actingAs($admin) + ->get("/vacation-limits") + ->assertOk() + ->assertInertia( + fn(Assert $page) => $page + ->component("VacationLimits") + ->has("limits.data", 10) + ); + } + + public function testAdminCanUpdateVacationLimits(): void + { + $admin = User::factory()->createQuietly(); + + User::factory(3)->create(); + + [$limit1, $limit2, $limit3] = VacationLimit::all(); + + $data = [ + [ + "id" => $limit1->id, + "hasVacation" => true, + "days" => 25, + ], + [ + "id" => $limit2->id, + "hasVacation" => false, + "days" => null, + ], + [ + "id" => $limit3->id, + "hasVacation" => true, + "days" => 20, + ], + ]; + + $this->actingAs($admin) + ->put("/vacation-limits", [ + "items" => $data + ]) + ->assertRedirect(); + + $this->assertDatabaseHas("vacation_limits", [ + "id" => $limit1->id, + "has_vacation" => true, + "days" => 25, + ]); + + $this->assertDatabaseHas("vacation_limits", [ + "id" => $limit2->id, + "has_vacation" => false, + "days" => null, + ]); + + $this->assertDatabaseHas("vacation_limits", [ + "id" => $limit3->id, + "has_vacation" => true, + "days" => 20, + ]); + } +} diff --git a/tests/Unit/AvatarTest.php b/tests/Unit/AvatarTest.php index 2da1550..78d74dc 100644 --- a/tests/Unit/AvatarTest.php +++ b/tests/Unit/AvatarTest.php @@ -7,16 +7,19 @@ namespace Tests\Unit; use Illuminate\Foundation\Testing\DatabaseMigrations; use Illuminate\Support\Facades\Storage; use Tests\TestCase; +use Tests\Traits\InteractsWithYearPeriods; use Toby\Models\User; class AvatarTest extends TestCase { use DatabaseMigrations; + use InteractsWithYearPeriods; protected function setUp(): void { parent::setUp(); + $this->createCurrentYearPeriod(); Storage::fake(); } diff --git a/tests/Unit/VacationLimitTest.php b/tests/Unit/VacationLimitTest.php new file mode 100644 index 0000000..7b2d7a9 --- /dev/null +++ b/tests/Unit/VacationLimitTest.php @@ -0,0 +1,50 @@ +createCurrentYearPeriod(); + } + + public function testWhenUserIsCreatedThenVacationLimitIsCreatedForCurrentYearPeriod(): void + { + $this->assertDatabaseCount("vacation_limits", 0); + + $currentYearPeriod = YearPeriod::current(); + $user = User::factory()->create(); + + $this->assertDatabaseCount("vacation_limits", 1); + + $this->assertDatabaseHas("vacation_limits", [ + "user_id" => $user->id, + "year_period_id" => $currentYearPeriod->id, + ]); + } + + public function testWhenYearPeriodIsCreatedThenVacationLimitsForThisYearPeriodAreCreated(): void + { + $this->assertDatabaseCount("vacation_limits", 0); + + User::factory(10)->createQuietly(); + + YearPeriod::factory()->create(); + + $this->assertDatabaseCount("vacation_limits", 10); + } +}