


diff --git a/app/Domain/CalendarGenerator.php b/app/Domain/CalendarGenerator.php
new file mode 100644
index 0000000..f1d72f2
--- /dev/null
+++ b/app/Domain/CalendarGenerator.php
@@ -0,0 +1,80 @@
+year, $this->monthNameToNumber($month));
+ $period = CarbonPeriod::create($date->startOfMonth(), $date->endOfMonth());
+ $holidays = $yearPeriod->holidays()->pluck("date");
+
+ return $this->generateCalendar($period, $holidays);
+ }
+
+ protected function monthNameToNumber($name): int
+ {
+ return match ($name) {
+ default => CarbonInterface::JANUARY,
+ "february" => CarbonInterface::FEBRUARY,
+ "march" => CarbonInterface::MARCH,
+ "april" => CarbonInterface::APRIL,
+ "may" => CarbonInterface::MAY,
+ "june" => CarbonInterface::JUNE,
+ "july" => CarbonInterface::JULY,
+ "august" => CarbonInterface::AUGUST,
+ "september" => CarbonInterface::SEPTEMBER,
+ "october" => CarbonInterface::OCTOBER,
+ "november" => CarbonInterface::NOVEMBER,
+ "december" => CarbonInterface::DECEMBER,
+ };
+ }
+
+ protected function generateCalendar(CarbonPeriod $period, Collection $holidays): array
+ {
+ $calendar = [];
+ $vacations = $this->getVacationsForPeriod($period);
+
+ foreach ($period as $day) {
+ $vacationsForDay = $vacations[$day->toDateString()] ?? new Collection();
+
+ $calendar[] = [
+ "date" => $day->toDateString(),
+ "dayOfMonth" => $day->translatedFormat("j"),
+ "dayOfWeek" => $day->translatedFormat("D"),
+ "isToday" => $day->isToday(),
+ "isWeekend" => $day->isWeekend(),
+ "isHoliday" => $holidays->contains($day),
+ "vacations" => $vacationsForDay->pluck("user_id"),
+ ];
+ }
+
+ return $calendar;
+ }
+
+ protected function getVacationsForPeriod(CarbonPeriod $period): Collection
+ {
+ return Vacation::query()
+ ->whereBetween("date", [$period->start, $period->end])
+ ->whereRelation("vacationRequest", "state", VacationRequestState::Approved->value)
+ ->get()
+ ->groupBy(fn(Vacation $vacation) => $vacation->date->toDateString());
+ }
+}
diff --git a/app/Domain/Enums/EmploymentForm.php b/app/Domain/Enums/EmploymentForm.php
index cdaa241..625b801 100644
--- a/app/Domain/Enums/EmploymentForm.php
+++ b/app/Domain/Enums/EmploymentForm.php
@@ -6,10 +6,10 @@ namespace Toby\Domain\Enums;
enum EmploymentForm: string
{
- case EMPLOYMENT_CONTRACT = "employment_contract";
- case COMMISSION_CONTRACT = "commission_contract";
- case B2B_CONTRACT = "b2b_contract";
- case BOARD_MEMBER_CONTRACT = "board_member_contract";
+ case EmploymentContract = "employment_contract";
+ case ComissionContract = "commission_contract";
+ case B2bContract = "b2b_contract";
+ case BoardMemberContract = "board_member_contract";
public function label(): string
{
diff --git a/app/Domain/Enums/Role.php b/app/Domain/Enums/Role.php
index 7f6a996..f4be8b9 100644
--- a/app/Domain/Enums/Role.php
+++ b/app/Domain/Enums/Role.php
@@ -6,10 +6,10 @@ namespace Toby\Domain\Enums;
enum Role: string
{
- case EMPLOYEE = "employee";
- case ADMINISTRATOR = "administrator";
- case TECHNICAL_APPROVER = "technical_approver";
- case ADMINISTRATIVE_APPROVER = "administrative_approver";
+ case Employee = "employee";
+ case Administrator = "administrator";
+ case TechnicalApprover = "technical_approver";
+ case AdministrativeApprover = "administrative_approver";
public function label(): string
{
diff --git a/app/Domain/Enums/VacationRequestState.php b/app/Domain/Enums/VacationRequestState.php
index cd7ec57..7fcbae5 100644
--- a/app/Domain/Enums/VacationRequestState.php
+++ b/app/Domain/Enums/VacationRequestState.php
@@ -6,14 +6,14 @@ namespace Toby\Domain\Enums;
enum VacationRequestState: string
{
- case CREATED = "created";
- case CANCELED = "canceled";
- case REJECTED = "rejected";
- case APPROVED = "approved";
- case WAITING_FOR_TECHNICAL = "waiting_for_technical";
- case WAITING_FOR_ADMINISTRATIVE = "waiting_for_administrative";
- case ACCEPTED_BY_TECHNICAL = "accepted_by_technical";
- case ACCEPTED_BY_ADMINSTRATIVE = "accepted_by_administrative";
+ case Created = "created";
+ case Canceled = "canceled";
+ case Rejected = "rejected";
+ case Approved = "approved";
+ case WaitingForTechnical = "waiting_for_technical";
+ case WaitingForAdministrative = "waiting_for_administrative";
+ case AcceptedByTechnical = "accepted_by_technical";
+ case AcceptedByAdministrative = "accepted_by_administrative";
public function label(): string
{
@@ -23,24 +23,24 @@ enum VacationRequestState: string
public static function pendingStates(): array
{
return [
- self::CREATED,
- self::WAITING_FOR_TECHNICAL,
- self::WAITING_FOR_ADMINISTRATIVE,
- self::ACCEPTED_BY_TECHNICAL,
- self::ACCEPTED_BY_ADMINSTRATIVE,
+ self::Created,
+ self::WaitingForTechnical,
+ self::WaitingForAdministrative,
+ self::AcceptedByTechnical,
+ self::AcceptedByAdministrative,
];
}
public static function successStates(): array
{
- return [self::APPROVED];
+ return [self::Approved];
}
public static function failedStates(): array
{
return [
- self::REJECTED,
- self::CANCELED,
+ self::Rejected,
+ self::Canceled,
];
}
diff --git a/app/Domain/Enums/VacationType.php b/app/Domain/Enums/VacationType.php
index c3502c8..63e2858 100644
--- a/app/Domain/Enums/VacationType.php
+++ b/app/Domain/Enums/VacationType.php
@@ -6,15 +6,15 @@ namespace Toby\Domain\Enums;
enum VacationType: string
{
- case VACATION = "vacation";
- case VACATION_ON_REQUEST = "vacation_on_request";
- case SPECIAL_VACATION = "special_vacation";
- case CHILDCARE_VACATION = "childcare_vacation";
- case TRAINING_VACATION = "training_vacation";
- case UNPAID_VACATION = "unpaid_vacation";
- case VOLUNTEERING_VACATION = "volunteering_vacation";
- case TIME_IN_LIEU = "time_in_lieu";
- case SICK_VACATION = "sick_vacation";
+ case Vacation = "vacation";
+ case OnRequest = "vacation_on_request";
+ case Special = "special_vacation";
+ case Childcare = "childcare_vacation";
+ case Training = "training_vacation";
+ case Unpaid = "unpaid_vacation";
+ case Volunteering = "volunteering_vacation";
+ case TimeInLieu = "time_in_lieu";
+ case Sick = "sick_vacation";
public function label(): string
{
diff --git a/app/Domain/VacationDaysCalculator.php b/app/Domain/VacationDaysCalculator.php
index fcbc9e8..0e41a4a 100644
--- a/app/Domain/VacationDaysCalculator.php
+++ b/app/Domain/VacationDaysCalculator.php
@@ -16,7 +16,7 @@ class VacationDaysCalculator
$period = CarbonPeriod::create($from, $to);
$holidays = $yearPeriod->holidays()->pluck("date");
- $validDays = collect();
+ $validDays = new Collection();
foreach ($period as $day) {
if ($this->passes($day, $holidays)) {
diff --git a/app/Domain/VacationRequestStateManager.php b/app/Domain/VacationRequestStateManager.php
index 8bee7c7..7de8484 100644
--- a/app/Domain/VacationRequestStateManager.php
+++ b/app/Domain/VacationRequestStateManager.php
@@ -23,50 +23,50 @@ class VacationRequestStateManager
public function markAsCreated(VacationRequest $vacationRequest): void
{
- $this->changeState($vacationRequest, VacationRequestState::CREATED);
+ $this->changeState($vacationRequest, VacationRequestState::Created);
$this->dispatcher->dispatch(new VacationRequestCreated($vacationRequest));
}
public function approve(VacationRequest $vacationRequest): void
{
- $this->changeState($vacationRequest, VacationRequestState::APPROVED);
+ $this->changeState($vacationRequest, VacationRequestState::Approved);
$this->dispatcher->dispatch(new VacationRequestApproved($vacationRequest));
}
public function reject(VacationRequest $vacationRequest): void
{
- $this->changeState($vacationRequest, VacationRequestState::REJECTED);
+ $this->changeState($vacationRequest, VacationRequestState::Rejected);
}
public function cancel(VacationRequest $vacationRequest): void
{
- $this->changeState($vacationRequest, VacationRequestState::CANCELED);
+ $this->changeState($vacationRequest, VacationRequestState::Canceled);
}
public function acceptAsTechnical(VacationRequest $vacationRequest): void
{
- $this->changeState($vacationRequest, VacationRequestState::ACCEPTED_BY_TECHNICAL);
+ $this->changeState($vacationRequest, VacationRequestState::AcceptedByTechnical);
$this->dispatcher->dispatch(new VacationRequestAcceptedByTechnical($vacationRequest));
}
public function acceptAsAdministrative(VacationRequest $vacationRequest): void
{
- $this->changeState($vacationRequest, VacationRequestState::ACCEPTED_BY_ADMINSTRATIVE);
+ $this->changeState($vacationRequest, VacationRequestState::AcceptedByAdministrative);
$this->dispatcher->dispatch(new VacationRequestAcceptedByAdministrative($vacationRequest));
}
public function waitForTechnical(VacationRequest $vacationRequest): void
{
- $this->changeState($vacationRequest, VacationRequestState::WAITING_FOR_TECHNICAL);
+ $this->changeState($vacationRequest, VacationRequestState::WaitingForTechnical);
}
public function waitForAdministrative(VacationRequest $vacationRequest): void
{
- $this->changeState($vacationRequest, VacationRequestState::WAITING_FOR_ADMINISTRATIVE);
+ $this->changeState($vacationRequest, VacationRequestState::WaitingForAdministrative);
}
protected function changeState(VacationRequest $vacationRequest, VacationRequestState $state): void
diff --git a/app/Domain/Validation/Rules/DoesNotExceedLimitRule.php b/app/Domain/Validation/Rules/DoesNotExceedLimitRule.php
index c9c4148..c8c9ab5 100644
--- a/app/Domain/Validation/Rules/DoesNotExceedLimitRule.php
+++ b/app/Domain/Validation/Rules/DoesNotExceedLimitRule.php
@@ -4,23 +4,64 @@ declare(strict_types=1);
namespace Toby\Domain\Validation\Rules;
+use Illuminate\Database\Eloquent\Builder;
+use Illuminate\Database\Eloquent\Collection;
+use Toby\Domain\Enums\VacationRequestState;
+use Toby\Domain\Enums\VacationType;
+use Toby\Domain\VacationDaysCalculator;
use Toby\Domain\VacationTypeConfigRetriever;
+use Toby\Eloquent\Models\User;
use Toby\Eloquent\Models\VacationRequest;
+use Toby\Eloquent\Models\YearPeriod;
class DoesNotExceedLimitRule implements VacationRequestRule
{
public function __construct(
protected VacationTypeConfigRetriever $configRetriever,
+ protected VacationDaysCalculator $vacationDaysCalculator,
) {
}
public function check(VacationRequest $vacationRequest): bool
{
- return true;
+ if (!$this->configRetriever->hasLimit($vacationRequest->type)) {
+ return true;
+ }
+
+ $limit = $this->getUserVacationLimit($vacationRequest->user, $vacationRequest->yearPeriod);
+ $vacationDays = $this->getVacationDaysWithLimit($vacationRequest->user, $vacationRequest->yearPeriod);
+ $estimatedDays = $this->vacationDaysCalculator->calculateDays($vacationRequest->yearPeriod, $vacationRequest->from, $vacationRequest->to)->count();
+
+ return $limit >= ($vacationDays + $estimatedDays);
}
public function errorMessage(): string
{
- return __("You have exceeded your vacation limit.");
+ return __("Vacation limit has been exceeded.");
+ }
+
+ protected function getUserVacationLimit(User $user, YearPeriod $yearPeriod): int
+ {
+ return $user->vacationLimits()->where("year_period_id", $yearPeriod->id)->first()->days ?? 0;
+ }
+
+ protected function getVacationDaysWithLimit(User $user, YearPeriod $yearPeriod): int
+ {
+ return $user->vacations()
+ ->where("year_period_id", $yearPeriod->id)
+ ->whereRelation(
+ "vacationRequest",
+ fn(Builder $query) => $query
+ ->whereIn("type", $this->getLimitableVacationTypes())
+ ->noStates(VacationRequestState::failedStates()),
+ )
+ ->count();
+ }
+
+ protected function getLimitableVacationTypes(): Collection
+ {
+ $types = new Collection(VacationType::cases());
+
+ return $types->filter(fn(VacationType $type) => $this->configRetriever->hasLimit($type));
}
}
diff --git a/app/Eloquent/Models/User.php b/app/Eloquent/Models/User.php
index 6a7c4db..1f8c6e3 100644
--- a/app/Eloquent/Models/User.php
+++ b/app/Eloquent/Models/User.php
@@ -28,6 +28,7 @@ use Toby\Domain\Enums\Role;
* @property Carbon $employment_date
* @property Collection $vacationLimits
* @property Collection $vacationRequests
+ * @property Collection $vacations
*/
class User extends Authenticatable
{
@@ -57,6 +58,11 @@ class User extends Authenticatable
return $this->hasMany(VacationRequest::class);
}
+ public function vacations(): HasMany
+ {
+ return $this->hasMany(Vacation::class);
+ }
+
public function scopeSearch(Builder $query, ?string $text): Builder
{
if ($text === null) {
diff --git a/app/Eloquent/Models/Vacation.php b/app/Eloquent/Models/Vacation.php
new file mode 100644
index 0000000..c22cf76
--- /dev/null
+++ b/app/Eloquent/Models/Vacation.php
@@ -0,0 +1,43 @@
+ "date",
+ ];
+
+ public function user(): BelongsTo
+ {
+ return $this->belongsTo(User::class);
+ }
+
+ public function vacationRequest(): BelongsTo
+ {
+ return $this->belongsTo(VacationRequest::class);
+ }
+
+ public function yearPeriod(): BelongsTo
+ {
+ return $this->belongsTo(YearPeriod::class);
+ }
+}
diff --git a/app/Eloquent/Models/VacationRequest.php b/app/Eloquent/Models/VacationRequest.php
index 0a81620..f8d29b9 100644
--- a/app/Eloquent/Models/VacationRequest.php
+++ b/app/Eloquent/Models/VacationRequest.php
@@ -21,11 +21,11 @@ use Toby\Domain\Enums\VacationType;
* @property VacationRequestState $state
* @property Carbon $from
* @property Carbon $to
- * @property int $estimated_days
* @property string $comment
* @property User $user
* @property YearPeriod $yearPeriod
* @property Collection $activities
+ * @property Collection $vacations
* @property Carbon $created_at
* @property Carbon $updated_at
*/
@@ -57,6 +57,11 @@ class VacationRequest extends Model
return $this->hasMany(VacationRequestActivity::class);
}
+ public function vacations(): HasMany
+ {
+ return $this->hasMany(Vacation::class);
+ }
+
public function changeStateTo(VacationRequestState $state): void
{
$this->state = $state;
@@ -69,6 +74,11 @@ class VacationRequest extends Model
return $query->whereIn("state", $states);
}
+ public function scopeNoStates(Builder $query, array $states): Builder
+ {
+ return $query->whereNotIn("state", $states);
+ }
+
public function scopeOverlapsWith(Builder $query, self $vacationRequest): Builder
{
return $query->where("from", "<=", $vacationRequest->to)
diff --git a/app/Infrastructure/Http/Controllers/VacationCalendarController.php b/app/Infrastructure/Http/Controllers/VacationCalendarController.php
new file mode 100644
index 0000000..d37cce6
--- /dev/null
+++ b/app/Infrastructure/Http/Controllers/VacationCalendarController.php
@@ -0,0 +1,38 @@
+query("month", Carbon::now()->englishMonth));
+ $yearPeriod = $yearPeriodRetriever->selected();
+ $users = User::query()
+ ->orderBy("last_name")
+ ->orderBy("first_name")
+ ->get();
+
+ $calendar = $calendarGenerator->generate($yearPeriod, $month);
+
+ return inertia("Calendar", [
+ "calendar" => $calendar,
+ "currentMonth" => $month,
+ "users" => UserResource::collection($users),
+ ]);
+ }
+}
diff --git a/app/Infrastructure/Http/Controllers/VacationRequestController.php b/app/Infrastructure/Http/Controllers/VacationRequestController.php
index ca8be78..814b35d 100644
--- a/app/Infrastructure/Http/Controllers/VacationRequestController.php
+++ b/app/Infrastructure/Http/Controllers/VacationRequestController.php
@@ -24,16 +24,21 @@ class VacationRequestController extends Controller
{
public function index(Request $request, YearPeriodRetriever $yearPeriodRetriever): Response
{
+ $status = $request->get("status", "all");
+
$vacationRequests = $request->user()
->vacationRequests()
+ ->with("vacations")
->where("year_period_id", $yearPeriodRetriever->selected()->id)
->latest()
- ->states(VacationRequestState::filterByStatus($request->query("status", "all")))
+ ->states(VacationRequestState::filterByStatus($status))
->paginate();
return inertia("VacationRequest/Index", [
"requests" => VacationRequestResource::collection($vacationRequests),
- "filters" => $request->only("status"),
+ "filters" => [
+ "status" => $status,
+ ],
]);
}
@@ -69,15 +74,24 @@ class VacationRequestController extends Controller
): RedirectResponse {
/** @var VacationRequest $vacationRequest */
$vacationRequest = $request->user()->vacationRequests()->make($request->data());
- $vacationRequest->estimated_days = $vacationDaysCalculator->calculateDays(
- $vacationRequest->yearPeriod,
- $vacationRequest->from,
- $vacationRequest->to,
- )->count();
-
$vacationRequestValidator->validate($vacationRequest);
$vacationRequest->save();
+
+ $days = $vacationDaysCalculator->calculateDays(
+ $vacationRequest->yearPeriod,
+ $vacationRequest->from,
+ $vacationRequest->to,
+ );
+
+ foreach ($days as $day) {
+ $vacationRequest->vacations()->create([
+ "date" => $day,
+ "user_id" => $vacationRequest->user->id,
+ "year_period_id" => $vacationRequest->yearPeriod->id,
+ ]);
+ }
+
$stateManager->markAsCreated($vacationRequest);
return redirect()
diff --git a/app/Infrastructure/Http/Resources/VacationRequestActivityResource.php b/app/Infrastructure/Http/Resources/VacationRequestActivityResource.php
index dcc11d2..c513785 100644
--- a/app/Infrastructure/Http/Resources/VacationRequestActivityResource.php
+++ b/app/Infrastructure/Http/Resources/VacationRequestActivityResource.php
@@ -13,9 +13,11 @@ class VacationRequestActivityResource extends JsonResource
public function toArray($request): array
{
return [
- "date" => $this->created_at->toDisplayString(),
- "who" => $this->user ? $this->user->fullName : __("System"),
- "to" => $this->to->label(),
+ "date" => $this->created_at->format("d.m.Y"),
+ "time" => $this->created_at->format("H:i"),
+ "user" => $this->user ? $this->user->fullName : __("System"),
+ "state" => $this->to,
+ "text" => $this->to->label(),
];
}
}
diff --git a/app/Infrastructure/Http/Resources/VacationRequestResource.php b/app/Infrastructure/Http/Resources/VacationRequestResource.php
index 009eeb6..112d7a0 100644
--- a/app/Infrastructure/Http/Resources/VacationRequestResource.php
+++ b/app/Infrastructure/Http/Resources/VacationRequestResource.php
@@ -17,11 +17,11 @@ class VacationRequestResource extends JsonResource
"name" => $this->name,
"user" => new UserResource($this->user),
"type" => $this->type->label(),
- "state" => $this->state->label(),
+ "state" => $this->state,
"from" => $this->from->toDisplayString(),
"to" => $this->to->toDisplayString(),
- "estimatedDays" => $this->estimated_days,
"comment" => $this->comment,
+ "days" => VacationResource::collection($this->vacations),
];
}
}
diff --git a/app/Infrastructure/Http/Resources/VacationResource.php b/app/Infrastructure/Http/Resources/VacationResource.php
new file mode 100644
index 0000000..e9590e3
--- /dev/null
+++ b/app/Infrastructure/Http/Resources/VacationResource.php
@@ -0,0 +1,21 @@
+ $this->id,
+ "displayDate" => $this->date->toDisplayString(),
+ "date" => $this->date->toDateString(),
+ ];
+ }
+}
diff --git a/config/vacation_types.php b/config/vacation_types.php
index 5039dfe..45a5900 100644
--- a/config/vacation_types.php
+++ b/config/vacation_types.php
@@ -6,55 +6,55 @@ use Toby\Domain\Enums\VacationType;
use Toby\Domain\VacationTypeConfigRetriever;
return [
- VacationType::VACATION->value => [
+ VacationType::Vacation->value => [
VacationTypeConfigRetriever::KEY_TECHNICAL_APPROVAL => true,
VacationTypeConfigRetriever::KEY_ADMINISTRATIVE_APPROVAL => true,
VacationTypeConfigRetriever::KEY_BILLABLE => true,
VacationTypeConfigRetriever::KEY_HAS_LIMIT => true,
],
- VacationType::VACATION_ON_REQUEST->value => [
+ VacationType::OnRequest->value => [
VacationTypeConfigRetriever::KEY_TECHNICAL_APPROVAL => true,
VacationTypeConfigRetriever::KEY_ADMINISTRATIVE_APPROVAL => true,
VacationTypeConfigRetriever::KEY_BILLABLE => true,
VacationTypeConfigRetriever::KEY_HAS_LIMIT => true,
],
- VacationType::TIME_IN_LIEU->value => [
+ VacationType::TimeInLieu->value => [
VacationTypeConfigRetriever::KEY_TECHNICAL_APPROVAL => false,
VacationTypeConfigRetriever::KEY_ADMINISTRATIVE_APPROVAL => false,
VacationTypeConfigRetriever::KEY_BILLABLE => true,
VacationTypeConfigRetriever::KEY_HAS_LIMIT => false,
],
- VacationType::SICK_VACATION->value => [
+ VacationType::Sick->value => [
VacationTypeConfigRetriever::KEY_TECHNICAL_APPROVAL => false,
VacationTypeConfigRetriever::KEY_ADMINISTRATIVE_APPROVAL => true,
VacationTypeConfigRetriever::KEY_BILLABLE => true,
VacationTypeConfigRetriever::KEY_HAS_LIMIT => false,
],
- VacationType::UNPAID_VACATION->value => [
+ VacationType::Unpaid->value => [
VacationTypeConfigRetriever::KEY_TECHNICAL_APPROVAL => true,
VacationTypeConfigRetriever::KEY_ADMINISTRATIVE_APPROVAL => true,
VacationTypeConfigRetriever::KEY_BILLABLE => false,
VacationTypeConfigRetriever::KEY_HAS_LIMIT => false,
],
- VacationType::SPECIAL_VACATION->value => [
+ VacationType::Special->value => [
VacationTypeConfigRetriever::KEY_TECHNICAL_APPROVAL => true,
VacationTypeConfigRetriever::KEY_ADMINISTRATIVE_APPROVAL => true,
VacationTypeConfigRetriever::KEY_BILLABLE => false,
VacationTypeConfigRetriever::KEY_HAS_LIMIT => false,
],
- VacationType::CHILDCARE_VACATION->value => [
+ VacationType::Childcare->value => [
VacationTypeConfigRetriever::KEY_TECHNICAL_APPROVAL => true,
VacationTypeConfigRetriever::KEY_ADMINISTRATIVE_APPROVAL => true,
VacationTypeConfigRetriever::KEY_BILLABLE => false,
VacationTypeConfigRetriever::KEY_HAS_LIMIT => false,
],
- VacationType::TRAINING_VACATION->value => [
+ VacationType::Training->value => [
VacationTypeConfigRetriever::KEY_TECHNICAL_APPROVAL => true,
VacationTypeConfigRetriever::KEY_ADMINISTRATIVE_APPROVAL => true,
VacationTypeConfigRetriever::KEY_BILLABLE => true,
VacationTypeConfigRetriever::KEY_HAS_LIMIT => false,
],
- VacationType::VOLUNTEERING_VACATION->value => [
+ VacationType::Volunteering->value => [
VacationTypeConfigRetriever::KEY_TECHNICAL_APPROVAL => true,
VacationTypeConfigRetriever::KEY_ADMINISTRATIVE_APPROVAL => true,
VacationTypeConfigRetriever::KEY_BILLABLE => true,
diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php
index c6a5d93..23f0ea4 100644
--- a/database/factories/UserFactory.php
+++ b/database/factories/UserFactory.php
@@ -23,7 +23,7 @@ class UserFactory extends Factory
"email" => $this->faker->unique()->safeEmail(),
"employment_form" => $this->faker->randomElement(EmploymentForm::cases()),
"position" => $this->faker->jobTitle(),
- "role" => Role::EMPLOYEE,
+ "role" => Role::Employee,
"employment_date" => Carbon::createFromInterface($this->faker->dateTimeBetween("2020-10-27"))->toDateString(),
"remember_token" => Str::random(10),
];
diff --git a/database/factories/VacationFactory.php b/database/factories/VacationFactory.php
new file mode 100644
index 0000000..d70c43d
--- /dev/null
+++ b/database/factories/VacationFactory.php
@@ -0,0 +1,15 @@
+ $this->faker->randomElement(VacationRequestState::cases()),
"from" => $from,
"to" => $from->addDays($days),
- "estimated_days" => fn(array $attributes) => $this->estimateDays($attributes),
"comment" => $this->faker->boolean ? $this->faker->paragraph() : null,
];
}
@@ -43,11 +41,4 @@ class VacationRequestFactory extends Factory
return "{$number}/{$year}";
}
-
- protected function estimateDays(array $attributes): int
- {
- $period = CarbonPeriod::create($attributes["from"], $attributes["to"]);
-
- return $period->count();
- }
}
diff --git a/database/migrations/2014_10_12_000000_create_users_table.php b/database/migrations/2014_10_12_000000_create_users_table.php
index 5f09111..7b3f574 100644
--- a/database/migrations/2014_10_12_000000_create_users_table.php
+++ b/database/migrations/2014_10_12_000000_create_users_table.php
@@ -16,7 +16,7 @@ return new class() extends Migration {
$table->string("last_name");
$table->string("email")->unique();
$table->string("avatar")->nullable();
- $table->string("role")->default(Role::EMPLOYEE->value);
+ $table->string("role")->default(Role::Employee->value);
$table->string("position");
$table->string("employment_form");
$table->date("employment_date");
diff --git a/database/migrations/2022_01_26_100039_create_vacation_requests_table.php b/database/migrations/2022_01_26_100039_create_vacation_requests_table.php
index b3f4e48..f5270da 100644
--- a/database/migrations/2022_01_26_100039_create_vacation_requests_table.php
+++ b/database/migrations/2022_01_26_100039_create_vacation_requests_table.php
@@ -18,7 +18,6 @@ return new class() extends Migration {
$table->foreignIdFor(YearPeriod::class)->constrained()->cascadeOnDelete();
$table->string("type");
$table->string("state")->nullable();
- $table->integer("estimated_days");
$table->date("from");
$table->date("to");
$table->text("comment")->nullable();
diff --git a/database/migrations/2022_02_07_133018_create_vacations_table.php b/database/migrations/2022_02_07_133018_create_vacations_table.php
new file mode 100644
index 0000000..648be3c
--- /dev/null
+++ b/database/migrations/2022_02_07_133018_create_vacations_table.php
@@ -0,0 +1,28 @@
+id();
+ $table->foreignIdFor(User::class)->constrained()->cascadeOnDelete();
+ $table->foreignIdFor(VacationRequest::class)->constrained()->cascadeOnDelete();
+ $table->foreignIdFor(YearPeriod::class)->constrained()->cascadeOnDelete();
+ $table->date("date");
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists("vacations");
+ }
+};
diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php
index 205246a..614d712 100644
--- a/database/seeders/DatabaseSeeder.php
+++ b/database/seeders/DatabaseSeeder.php
@@ -8,6 +8,7 @@ use Illuminate\Database\Seeder;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Toby\Domain\PolishHolidaysRetriever;
+use Toby\Domain\VacationDaysCalculator;
use Toby\Eloquent\Helpers\UserAvatarGenerator;
use Toby\Eloquent\Models\User;
use Toby\Eloquent\Models\VacationLimit;
@@ -79,6 +80,21 @@ class DatabaseSeeder extends Seeder
->sequence(fn() => [
"year_period_id" => $yearPeriods->random()->id,
])
+ ->afterCreating(function (VacationRequest $vacationRequest): void {
+ $days = app(VacationDaysCalculator::class)->calculateDays(
+ $vacationRequest->yearPeriod,
+ $vacationRequest->from,
+ $vacationRequest->to,
+ );
+
+ foreach ($days as $day) {
+ $vacationRequest->vacations()->create([
+ "date" => $day,
+ "user_id" => $vacationRequest->user->id,
+ "year_period_id" => $vacationRequest->yearPeriod->id,
+ ]);
+ }
+ })
->create();
}
}
diff --git a/resources/js/Composables/monthInfo.js b/resources/js/Composables/monthInfo.js
new file mode 100644
index 0000000..3d7af0a
--- /dev/null
+++ b/resources/js/Composables/monthInfo.js
@@ -0,0 +1,60 @@
+const months = [
+ {
+ 'name': 'Styczeń',
+ 'value': 'january',
+ },
+ {
+ 'name': 'Luty',
+ 'value': 'february',
+ },
+ {
+ 'name': 'Marzec',
+ 'value': 'march',
+ },
+ {
+ 'name': 'Kwiecień',
+ 'value': 'april',
+ },
+ {
+ 'name': 'Maj',
+ 'value': 'may',
+ },
+ {
+ 'name': 'Czerwiec',
+ 'value': 'june',
+ },
+ {
+ 'name': 'Lipiec',
+ 'value': 'july',
+ },
+ {
+ 'name': 'Sierpień',
+ 'value': 'august',
+ },
+ {
+ 'name': 'Wrzesień',
+ 'value': 'september',
+ },
+ {
+ 'name': 'Październik',
+ 'value': 'october',
+ },
+ {
+ 'name': 'Listopad',
+ 'value': 'november',
+ },
+ {
+ 'name': 'Grudzień',
+ 'value': 'december',
+ },
+]
+
+export function useMonthInfo() {
+ const getMonths = () => months
+ const findMonth = value => months.find(month => month.value === value)
+
+ return {
+ getMonths,
+ findMonth,
+ }
+}
diff --git a/resources/js/Composables/statusInfo.js b/resources/js/Composables/statusInfo.js
new file mode 100644
index 0000000..94026d7
--- /dev/null
+++ b/resources/js/Composables/statusInfo.js
@@ -0,0 +1,128 @@
+import {
+ CheckIcon as OutlineCheckIcon,
+ ClockIcon as OutlineClockIcon,
+ DocumentTextIcon as OutlineDocumentTextIcon,
+ ThumbDownIcon as OutlineThumbDownIcon,
+ ThumbUpIcon as OutlineThumbUpIcon,
+ XIcon as OutlineXIcon,
+} from '@heroicons/vue/outline'
+
+import {
+ CheckIcon as SolidCheckIcon,
+ ClockIcon as SolidClockIcon,
+ DocumentTextIcon as SolidDocumentTextIcon,
+ ThumbDownIcon as SolidThumbDownIcon,
+ ThumbUpIcon as SolidThumbUpIcon,
+ XIcon as SolidXIcon,
+} from '@heroicons/vue/solid'
+
+const statuses = [
+ {
+ text: 'Utworzony',
+ value: 'created',
+ outline: {
+ icon: OutlineDocumentTextIcon,
+ foreground: 'text-white',
+ background: 'bg-gray-400',
+ },
+ solid: {
+ icon: SolidDocumentTextIcon,
+ color: 'text-gray-400',
+ },
+ },
+ {
+ text: 'Czeka na akceptację od przełożonego technicznego',
+ value: 'waiting_for_technical',
+ outline: {
+ icon: OutlineClockIcon,
+ foreground: 'text-white',
+ background: 'bg-amber-400',
+ },
+ solid: {
+ icon: SolidClockIcon,
+ color: 'text-amber-400',
+ },
+ },
+ {
+ text: 'Czeka na akceptację od przełożonego administracyjnego',
+ value: 'waiting_for_administrative',
+ outline: {
+ icon: OutlineClockIcon,
+ foreground: 'text-white',
+ background: 'bg-amber-400',
+ },
+ solid: {
+ icon: SolidClockIcon,
+ color: 'text-amber-400',
+ },
+ },
+ {
+ text: 'Odrzucony',
+ value: 'rejected',
+ outline: {
+ icon: OutlineThumbDownIcon,
+ foreground: 'text-white',
+ background: 'bg-rose-600',
+ },
+ solid: {
+ icon: SolidThumbDownIcon,
+ color: 'text-rose-600',
+ },
+ },
+ {
+ text: 'Zaakceptowany przez przełożonego technicznego',
+ value: 'accepted_by_technical',
+ outline: {
+ icon: OutlineThumbUpIcon,
+ foreground: 'text-white',
+ background: 'bg-green-500',
+ },
+ solid: {
+ icon: SolidThumbUpIcon,
+ color: 'text-green-500',
+ },
+ },
+ {
+ text: 'Zaakceptowany przez przełożonego administracyjnego',
+ value: 'accepted_by_administrative',
+ outline: {
+ icon: OutlineThumbUpIcon,
+ foreground: 'text-white',
+ background: 'bg-green-500',
+ },
+ solid: {
+ icon: SolidThumbUpIcon,
+ color: 'text-green-500',
+ },
+ },
+ {
+ text: 'Zatwierdzony',
+ value: 'approved',
+ outline: {
+ icon: OutlineCheckIcon,
+ foreground: 'text-white',
+ background: 'bg-blumilk-500',
+ },
+ solid: {
+ icon: SolidCheckIcon,
+ color: 'text-blumilk-500',
+ },
+ },
+ {
+ text: 'Anulowany',
+ value: 'canceled',
+ outline: {
+ icon: OutlineXIcon,
+ foreground: 'text-white',
+ background: 'bg-gray-900',
+ },
+ solid: {
+ icon: SolidXIcon,
+ color: 'text-gray-900',
+ },
+ },
+]
+
+export function useStatusInfo(status) {
+ return statuses.find(statusInfo => statusInfo.value === status)
+}
diff --git a/resources/js/Pages/Calendar.vue b/resources/js/Pages/Calendar.vue
new file mode 100644
index 0000000..1415b0a
--- /dev/null
+++ b/resources/js/Pages/Calendar.vue
@@ -0,0 +1,167 @@
+
+
+ {{ day.dayOfWeek }}
+
+ Kalendarz urlopów
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+