From e2d7ddd36b583200a5d3f218a62dd0869579b988 Mon Sep 17 00:00:00 2001
From: Kamil Piech
Date: Fri, 28 Jun 2024 08:33:42 +0200
Subject: [PATCH] #443 - employment, medical and ohs history (#451)
* #443 - added tests for user history
* #443 - added console command for migrating data
* #443 - cr fixes
* #443 - fix: code review fixes
* #443 - fix: fixes after code review - fixed validation and edit view
* #443 - fix: fixes
---
.../MigrateProfileDataIntoUserHistory.php | 49 ++++
app/Enums/UserHistoryType.php | 29 ++
app/Http/Controllers/UserController.php | 1 +
.../Controllers/UserHistoryController.php | 86 ++++++
app/Http/Requests/UserHistoryRequest.php | 35 +++
app/Http/Requests/UserRequest.php | 6 -
app/Http/Resources/UserHistoryResource.php | 27 ++
app/Http/Resources/UserResource.php | 11 +-
app/Models/User.php | 22 ++
app/Models/UserHistory.php | 40 +++
database/factories/UserHistoryFactory.php | 30 ++
...24_06_14_112722_add_user_history_table.php | 28 ++
lang/pl.json | 8 +-
lang/pl/validation.php | 9 +
resources/js/Pages/UserHistory/Create.vue | 276 ++++++++++++++++++
resources/js/Pages/UserHistory/Edit.vue | 276 ++++++++++++++++++
resources/js/Pages/UserHistory/Index.vue | 166 +++++++++++
resources/js/Pages/Users/Create.vue | 92 ------
resources/js/Pages/Users/Edit.vue | 92 ------
resources/js/Pages/Users/Index.vue | 13 +-
routes/web.php | 14 +
tests/Feature/UserHistoryTest.php | 109 +++++++
22 files changed, 1223 insertions(+), 196 deletions(-)
create mode 100644 app/Console/Commands/MigrateProfileDataIntoUserHistory.php
create mode 100644 app/Enums/UserHistoryType.php
create mode 100644 app/Http/Controllers/UserHistoryController.php
create mode 100644 app/Http/Requests/UserHistoryRequest.php
create mode 100644 app/Http/Resources/UserHistoryResource.php
create mode 100644 app/Models/UserHistory.php
create mode 100644 database/factories/UserHistoryFactory.php
create mode 100644 database/migrations/2024_06_14_112722_add_user_history_table.php
create mode 100644 resources/js/Pages/UserHistory/Create.vue
create mode 100644 resources/js/Pages/UserHistory/Edit.vue
create mode 100644 resources/js/Pages/UserHistory/Index.vue
create mode 100644 tests/Feature/UserHistoryTest.php
diff --git a/app/Console/Commands/MigrateProfileDataIntoUserHistory.php b/app/Console/Commands/MigrateProfileDataIntoUserHistory.php
new file mode 100644
index 00000000..c46f23c3
--- /dev/null
+++ b/app/Console/Commands/MigrateProfileDataIntoUserHistory.php
@@ -0,0 +1,49 @@
+with("profile")
+ ->get();
+
+ foreach ($users as $user) {
+ $this->moveMedicalDataToHistory($user);
+ $this->moveOhsDataToHistory($user);
+ }
+ }
+
+ private function moveMedicalDataToHistory(User $user): void
+ {
+ if ($user->profile->last_medical_exam_date && $user->profile->next_medical_exam_date) {
+ $user->histories()->create([
+ "from" => $user->profile->last_medical_exam_date,
+ "to" => $user->profile->next_medical_exam_date,
+ "type" => UserHistoryType::MedicalExam,
+ ]);
+ }
+ }
+
+ private function moveOhsDataToHistory(User $user): void
+ {
+ if ($user->profile->last_ohs_training_date && $user->profile->next_ohs_training_date) {
+ $user->histories()->create([
+ "from" => $user->profile->last_ohs_training_date,
+ "to" => $user->profile->next_ohs_training_date,
+ "type" => UserHistoryType::OhsTraining,
+ ]);
+ }
+ }
+}
diff --git a/app/Enums/UserHistoryType.php b/app/Enums/UserHistoryType.php
new file mode 100644
index 00000000..6b4a2b9e
--- /dev/null
+++ b/app/Enums/UserHistoryType.php
@@ -0,0 +1,29 @@
+value);
+ }
+
+ public static function casesToSelect(): array
+ {
+ $cases = collect(UserHistoryType::cases());
+
+ return $cases->map(
+ fn(UserHistoryType $enum): array => [
+ "label" => $enum->label(),
+ "value" => $enum->value,
+ ],
+ )->toArray();
+ }
+}
diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php
index 659f70d6..72e81526 100644
--- a/app/Http/Controllers/UserController.php
+++ b/app/Http/Controllers/UserController.php
@@ -31,6 +31,7 @@ public function index(Request $request): Response
$status = $request->query("status", "active");
$users = User::query()
+ ->with("histories")
->search($searchText)
->status($status)
->orderByProfileField("last_name")
diff --git a/app/Http/Controllers/UserHistoryController.php b/app/Http/Controllers/UserHistoryController.php
new file mode 100644
index 00000000..ffe8d00e
--- /dev/null
+++ b/app/Http/Controllers/UserHistoryController.php
@@ -0,0 +1,86 @@
+authorize("manageUsers");
+
+ $history = $user->histories()
+ ->orderBy("from", "desc")
+ ->get();
+
+ return inertia("UserHistory/Index", [
+ "history" => UserHistoryResource::collection($history),
+ "userId" => $user->id,
+ ]);
+ }
+
+ public function create(User $user): Response
+ {
+ $this->authorize("manageUsers");
+
+ return inertia("UserHistory/Create", [
+ "types" => UserHistoryType::casesToSelect(),
+ "employmentForms" => EmploymentForm::casesToSelect(),
+ "userId" => $user->id,
+ ]);
+ }
+
+ public function store(UserHistoryRequest $request, User $user): RedirectResponse
+ {
+ $this->authorize("manageUsers");
+
+ $user->histories()->create($request->data());
+
+ return redirect()
+ ->route("users.history", $user)
+ ->with("success", __("User history created."));
+ }
+
+ public function edit(UserHistory $history): Response
+ {
+ $this->authorize("manageUsers");
+
+ return inertia("UserHistory/Edit", [
+ "history" => UserHistoryResource::make($history),
+ "types" => UserHistoryType::casesToSelect(),
+ "employmentForms" => EmploymentForm::casesToSelect(),
+ ]);
+ }
+
+ public function update(UserHistoryRequest $request, UserHistory $history): RedirectResponse
+ {
+ $this->authorize("manageUsers");
+
+ $history->update($request->data());
+
+ return redirect()
+ ->route("users.history", $history->user_id)
+ ->with("success", __("User history updated."));
+ }
+
+ public function destroy(UserHistory $history): RedirectResponse
+ {
+ $this->authorize("manageUsers");
+
+ $history->delete();
+
+ return redirect()
+ ->back()
+ ->with("success", __("User history deleted."));
+ }
+}
diff --git a/app/Http/Requests/UserHistoryRequest.php b/app/Http/Requests/UserHistoryRequest.php
new file mode 100644
index 00000000..c7e3f93f
--- /dev/null
+++ b/app/Http/Requests/UserHistoryRequest.php
@@ -0,0 +1,35 @@
+ ["required", "date"],
+ "to" => ["nullable", "date", "after:from", "required_if:type," . UserHistoryType::MedicalExam->value . "," . UserHistoryType::OhsTraining->value],
+ "comment" => ["nullable", "string", "max:255"],
+ "type" => ["required", new Enum(UserHistoryType::class)],
+ "employmentForm" => [new Enum(EmploymentForm::class), "required_if:type," . UserHistoryType::Employment->value],
+ ];
+ }
+
+ public function data(): array
+ {
+ return [
+ "from" => $this->get("from"),
+ "to" => $this->get("to"),
+ "type" => $this->get("type"),
+ "employment_form" => $this->get("type") === UserHistoryType::Employment->value ? $this->get("employmentForm") : null,
+ "comment" => $this->get("comment"),
+ ];
+ }
+}
diff --git a/app/Http/Requests/UserRequest.php b/app/Http/Requests/UserRequest.php
index de04008c..f547cd29 100644
--- a/app/Http/Requests/UserRequest.php
+++ b/app/Http/Requests/UserRequest.php
@@ -24,8 +24,6 @@ public function rules(): array
"employmentDate" => ["required", "date_format:Y-m-d"],
"birthday" => ["required", "date_format:Y-m-d", "before:today"],
"slackId" => [],
- "nextMedicalExamDate" => ["nullable", "after:lastMedicalExamDate"],
- "nextOhsTrainingDate" => ["nullable", "after:lastOhsTrainingDate"],
];
}
@@ -47,10 +45,6 @@ public function profileData(): array
"employment_date" => $this->get("employmentDate"),
"birthday" => $this->get("birthday"),
"slack_id" => $this->get("slackId"),
- "last_medical_exam_date" => $this->get("lastMedicalExamDate"),
- "next_medical_exam_date" => $this->get("nextMedicalExamDate"),
- "last_ohs_training_date" => $this->get("lastOhsTrainingDate"),
- "next_ohs_training_date" => $this->get("nextOhsTrainingDate"),
];
}
}
diff --git a/app/Http/Resources/UserHistoryResource.php b/app/Http/Resources/UserHistoryResource.php
new file mode 100644
index 00000000..52bb80b9
--- /dev/null
+++ b/app/Http/Resources/UserHistoryResource.php
@@ -0,0 +1,27 @@
+ $this->id,
+ "from" => $this->from->format("d.m.Y"),
+ "to" => $this->to?->format("d.m.Y"),
+ "type" => $this->type->value,
+ "typeLabel" => $this->type->label(),
+ "employmentFormLabel" => $this->employment_form?->label(),
+ "employmentForm" => $this->employment_form?->value,
+ "comment" => $this->comment,
+ "userId" => $this->user_id,
+ ];
+ }
+}
diff --git a/app/Http/Resources/UserResource.php b/app/Http/Resources/UserResource.php
index 8360cfa5..52b2f1ed 100644
--- a/app/Http/Resources/UserResource.php
+++ b/app/Http/Resources/UserResource.php
@@ -12,6 +12,9 @@ class UserResource extends JsonResource
public function toArray($request): array
{
+ $lastMedicalExam = $this->lastMedicalExam();
+ $lastOhsTraining = $this->lastOhsTraining();
+
return [
"id" => $this->id,
"name" => $this->profile->full_name,
@@ -23,10 +26,10 @@ public function toArray($request): array
"lastActiveAt" => $this->last_active_at?->toDateTimeString(),
"employmentForm" => $this->profile->employment_form->label(),
"employmentDate" => $this->profile->employment_date->toDisplayString(),
- "lastMedicalExamDate" => $this->profile->last_medical_exam_date?->toDisplayString(),
- "nextMedicalExamDate" => $this->profile->next_medical_exam_date?->toDisplayString(),
- "lastOhsTrainingDate" => $this->profile->last_ohs_training_date?->toDisplayString(),
- "nextOhsTrainingDate" => $this->profile->next_ohs_training_date?->toDisplayString(),
+ "lastMedicalExamDate" => $lastMedicalExam?->from?->toDisplayString(),
+ "nextMedicalExamDate" => $lastMedicalExam?->to?->toDisplayString(),
+ "lastOhsTrainingDate" => $lastOhsTraining?->from?->toDisplayString(),
+ "nextOhsTrainingDate" => $lastOhsTraining?->to?->toDisplayString(),
];
}
}
diff --git a/app/Models/User.php b/app/Models/User.php
index a6027a7e..8ff620e3 100644
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -19,6 +19,7 @@
use Spatie\Permission\Traits\HasRoles;
use Toby\Enums\EmploymentForm;
use Toby\Enums\Role;
+use Toby\Enums\UserHistoryType;
use Toby\Notifications\Notifiable as NotifiableInterface;
/**
@@ -80,6 +81,27 @@ public function vacations(): HasMany
return $this->hasMany(Vacation::class);
}
+ public function histories(): HasMany
+ {
+ return $this->hasMany(UserHistory::class);
+ }
+
+ public function lastMedicalExam(): ?UserHistory
+ {
+ return $this->histories()
+ ->where("type", UserHistoryType::MedicalExam)
+ ->orderBy("from", "desc")
+ ->first();
+ }
+
+ public function lastOhsTraining(): ?UserHistory
+ {
+ return $this->histories()
+ ->where("type", UserHistoryType::OhsTraining)
+ ->orderBy("from", "desc")
+ ->first();
+ }
+
public function keys(): HasMany
{
return $this->hasMany(Key::class);
diff --git a/app/Models/UserHistory.php b/app/Models/UserHistory.php
new file mode 100644
index 00000000..740d1ef3
--- /dev/null
+++ b/app/Models/UserHistory.php
@@ -0,0 +1,40 @@
+ "date:Y-m-d",
+ "to" => "date:Y-m-d",
+ "type" => UserHistoryType::class,
+ "employment_form" => EmploymentForm::class,
+ ];
+
+ public function user(): BelongsTo
+ {
+ return $this->belongsTo(User::class);
+ }
+}
diff --git a/database/factories/UserHistoryFactory.php b/database/factories/UserHistoryFactory.php
new file mode 100644
index 00000000..88432e92
--- /dev/null
+++ b/database/factories/UserHistoryFactory.php
@@ -0,0 +1,30 @@
+faker->randomElement(UserHistoryType::cases());
+
+ return [
+ "user_id" => User::factory(),
+ "from" => $this->faker->date(),
+ "to" => $this->faker->date(),
+ "type" => $type,
+ "employment_form" => $type->is(UserHistoryType::Employment) ? $this->faker->randomElement(EmploymentForm::cases()) : null,
+ "comment" => $this->faker->sentence(),
+ ];
+ }
+}
diff --git a/database/migrations/2024_06_14_112722_add_user_history_table.php b/database/migrations/2024_06_14_112722_add_user_history_table.php
new file mode 100644
index 00000000..bb33d06e
--- /dev/null
+++ b/database/migrations/2024_06_14_112722_add_user_history_table.php
@@ -0,0 +1,28 @@
+id();
+ $table->foreignId("user_id")->constrained()->cascadeOnDelete();
+ $table->string("comment")->nullable();
+ $table->date("from");
+ $table->date("to")->nullable();
+ $table->string("type");
+ $table->string("employment_form")->nullable();
+ $table->timestamps();
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists("user_histories");
+ }
+};
diff --git a/lang/pl.json b/lang/pl.json
index c17ba212..fb471647 100644
--- a/lang/pl.json
+++ b/lang/pl.json
@@ -3,6 +3,9 @@
"employment_contract": "Umowa o pracę",
"commission_contract": "Umowa zlecenie",
"b2b_contract": "Kontrakt B2B",
+ "employment": "Zatrudnienie",
+ "medical_exam": "Badanie lekarskie",
+ "ohs_training": "Szkolenie BHP",
"board_member_contract": "Członek zarządu",
"vacation": "Urlop wypoczynkowy",
"vacation_on_request": "Urlop na żądanie",
@@ -182,5 +185,8 @@
"Labels": "Etykiety",
"Is mobile": "Czy mobilny",
"Assignee": "Przypisany do",
- "Assigned at": "Przypisany od"
+ "Assigned at": "Przypisany od",
+ "User history created.": "Historia użytkownika utworzona.",
+ "User history updated.": "Historia użytkownika zaktualizowana.",
+ "User history deleted.": "Historia użytkownika usunięta."
}
diff --git a/lang/pl/validation.php b/lang/pl/validation.php
index cb5e6205..0cf194ee 100644
--- a/lang/pl/validation.php
+++ b/lang/pl/validation.php
@@ -183,6 +183,12 @@
"birthday" => [
"before" => "Data urodzenia musi być datą wcześniejszą od dzisiaj.",
],
+ "employmentForm" => [
+ "required_if" => "Forma zatrudnienia jest wymagana.",
+ ],
+ "to" => [
+ "required_if" => "Data do jest wymagane.",
+ ],
],
"attributes" => [
"to" => "do",
@@ -200,5 +206,8 @@
"assignee" => "przydzielona osoba",
"assignedAt" => "data przydzielenia",
"birthday" => "data urodzenia",
+ "type" => "typ wpisu",
+ "employmentForm" => "forma zatrudnienia",
+ "comment" => "komentarz",
],
];
diff --git a/resources/js/Pages/UserHistory/Create.vue b/resources/js/Pages/UserHistory/Create.vue
new file mode 100644
index 00000000..f87e02b7
--- /dev/null
+++ b/resources/js/Pages/UserHistory/Create.vue
@@ -0,0 +1,276 @@
+
+
+
+
+
+
diff --git a/resources/js/Pages/UserHistory/Edit.vue b/resources/js/Pages/UserHistory/Edit.vue
new file mode 100644
index 00000000..0341f3dd
--- /dev/null
+++ b/resources/js/Pages/UserHistory/Edit.vue
@@ -0,0 +1,276 @@
+
+
+
+
+
+
+
+
+ Edytuj wpis
+
+
+
+
+
+
diff --git a/resources/js/Pages/UserHistory/Index.vue b/resources/js/Pages/UserHistory/Index.vue
new file mode 100644
index 00000000..24991510
--- /dev/null
+++ b/resources/js/Pages/UserHistory/Index.vue
@@ -0,0 +1,166 @@
+
+
+
+
+
+
+
+
+ Historia
+
+
+
+
+ Dodaj wpis
+
+
+
+
+
+
+
+
+
+ Typ zdarzenia
+ |
+
+ Komentarz
+ |
+
+ Od
+ |
+
+ Do
+ |
+ |
+
+
+
+
+
+ {{ item.typeLabel }} ({{ item.employmentFormLabel }})
+ |
+
+ {{ item.comment || '-' }}
+ |
+
+ {{ item.from }}
+ |
+
+ {{ item.to || '-' }}
+ |
+
+
+ |
+
+
+
+
+
+ Brak wyników
+
+
+ Dodaj pierwszy wpis
+
+
+ |
+
+
+
+
+
+
+
diff --git a/resources/js/Pages/Users/Create.vue b/resources/js/Pages/Users/Create.vue
index d4d27519..aabce72f 100644
--- a/resources/js/Pages/Users/Create.vue
+++ b/resources/js/Pages/Users/Create.vue
@@ -19,10 +19,6 @@ const form = useForm({
employmentDate: null,
birthday: null,
slackId: null,
- lastMedicalExamDate: null,
- nextMedicalExamDate: null,
- lastOhsTrainingDate: null,
- nextOhsTrainingDate: null,
})
function createUser() {
@@ -316,94 +312,6 @@ function createUser() {
-
-
-
-
-
- {{ form.errors.lastMedicalExamDate }}
-
-
-
-
-
-
-
-
- {{ form.errors.nextMedicalExamDate }}
-
-
-
-
-
-
-
-
- {{ form.errors.lastOhsTrainingDate }}
-
-
-
-
-
-
-
-
- {{ form.errors.nextOhsTrainingDate }}
-
-
-
-
-
-
-
-
- {{ form.errors.lastMedicalExamDate }}
-
-
-
-
-
-
-
-
- {{ form.errors.nextMedicalExamDate }}
-
-
-
-
-
-
-
-
- {{ form.errors.lastOhsTrainingDate }}
-
-
-
-
-
-
-
-
- {{ form.errors.nextOhsTrainingDate }}
-
-
-
{
Uprawnienia
+