diff --git a/app/Helpers/YearPeriodRetriever.php b/app/Helpers/YearPeriodRetriever.php new file mode 100644 index 0000000..b474865 --- /dev/null +++ b/app/Helpers/YearPeriodRetriever.php @@ -0,0 +1,57 @@ +find($this->session->get(static::SESSION_KEY)); + + return $yearPeriod !== null ? $yearPeriod : $this->current(); + } + + public function current(): YearPeriod + { + return YearPeriod::current(); + } + + public function links(): array + { + $current = $this->selected(); + + $years = YearPeriod::query()->whereIn("year", $this->offset($current->year))->get(); + $navigation = $years->map(fn(YearPeriod $yearPeriod) => $this->toNavigation($yearPeriod)); + + return [ + "current" => $current->year, + "navigation" => $navigation->toArray(), + ]; + } + + protected function offset(int $year): array + { + return range($year - 2, $year + 2); + } + + protected function toNavigation(YearPeriod $yearPeriod): array + { + return [ + "year" => $yearPeriod->year, + "link" => route("year-periods.select", $yearPeriod->id), + ]; + } +} diff --git a/app/Http/Controllers/SelectYearPeriodController.php b/app/Http/Controllers/SelectYearPeriodController.php new file mode 100644 index 0000000..4965ef3 --- /dev/null +++ b/app/Http/Controllers/SelectYearPeriodController.php @@ -0,0 +1,22 @@ +session()->put(YearPeriodRetriever::SESSION_KEY, $yearPeriod->id); + + return redirect() + ->back() + ->with("success", __("Selected year period has been changed")); + } +} diff --git a/app/Http/Controllers/VacationLimitController.php b/app/Http/Controllers/VacationLimitController.php new file mode 100644 index 0000000..3d5e6d3 --- /dev/null +++ b/app/Http/Controllers/VacationLimitController.php @@ -0,0 +1,34 @@ + VacationLimitResource::collection(VacationLimit::query()->with("user")->get()), + ]); + } + + public function update(VacationLimitRequest $request): RedirectResponse + { + $data = $request->data(); + + foreach ($request->vacationLimits() as $limit) { + $limit->update($data[$limit->id]); + } + + return redirect() + ->back() + ->with("success", __("Vacation limits have been updated")); + } +} diff --git a/app/Http/Middleware/HandleInertiaRequests.php b/app/Http/Middleware/HandleInertiaRequests.php index 48117f8..dd63c51 100644 --- a/app/Http/Middleware/HandleInertiaRequests.php +++ b/app/Http/Middleware/HandleInertiaRequests.php @@ -6,10 +6,16 @@ namespace Toby\Http\Middleware; use Illuminate\Http\Request; use Inertia\Middleware; +use Toby\Helpers\YearPeriodRetriever; use Toby\Http\Resources\UserResource; class HandleInertiaRequests extends Middleware { + public function __construct( + protected YearPeriodRetriever $yearPeriodRetriever, + ) { + } + public function share(Request $request): array { $user = $request->user(); @@ -22,6 +28,7 @@ class HandleInertiaRequests extends Middleware "success" => $request->session()->get("success"), "error" => $request->session()->get("error"), ], + "years" => fn() => $user ? $this->yearPeriodRetriever->links() : [], ]); } } diff --git a/app/Http/Requests/VacationLimitRequest.php b/app/Http/Requests/VacationLimitRequest.php new file mode 100644 index 0000000..fa43bbc --- /dev/null +++ b/app/Http/Requests/VacationLimitRequest.php @@ -0,0 +1,33 @@ + ["required", "array"], + "items.*.id" => ["required", "exists:vacation_limits,id"], + "items.*.days" => ["nullable", "integer", "min:0"], + ]; + } + + public function vacationLimits(): Collection + { + return VacationLimit::query()->find($this->collect("items")->pluck("id")); + } + + public function data(): array + { + return $this->collect("items") + ->keyBy("id") + ->toArray(); + } +} diff --git a/app/Http/Resources/UserFormDataResource.php b/app/Http/Resources/UserFormDataResource.php index df7da1f..1ee44f3 100644 --- a/app/Http/Resources/UserFormDataResource.php +++ b/app/Http/Resources/UserFormDataResource.php @@ -8,7 +8,7 @@ use Illuminate\Http\Resources\Json\JsonResource; class UserFormDataResource extends JsonResource { - public static $wrap = false; + public static $wrap = null; public function toArray($request): array { diff --git a/app/Http/Resources/UserResource.php b/app/Http/Resources/UserResource.php index c9217a6..132092b 100644 --- a/app/Http/Resources/UserResource.php +++ b/app/Http/Resources/UserResource.php @@ -8,7 +8,7 @@ use Illuminate\Http\Resources\Json\JsonResource; class UserResource extends JsonResource { - public static $wrap = false; + public static $wrap = null; public function toArray($request): array { diff --git a/app/Http/Resources/VacationLimitResource.php b/app/Http/Resources/VacationLimitResource.php new file mode 100644 index 0000000..85692b9 --- /dev/null +++ b/app/Http/Resources/VacationLimitResource.php @@ -0,0 +1,22 @@ + $this->id, + "user" => new UserResource($this->user), + "hasVacation" => $this->hasVacation(), + "days" => $this->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/Models/VacationLimit.php b/app/Models/VacationLimit.php new file mode 100644 index 0000000..6e6a361 --- /dev/null +++ b/app/Models/VacationLimit.php @@ -0,0 +1,37 @@ +days !== null; + } + + public function user(): BelongsTo + { + return $this->belongsTo(User::class); + } + + public function yearPeriod(): BelongsTo + { + return $this->belongsTo(YearPeriod::class); + } +} diff --git a/app/Models/YearPeriod.php b/app/Models/YearPeriod.php index bcf2096..569ad03 100644 --- a/app/Models/YearPeriod.php +++ b/app/Models/YearPeriod.php @@ -7,10 +7,13 @@ namespace Toby\Models; use Carbon\Carbon; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Support\Collection; /** * @property int $id * @property int $year + * @property Collection $vacationLimits */ class YearPeriod extends Model { @@ -27,4 +30,9 @@ class YearPeriod extends Model return $year; } + + public function vacationLimits(): HasMany + { + return $this->hasMany(VacationLimit::class); + } } diff --git a/app/Observers/UserObserver.php b/app/Observers/UserObserver.php index 32f6f9c..3462cdf 100644 --- a/app/Observers/UserObserver.php +++ b/app/Observers/UserObserver.php @@ -6,20 +6,24 @@ 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..71caa82 --- /dev/null +++ b/app/Observers/YearPeriodObserver.php @@ -0,0 +1,28 @@ +vacationLimits()->create([ + "user_id" => $user->id, + ]); + } + } +} diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index db3deaa..cf3fe47 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -6,15 +6,15 @@ namespace Toby\Providers; use Illuminate\Support\Carbon; use Illuminate\Support\ServiceProvider; -use Toby\Models\User; -use Toby\Observers\UserObserver; +use Toby\Models\VacationLimit; +use Toby\Scopes\SelectedYearPeriodScope; class AppServiceProvider extends ServiceProvider { public function boot(): void { - User::observe(UserObserver::class); - Carbon::macro("toDisplayString", fn() => $this->translatedFormat("j F Y")); + + VacationLimit::addGlobalScope($this->app->make(SelectedYearPeriodScope::class)); } } diff --git a/app/Providers/ObserverServiceProvider.php b/app/Providers/ObserverServiceProvider.php new file mode 100644 index 0000000..16badc4 --- /dev/null +++ b/app/Providers/ObserverServiceProvider.php @@ -0,0 +1,20 @@ +where("year_period_id", $this->yearPeriodRetriever->selected()->id); + } +} diff --git a/config/app.php b/config/app.php index 77122a5..3fa85eb 100644 --- a/config/app.php +++ b/config/app.php @@ -42,5 +42,6 @@ return [ Toby\Providers\EventServiceProvider::class, Toby\Providers\RouteServiceProvider::class, Toby\Providers\TelescopeServiceProvider::class, + Toby\Providers\ObserverServiceProvider::class, ], ]; diff --git a/database/factories/VacationLimitFactory.php b/database/factories/VacationLimitFactory.php new file mode 100644 index 0000000..d5f3d6d --- /dev/null +++ b/database/factories/VacationLimitFactory.php @@ -0,0 +1,24 @@ +faker->boolean(75); + + return [ + "user_id" => User::factory(), + "year_period_id" => YearPeriod::factory(), + "has_vacation" => $hasVacation, + "days" => $hasVacation ? $this->faker->numberBetween(20, 26) : null, + ]; + } +} diff --git a/database/migrations/2022_01_19_140630_create_vacation_limits_table.php b/database/migrations/2022_01_19_140630_create_vacation_limits_table.php new file mode 100644 index 0000000..c2b29d5 --- /dev/null +++ b/database/migrations/2022_01_19_140630_create_vacation_limits_table.php @@ -0,0 +1,27 @@ +id(); + $table->foreignIdFor(User::class)->constrained()->cascadeOnDelete(); + $table->foreignIdFor(YearPeriod::class)->constrained()->cascadeOnDelete(); + $table->integer("days")->nullable(); + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists("vacation_limits"); + } +}; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index af2722b..d6a4f27 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -6,23 +6,61 @@ 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::factory(35)->create(); + User::unsetEventDispatcher(); + YearPeriod::unsetEventDispatcher(); + + User::factory(9)->create(); User::factory([ "email" => env("LOCAL_EMAIL_FOR_LOGIN_VIA_GOOGLE"), ])->create(); - YearPeriod::factory([ - "year" => Carbon::now()->year, - ])->create(); - YearPeriod::factory([ - "year" => Carbon::now()->year + 1, - ])->create(); + $users = User::all(); + + $this->generateAvatarsForUsers($users); + + YearPeriod::factory() + ->count(3) + ->sequence( + [ + "year" => Carbon::now()->year - 1, + ], + [ + "year" => Carbon::now()->year, + ], + [ + "year" => Carbon::now()->year + 1, + ], + ) + ->afterCreating(function (YearPeriod $yearPeriod) use ($users): void { + foreach ($users as $user) { + VacationLimit::factory() + ->for($yearPeriod) + ->for($user) + ->create(); + } + }) + ->create(); + } + + protected function generateAvatarsForUsers(Collection $users): void + { + foreach ($users as $user) { + $user->saveAvatar($this->avatarGenerator->generateFor($user)); + } } } diff --git a/package-lock.json b/package-lock.json index c24807a..7a47b79 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "application", + "name": "toby", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/resources/js/Pages/VacationLimits.vue b/resources/js/Pages/VacationLimits.vue new file mode 100644 index 0000000..04afda6 --- /dev/null +++ b/resources/js/Pages/VacationLimits.vue @@ -0,0 +1,175 @@ + + + diff --git a/resources/js/Shared/MainMenu.vue b/resources/js/Shared/MainMenu.vue index 7373e89..4966645 100644 --- a/resources/js/Shared/MainMenu.vue +++ b/resources/js/Shared/MainMenu.vue @@ -19,6 +19,53 @@