From c161ef34ccc40f5f08d19ba27ec6e803c19178d6 Mon Sep 17 00:00:00 2001 From: Mateusz Lencki Date: Tue, 3 Oct 2023 15:31:07 +0200 Subject: [PATCH 01/14] #178 - Equipment management --- app/Eloquent/Models/EquipmentItem.php | 90 ++++++ app/Eloquent/Models/EquipmentLabel.php | 35 +++ app/Eloquent/Models/User.php | 4 +- .../Http/Controllers/EquipmentController.php | 122 ++++++++ .../Controllers/EquipmentLabelController.php | 57 ++++ .../Http/Requests/EquipmentLabelRequest.php | 29 ++ .../Http/Requests/EquipmentRequest.php | 35 +++ .../Http/Resources/EquipmentItemResource.php | 26 ++ .../Http/Resources/EquipmentLabelResource.php | 20 ++ config/permission.php | 3 + database/factories/EquipmentItemFactory.php | 30 ++ database/factories/EquipmentLabelFactory.php | 20 ++ ...26_140056_create_equipment_items_table.php | 29 ++ ...6_140126_create_equipment_labels_table.php | 23 ++ database/seeders/DatabaseSeeder.php | 6 + database/seeders/DemoSeeder.php | 72 +++++ lang/pl.json | 7 +- lang/pl/validation.php | 14 + resources/js/Pages/Equipment/Create.vue | 272 +++++++++++++++++ resources/js/Pages/Equipment/Edit.vue | 273 +++++++++++++++++ resources/js/Pages/Equipment/Index.vue | 275 ++++++++++++++++++ resources/js/Pages/EquipmentLabels.vue | 231 +++++++++++++++ resources/js/Pages/Resumes/Create.vue | 1 + resources/js/Pages/Resumes/Edit.vue | 1 + resources/js/Pages/VacationRequest/Create.vue | 4 +- .../js/Shared/Forms/MultipleCombobox.vue | 10 +- resources/js/Shared/MainMenu.vue | 8 + routes/web.php | 10 + tests/Feature/PermissionTest.php | 9 +- 29 files changed, 1707 insertions(+), 9 deletions(-) create mode 100644 app/Eloquent/Models/EquipmentItem.php create mode 100644 app/Eloquent/Models/EquipmentLabel.php create mode 100644 app/Infrastructure/Http/Controllers/EquipmentController.php create mode 100644 app/Infrastructure/Http/Controllers/EquipmentLabelController.php create mode 100644 app/Infrastructure/Http/Requests/EquipmentLabelRequest.php create mode 100644 app/Infrastructure/Http/Requests/EquipmentRequest.php create mode 100644 app/Infrastructure/Http/Resources/EquipmentItemResource.php create mode 100644 app/Infrastructure/Http/Resources/EquipmentLabelResource.php create mode 100644 database/factories/EquipmentItemFactory.php create mode 100644 database/factories/EquipmentLabelFactory.php create mode 100644 database/migrations/2023_09_26_140056_create_equipment_items_table.php create mode 100644 database/migrations/2023_09_26_140126_create_equipment_labels_table.php create mode 100644 resources/js/Pages/Equipment/Create.vue create mode 100644 resources/js/Pages/Equipment/Edit.vue create mode 100644 resources/js/Pages/Equipment/Index.vue create mode 100644 resources/js/Pages/EquipmentLabels.vue diff --git a/app/Eloquent/Models/EquipmentItem.php b/app/Eloquent/Models/EquipmentItem.php new file mode 100644 index 00000000..446c16b4 --- /dev/null +++ b/app/Eloquent/Models/EquipmentItem.php @@ -0,0 +1,90 @@ + "date", + "labels" => AsCollection::class, + ]; + protected $fillable = [ + "id_number", + "name", + "is_mobile", + "labels", + "assignee_id", + "assigned_at", + ]; + + public function scopeSearch(Builder $query, ?string $text): Builder + { + if ($text === null) { + return $query; + } + + return $query + ->where("id_number", "ILIKE", "%{$text}%") + ->orWhere("name", "ILIKE", "%{$text}%") + ->orWhere("labels", "ILIKE", "%{$text}%") + ->orWhereRelation( + "assignee", + fn(Builder $query): Builder => $query + ->where("email", "ILIKE", "%{$text}%") + ->orWhereRelation( + "profile", + fn(Builder $query): Builder => $query + ->where("first_name", "ILIKE", "%{$text}%") + ->orWhere("last_name", "ILIKE", "%{$text}%") + ->orWhere(DB::raw("CONCAT(first_name, ' ', last_name)"), "ILIKE", "%{$text}%"), + ), + ); + } + + public function scopeLabels(Builder $query, ?array $labels): Builder + { + if ($labels === null) { + return $query; + } + + foreach ($labels as $label) { + $query->orWhereJsonContains("labels", $label); + } + + return $query; + } + + public function assignee(): BelongsTo + { + return $this->belongsTo(User::class, "assignee_id"); + } + + protected static function newFactory(): EquipmentItemFactory + { + return EquipmentItemFactory::new(); + } +} diff --git a/app/Eloquent/Models/EquipmentLabel.php b/app/Eloquent/Models/EquipmentLabel.php new file mode 100644 index 00000000..c073daa9 --- /dev/null +++ b/app/Eloquent/Models/EquipmentLabel.php @@ -0,0 +1,35 @@ +belongsToMany(EquipmentItem::class, "equipment_items_labels"); + } + + protected static function newFactory(): EquipmentLabelFactory + { + return EquipmentLabelFactory::new(); + } +} diff --git a/app/Eloquent/Models/User.php b/app/Eloquent/Models/User.php index 0d403f30..3a92ff54 100644 --- a/app/Eloquent/Models/User.php +++ b/app/Eloquent/Models/User.php @@ -14,6 +14,7 @@ use Illuminate\Notifications\Notifiable; use Illuminate\Support\Carbon; use Illuminate\Support\Collection; +use Illuminate\Support\Facades\DB; use Spatie\Permission\Traits\HasRoles; use Toby\Domain\Enums\EmploymentForm; use Toby\Domain\Enums\Role; @@ -102,7 +103,8 @@ public function scopeSearch(Builder $query, ?string $text): Builder "profile", fn(Builder $query): Builder => $query ->where("first_name", "ILIKE", "%{$text}%") - ->orWhere("last_name", "ILIKE", "%{$text}%"), + ->orWhere("last_name", "ILIKE", "%{$text}%") + ->orWhere(DB::raw("CONCAT(first_name, ' ', last_name)"), "ILIKE", "%{$text}%"), ); } diff --git a/app/Infrastructure/Http/Controllers/EquipmentController.php b/app/Infrastructure/Http/Controllers/EquipmentController.php new file mode 100644 index 00000000..35c7536c --- /dev/null +++ b/app/Infrastructure/Http/Controllers/EquipmentController.php @@ -0,0 +1,122 @@ +authorize("manageEquipment"); + + $searchQuery = $request->query("search"); + + $equipmentItems = EquipmentItem::query() + ->search($searchQuery) + ->labels($request->query("labels")) + ->paginate() + ->withQueryString(); + + return inertia("Equipment/Index", [ + "equipmentItems" => EquipmentItemResource::collection($equipmentItems), + "labels" => EquipmentLabel::query()->pluck("name"), + "filters" => [ + "search" => $searchQuery, + ], + ]); + } + + /** + * @throws AuthorizationException + */ + public function create(): Response + { + $this->authorize("manageEquipment"); + + $users = User::query() + ->orderByProfileField("last_name") + ->orderByProfileField("first_name") + ->get(); + + return inertia("Equipment/Create", [ + "users" => SimpleUserResource::collection($users), + "labels" => EquipmentLabel::query()->pluck("name"), + ]); + } + + /** + * @throws AuthorizationException + */ + public function store( + EquipmentRequest $request, + ): RedirectResponse { + $this->authorize("manageEquipment"); + + EquipmentItem::query()->create($request->data()); + + return redirect() + ->route("equipment-items.index") + ->with("success", __("Equipment item created.")); + } + + /** + * @throws AuthorizationException + */ + public function edit(EquipmentItem $equipmentItem): Response + { + $this->authorize("manageEquipment"); + + $users = User::query() + ->orderByProfileField("last_name") + ->orderByProfileField("first_name") + ->get(); + + return inertia("Equipment/Edit", [ + "equipmentItem" => new EquipmentItemResource($equipmentItem), + "users" => SimpleUserResource::collection($users), + "labels" => EquipmentLabel::query()->pluck("name"), + ]); + } + + /** + * @throws AuthorizationException + */ + public function update( + EquipmentRequest $request, + EquipmentItem $equipmentItem, + ): RedirectResponse { + $this->authorize("manageUsers"); + + $equipmentItem->update($request->data()); + + return redirect() + ->route("equipment-items.index") + ->with("success", __("Equipment item updated.")); + } + + public function destroy(EquipmentItem $equipmentItem): RedirectResponse + { + $this->authorize("manageEquipment"); + + $equipmentItem->delete(); + + return redirect() + ->route("equipment-items.index") + ->with("success", __("Equipment item deleted.")); + } +} diff --git a/app/Infrastructure/Http/Controllers/EquipmentLabelController.php b/app/Infrastructure/Http/Controllers/EquipmentLabelController.php new file mode 100644 index 00000000..1d951501 --- /dev/null +++ b/app/Infrastructure/Http/Controllers/EquipmentLabelController.php @@ -0,0 +1,57 @@ +authorize("manageEquipment"); + + $labels = EquipmentLabel::query() + ->orderBy("name") + ->get(); + + return inertia("EquipmentLabels", [ + "labels" => EquipmentLabelResource::collection($labels), + ]); + } + + /** + * @throws AuthorizationException + */ + public function store(EquipmentLabelRequest $request): RedirectResponse + { + $this->authorize("manageEquipment"); + + $label = EquipmentLabel::query()->create($request->data()); + + return redirect() + ->back() + ->with("success", __("Label :name created.", [ + "name" => $label->name, + ])); + } + + public function destroy(EquipmentLabel $equipmentLabel): RedirectResponse + { + $this->authorize("manageEquipment"); + + $equipmentLabel->delete(); + + return redirect() + ->back() + ->with("success", __("Label :name deleted.", [ + "name" => $equipmentLabel->name, + ])); + } +} diff --git a/app/Infrastructure/Http/Requests/EquipmentLabelRequest.php b/app/Infrastructure/Http/Requests/EquipmentLabelRequest.php new file mode 100644 index 00000000..b1cb4355 --- /dev/null +++ b/app/Infrastructure/Http/Requests/EquipmentLabelRequest.php @@ -0,0 +1,29 @@ + [ + "required", + Rule::unique("equipment_labels", "name")->ignore($this->equipment_label), + "max:255", + ], + ]; + } + + public function data(): array + { + return [ + "name" => $this->get("name"), + ]; + } +} diff --git a/app/Infrastructure/Http/Requests/EquipmentRequest.php b/app/Infrastructure/Http/Requests/EquipmentRequest.php new file mode 100644 index 00000000..c064637f --- /dev/null +++ b/app/Infrastructure/Http/Requests/EquipmentRequest.php @@ -0,0 +1,35 @@ + ["required", "min:3", "max:80", Rule::unique("equipment_items", "id_number")->ignore($this->equipment_item)], + "name" => ["required", "min:3", "max:80"], + "isMobile" => ["required", "boolean"], + "assignee" => ["required_with:assignedAt", "nullable", "exists:users,id"], + "assignedAt" => ["required_with:assignee", "nullable", "date_format:Y-m-d"], + "labels" => ["array", "distinct"], + ]; + } + + public function data(): array + { + return [ + "id_number" => $this->get("idNumber"), + "name" => $this->get("name"), + "is_mobile" => $this->get("isMobile"), + "assignee_id" => $this->get("assignee"), + "assigned_at" => $this->get("assignedAt"), + "labels" => $this->get("labels"), + ]; + } +} diff --git a/app/Infrastructure/Http/Resources/EquipmentItemResource.php b/app/Infrastructure/Http/Resources/EquipmentItemResource.php new file mode 100644 index 00000000..2581aa42 --- /dev/null +++ b/app/Infrastructure/Http/Resources/EquipmentItemResource.php @@ -0,0 +1,26 @@ + $this->id, + "idNumber" => $this->id_number, + "name" => $this->name, + "isMobile" => $this->is_mobile, + "assignee" => new SimpleUserResource($this->assignee), + "labels" => $this->labels, + "assignedAt" => $this->assigned_at?->toDateString(), + "displayAssignedAt" => $this->assigned_at?->toDisplayString(), + ]; + } +} diff --git a/app/Infrastructure/Http/Resources/EquipmentLabelResource.php b/app/Infrastructure/Http/Resources/EquipmentLabelResource.php new file mode 100644 index 00000000..a160b0c9 --- /dev/null +++ b/app/Infrastructure/Http/Resources/EquipmentLabelResource.php @@ -0,0 +1,20 @@ + $this->id, + "name" => $this->name, + ]; + } +} diff --git a/config/permission.php b/config/permission.php index 0420acb2..a3f4faa3 100644 --- a/config/permission.php +++ b/config/permission.php @@ -53,6 +53,7 @@ "receiveVacationRequestWaitsForApprovalNotification", "receiveVacationRequestStatusChangedNotification", "receiveBenefitsReportCreationNotification", + "manageEquipment", ], "permission_roles" => [ Role::Administrator->value => [ @@ -73,6 +74,7 @@ "receiveVacationRequestsSummaryNotification", "receiveVacationRequestWaitsForApprovalNotification", "receiveVacationRequestStatusChangedNotification", + "manageEquipment", ], Role::AdministrativeApprover->value => [ "managePermissions", @@ -94,6 +96,7 @@ "receiveVacationRequestWaitsForApprovalNotification", "receiveVacationRequestStatusChangedNotification", "receiveBenefitsReportCreationNotification", + "manageEquipment", ], Role::TechnicalApprover->value => [ "manageTechnologies", diff --git a/database/factories/EquipmentItemFactory.php b/database/factories/EquipmentItemFactory.php new file mode 100644 index 00000000..c8ed4e1d --- /dev/null +++ b/database/factories/EquipmentItemFactory.php @@ -0,0 +1,30 @@ +faker->boolean; + + return [ + "id_number" => $this->faker->numerify("ABC#########"), + "name" => $this->faker->word, + "assignee_id" => $isAssigned ? User::factory() : null, + "assigned_at" => $isAssigned ? $this->faker->dateTimeBetween("-1 year") : null, + "is_mobile" => $this->faker->boolean, + "labels" => [ + $this->faker->word, $this->faker->word, $this->faker->word, + ], + ]; + } +} diff --git a/database/factories/EquipmentLabelFactory.php b/database/factories/EquipmentLabelFactory.php new file mode 100644 index 00000000..e90aa8ec --- /dev/null +++ b/database/factories/EquipmentLabelFactory.php @@ -0,0 +1,20 @@ + $this->faker->word, + ]; + } +} diff --git a/database/migrations/2023_09_26_140056_create_equipment_items_table.php b/database/migrations/2023_09_26_140056_create_equipment_items_table.php new file mode 100644 index 00000000..74119295 --- /dev/null +++ b/database/migrations/2023_09_26_140056_create_equipment_items_table.php @@ -0,0 +1,29 @@ +id(); + $table->string("id_number")->unique(); + $table->string("name"); + $table->boolean("is_mobile")->default(false); + $table->foreignIdFor(User::class, "assignee_id")->nullable()->constrained("users")->cascadeOnDelete(); + $table->date("assigned_at")->nullable(); + $table->json("labels")->nullable(); + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists("equipment_items"); + } +}; diff --git a/database/migrations/2023_09_26_140126_create_equipment_labels_table.php b/database/migrations/2023_09_26_140126_create_equipment_labels_table.php new file mode 100644 index 00000000..2302ddda --- /dev/null +++ b/database/migrations/2023_09_26_140126_create_equipment_labels_table.php @@ -0,0 +1,23 @@ +id(); + $table->string("name"); + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists("equipment_labels"); + } +}; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index e59c21b8..f182e303 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -10,6 +10,8 @@ use Toby\Domain\WorkDaysCalculator; use Toby\Eloquent\Models\Benefit; use Toby\Eloquent\Models\BenefitsReport; +use Toby\Eloquent\Models\EquipmentItem; +use Toby\Eloquent\Models\EquipmentLabel; use Toby\Eloquent\Models\Key; use Toby\Eloquent\Models\User; use Toby\Eloquent\Models\VacationLimit; @@ -106,5 +108,9 @@ public function run(): void ]); BenefitsReport::factory(3)->create(); + + EquipmentLabel::factory(10)->create(); + + EquipmentItem::factory(40)->create(); } } diff --git a/database/seeders/DemoSeeder.php b/database/seeders/DemoSeeder.php index d436c66e..fa8fa849 100644 --- a/database/seeders/DemoSeeder.php +++ b/database/seeders/DemoSeeder.php @@ -21,6 +21,8 @@ use Toby\Domain\WorkDaysCalculator; use Toby\Eloquent\Models\Benefit; use Toby\Eloquent\Models\BenefitsReport; +use Toby\Eloquent\Models\EquipmentItem; +use Toby\Eloquent\Models\EquipmentLabel; use Toby\Eloquent\Models\Key; use Toby\Eloquent\Models\Resume; use Toby\Eloquent\Models\Technology; @@ -395,5 +397,75 @@ public function run(): void "data" => null, "committed_at" => null, ]); + + EquipmentLabel::factory()->createMany([ + ["name" => "Komputery"], + ["name" => "Telefony"], + ["name" => "Urządzenia peryferyjne"], + ["name" => "Monitory"], + ["name" => "Telewizory"], + ["name" => "Wyposażenie biura"], + ["name" => "Inne"], + ]); + + User::query()->each(function (User $user): void { + /** @var EquipmentItem $computer */ + $computer = EquipmentItem::factory([ + "name" => "Laptop Dell Latitude 7400", + "assigned_at" => fake()->dateTimeBetween("-1 year"), + "is_mobile" => true, + "labels" => [ + "Komputery", + ], + ])->for($user, "assignee")->create(); + + EquipmentItem::factory([ + "name" => "Monitor Philips 2" . fake()->numberBetween(1, 8) . '"', + "assigned_at" => $computer->assigned_at, + "is_mobile" => false, + "labels" => [ + "Monitory", + "Urządzenia peryferyjne", + ], + ])->for($user, "assignee")->create(); + + EquipmentItem::factory([ + "name" => "Myszka Logitech MX" . fake()->numerify(), + "assigned_at" => $computer->assigned_at, + "is_mobile" => true, + "labels" => [ + "Urządzenia peryferyjne", + ], + ])->for($user, "assignee")->create(); + + EquipmentItem::factory([ + "name" => "Klawiatura Dell " . fake()->numerify("#####"), + "assigned_at" => $computer->assigned_at, + "is_mobile" => true, + "labels" => [ + "Urządzenia peryferyjne", + ], + ])->for($user, "assignee")->create(); + + EquipmentItem::factory([ + "name" => "Hub USB", + "assigned_at" => $computer->assigned_at, + "is_mobile" => true, + "labels" => [ + "Urządzenia peryferyjne", + ], + ])->for($user, "assignee")->create(); + }); + + EquipmentItem::factory([ + "name" => 'Telewizor TCN 55" 4K', + "is_mobile" => false, + "assigned_at" => null, + "assignee_id" => null, + "labels" => [ + "Telewizory", + "Wyposażenie biura", + ], + ])->create(); } } diff --git a/lang/pl.json b/lang/pl.json index 8f418658..48672e04 100644 --- a/lang/pl.json +++ b/lang/pl.json @@ -154,5 +154,10 @@ ":white_check_mark: Key no. :number has been taken from the office": ":white_check_mark: Klucz nr :number został zabrany z biura", "Leave the keys in the office.": "Zostaw klucze w biurze.", "You don't have any key to leave in the office.": "Nie masz żadnych kluczy do pozostawienia w biurze.", - ":white_check_mark: Key no. :number has been left in the office": ":white_check_mark: Klucz nr :number został pozostawiony w biurze" + ":white_check_mark: Key no. :number has been left in the office": ":white_check_mark: Klucz nr :number został pozostawiony w biurze", + "Equipment item created.": "Sprzęt utworzony.", + "Equipment item updated.": "Sprzęt zaktualizowany.", + "Equipment item deleted.": "Sprzęt usunięty.", + "Label :name created.": "Etykieta :name utworzona.", + "Label :name deleted.": "Etykieta :name usunięta." } diff --git a/lang/pl/validation.php b/lang/pl/validation.php index e52bfc17..afb575da 100644 --- a/lang/pl/validation.php +++ b/lang/pl/validation.php @@ -158,8 +158,18 @@ "projects.*.tasks" => [ "required" => "Zadania w projekcie są wymagane.", ], + "idNumber" => [ + "required" => "ID jest wymagane.", + ], + "assignee" => [ + "required_with" => "Przydzielona osoba jest wymagana.", + ], + "assignedAt" => [ + "required_with" => "Data przydzielenia jest wymagana.", + ], "name" => [ "unique" => "Taka nazwa już występuje.", + "required" => "Nazwa jest wymagana.", ], "items.*.days" => [ "max" => "Limit dni urlopu nie może być większy niż :max.", @@ -182,5 +192,9 @@ "date" => "data", "name" => "nazwa", "password" => "hasło", + "idNumber" => "ID", + "isMobile" => "mobilny", + "assignee" => "przydzielona osoba", + "assignedAt" => "data przydzielenia", ], ]; diff --git a/resources/js/Pages/Equipment/Create.vue b/resources/js/Pages/Equipment/Create.vue new file mode 100644 index 00000000..587fcc07 --- /dev/null +++ b/resources/js/Pages/Equipment/Create.vue @@ -0,0 +1,272 @@ + + + diff --git a/resources/js/Pages/Equipment/Edit.vue b/resources/js/Pages/Equipment/Edit.vue new file mode 100644 index 00000000..5bb761c2 --- /dev/null +++ b/resources/js/Pages/Equipment/Edit.vue @@ -0,0 +1,273 @@ + + + diff --git a/resources/js/Pages/Equipment/Index.vue b/resources/js/Pages/Equipment/Index.vue new file mode 100644 index 00000000..9aa82b99 --- /dev/null +++ b/resources/js/Pages/Equipment/Index.vue @@ -0,0 +1,275 @@ + + + diff --git a/resources/js/Pages/EquipmentLabels.vue b/resources/js/Pages/EquipmentLabels.vue new file mode 100644 index 00000000..07d5f0fe --- /dev/null +++ b/resources/js/Pages/EquipmentLabels.vue @@ -0,0 +1,231 @@ + + + diff --git a/resources/js/Pages/Resumes/Create.vue b/resources/js/Pages/Resumes/Create.vue index 6807d713..e99ac1cc 100644 --- a/resources/js/Pages/Resumes/Create.vue +++ b/resources/js/Pages/Resumes/Create.vue @@ -531,6 +531,7 @@ function submitResume() {

- {{ form.errors.type }} + {{ form.errors.user }}

diff --git a/resources/js/Shared/Forms/MultipleCombobox.vue b/resources/js/Shared/Forms/MultipleCombobox.vue index f683dce1..fead3ae7 100644 --- a/resources/js/Shared/Forms/MultipleCombobox.vue +++ b/resources/js/Shared/Forms/MultipleCombobox.vue @@ -13,6 +13,8 @@ const props = defineProps({ items: Array, modelValue: Array, id: String, + placeholder: String, + showChips: Boolean, }) const emit = defineEmits(['update:modelValue']) @@ -41,7 +43,10 @@ const filteredItems = computed(() => nullable multiple > -
+
-
+
diff --git a/resources/js/Shared/MainMenu.vue b/resources/js/Shared/MainMenu.vue index 08858483..c08565a2 100644 --- a/resources/js/Shared/MainMenu.vue +++ b/resources/js/Shared/MainMenu.vue @@ -27,6 +27,7 @@ import { BeakerIcon, GiftIcon, BanknotesIcon, + ComputerDesktopIcon, } from '@heroicons/vue/24/outline' import { CheckIcon, ChevronDownIcon } from '@heroicons/vue/24/solid' @@ -135,6 +136,13 @@ const miscNavigation = computed(() => [ icon: BanknotesIcon, can: props.auth.can.manageBenefits, }, + { + name: 'Sprzęt', + href: '/equipment-items', + section: 'Equipment', + icon: ComputerDesktopIcon, + can: props.auth.can.manageEquipment, + }, ].filter(item => item.can)) diff --git a/routes/web.php b/routes/web.php index 61f33c18..4c5250c4 100644 --- a/routes/web.php +++ b/routes/web.php @@ -8,6 +8,8 @@ use Toby\Infrastructure\Http\Controllers\BenefitController; use Toby\Infrastructure\Http\Controllers\BenefitsReportController; use Toby\Infrastructure\Http\Controllers\DashboardController; +use Toby\Infrastructure\Http\Controllers\EquipmentController; +use Toby\Infrastructure\Http\Controllers\EquipmentLabelController; use Toby\Infrastructure\Http\Controllers\GoogleController; use Toby\Infrastructure\Http\Controllers\HolidayController; use Toby\Infrastructure\Http\Controllers\KeysController; @@ -39,6 +41,14 @@ ->whereNumber("user") ->withTrashed(); + Route::resource("equipment-items", EquipmentController::class) + ->except("show") + ->whereNumber("equipmentItem"); + + Route::resource("equipment-labels", EquipmentLabelController::class) + ->only(["index", "store", "destroy"]) + ->whereNumber("equipmentLabels"); + Route::get("/users/{user}/permissions", [PermissionController::class, "show"]) ->whereNumber("user"); Route::patch("/users/{user}/permissions", [PermissionController::class, "update"]) diff --git a/tests/Feature/PermissionTest.php b/tests/Feature/PermissionTest.php index 9d152a3c..acaf741e 100644 --- a/tests/Feature/PermissionTest.php +++ b/tests/Feature/PermissionTest.php @@ -45,7 +45,8 @@ public function testAdminCanSeeEditEmployeePermissionsForm(): void ->where("receiveBenefitsReportCreationNotification", false) ->where("receiveVacationRequestsSummaryNotification", false) ->where("receiveVacationRequestWaitsForApprovalNotification", false) - ->where("receiveVacationRequestStatusChangedNotification", false), + ->where("receiveVacationRequestStatusChangedNotification", false) + ->where("manageEquipment", false), ), ); } @@ -82,7 +83,8 @@ public function testAdminCanSeeEditTechnicalApproverPermissionsForm(): void ->where("receiveBenefitsReportCreationNotification", false) ->where("receiveVacationRequestsSummaryNotification", true) ->where("receiveVacationRequestWaitsForApprovalNotification", true) - ->where("receiveVacationRequestStatusChangedNotification", true), + ->where("receiveVacationRequestStatusChangedNotification", true) + ->where("manageEquipment", false), ), ); } @@ -119,7 +121,8 @@ public function testAdminCanSeeEditAdministrativeApproverPermissionsForm(): void ->where("receiveBenefitsReportCreationNotification", true) ->where("receiveVacationRequestsSummaryNotification", true) ->where("receiveVacationRequestWaitsForApprovalNotification", true) - ->where("receiveVacationRequestStatusChangedNotification", true), + ->where("receiveVacationRequestStatusChangedNotification", true) + ->where("manageEquipment", true), ), ); } From 07896926c3f7b45e8aceaec8bba75c09d5a37232 Mon Sep 17 00:00:00 2001 From: Mateusz Lencki Date: Wed, 4 Oct 2023 13:00:11 +0200 Subject: [PATCH 02/14] Exporting equipment to excel file --- app/Domain/EquipmentExport.php | 103 ++++++++++++++++++ .../Http/Controllers/EquipmentController.php | 17 +++ lang/pl.json | 8 +- resources/js/Composables/permissionInfo.js | 5 + resources/js/Pages/Equipment/Index.vue | 6 + routes/web.php | 2 + 6 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 app/Domain/EquipmentExport.php diff --git a/app/Domain/EquipmentExport.php b/app/Domain/EquipmentExport.php new file mode 100644 index 00000000..01966703 --- /dev/null +++ b/app/Domain/EquipmentExport.php @@ -0,0 +1,103 @@ +equipmentItems as $equipmentItem) { + $row = [ + $equipmentItem->id_number, + $equipmentItem->name, + $equipmentItem->labels?->implode(", "), + $equipmentItem->is_mobile, + $equipmentItem->assignee->profile->full_name ?? "", + $equipmentItem->assigned_at ? Date::dateTimeToExcel($equipmentItem->assigned_at) : "", + ]; + + yield $row; + } + } + + public function headings(): array + { + return [ + __("ID"), + __("Name"), + __("Labels"), + __("Is mobile"), + __("Assignee"), + __("Assigned at"), + ]; + } + + public function columnFormats(): array + { + return [ + "A" => NumberFormat::FORMAT_TEXT, + "B" => NumberFormat::FORMAT_TEXT, + "C" => NumberFormat::FORMAT_TEXT, + "D" => DataType::TYPE_BOOL, + "E" => NumberFormat::FORMAT_TEXT, + "F" => NumberFormat::FORMAT_DATE_DDMMYYYY, + ]; + } + + public function styles(Worksheet $sheet): void + { + $lastRow = $sheet->getHighestRow(); + $lastColumn = $sheet->getHighestColumn(); + + $sheet->getStyle("A1:{$lastColumn}1") + ->getFont() + ->setBold(true); + + $sheet->getStyle("A1:{$lastColumn}1") + ->getAlignment() + ->setVertical(Alignment::VERTICAL_CENTER); + + $sheet->getStyle("A1:{$lastColumn}1") + ->getFill() + ->setFillType(Fill::FILL_SOLID) + ->getStartColor() + ->setRGB("D9D9D9"); + + $sheet->getStyle("A2:A{$lastRow}") + ->getFont() + ->setBold(true); + + $sheet->getStyle("A1:{$lastColumn}{$lastRow}") + ->getBorders() + ->getAllBorders() + ->setBorderStyle(Border::BORDER_THIN) + ->getColor() + ->setRGB("B7B7B7"); + } +} diff --git a/app/Infrastructure/Http/Controllers/EquipmentController.php b/app/Infrastructure/Http/Controllers/EquipmentController.php index 35c7536c..aa4d3b39 100644 --- a/app/Infrastructure/Http/Controllers/EquipmentController.php +++ b/app/Infrastructure/Http/Controllers/EquipmentController.php @@ -4,10 +4,14 @@ namespace Toby\Infrastructure\Http\Controllers; +use Carbon\Carbon; use Illuminate\Auth\Access\AuthorizationException; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Inertia\Response; +use Maatwebsite\Excel\Facades\Excel; +use Symfony\Component\HttpFoundation\BinaryFileResponse; +use Toby\Domain\EquipmentExport; use Toby\Eloquent\Models\EquipmentItem; use Toby\Eloquent\Models\EquipmentLabel; use Toby\Eloquent\Models\User; @@ -119,4 +123,17 @@ public function destroy(EquipmentItem $equipmentItem): RedirectResponse ->route("equipment-items.index") ->with("success", __("Equipment item deleted.")); } + + public function downloadExcel(): BinaryFileResponse + { + $this->authorize("manageEquipment"); + + $equipmentItems = EquipmentItem::query()->get(); + + $equipmentExport = new EquipmentExport($equipmentItems); + + $name = __("Equipment") . " " . Carbon::now()->translatedFormat("d F Y") . ".xlsx"; + + return Excel::download($equipmentExport, $name); + } } diff --git a/lang/pl.json b/lang/pl.json index 48672e04..3d827ca7 100644 --- a/lang/pl.json +++ b/lang/pl.json @@ -159,5 +159,11 @@ "Equipment item updated.": "Sprzęt zaktualizowany.", "Equipment item deleted.": "Sprzęt usunięty.", "Label :name created.": "Etykieta :name utworzona.", - "Label :name deleted.": "Etykieta :name usunięta." + "Label :name deleted.": "Etykieta :name usunięta.", + "Equipment": "Sprzęt", + "Name": "Nazwa", + "Labels": "Etykiety", + "Is mobile": "Czy mobilny", + "Assignee": "Przypisany do", + "Assigned at": "Przypisany od" } diff --git a/resources/js/Composables/permissionInfo.js b/resources/js/Composables/permissionInfo.js index a02c0921..ca0f1a21 100644 --- a/resources/js/Composables/permissionInfo.js +++ b/resources/js/Composables/permissionInfo.js @@ -19,6 +19,11 @@ const permissionsInfo = [ 'value': 'manageKeys', 'section': 'Biuro', }, + { + 'name': 'Zarządzanie sprzętem', + 'value': 'manageEquipment', + 'section': 'Biuro', + }, { 'name': 'Zarządzanie technologiami', 'value': 'manageTechnologies', diff --git a/resources/js/Pages/Equipment/Index.vue b/resources/js/Pages/Equipment/Index.vue index 9aa82b99..2ea48bac 100644 --- a/resources/js/Pages/Equipment/Index.vue +++ b/resources/js/Pages/Equipment/Index.vue @@ -47,6 +47,12 @@ watch(form, debounce(() => {
+ + Pobierz plik Excel + except("show") ->whereNumber("equipmentItem"); + Route::get("/equipment-items/download", [EquipmentController::class, "downloadExcel"]) + ->name("equipment-items.download"); Route::resource("equipment-labels", EquipmentLabelController::class) ->only(["index", "store", "destroy"]) From b1db75b65ffb0c81412a3ec2ad05b65e1f9aabe2 Mon Sep 17 00:00:00 2001 From: Mateusz Lencki Date: Thu, 5 Oct 2023 14:23:43 +0200 Subject: [PATCH 03/14] Added equipment tests --- lang/pl.json | 3 +- tests/Feature/EquipmentTest.php | 271 ++++++++++++++++++++++++++++++++ 2 files changed, 272 insertions(+), 2 deletions(-) create mode 100644 tests/Feature/EquipmentTest.php diff --git a/lang/pl.json b/lang/pl.json index 72013082..d8165892 100644 --- a/lang/pl.json +++ b/lang/pl.json @@ -168,8 +168,7 @@ "waiting_for_administrative": "czeka na akceptację od przełożonego administracyjnego", "You do not have permission to perform this action.": "Nie masz uprawnień, aby wykonać tę akcję.", "waiting_for_technical": "czeka na akceptację od przełożonego technicznego", - "You cannot perform this action because the current status of the request :title by user :requester is :status.": "Nie możesz wykonać tej akcji, ponieważ aktualny status wniosku :title użytkownika :requester to :status" - ":white_check_mark: Key no. :number has been left in the office": ":white_check_mark: Klucz nr :number został pozostawiony w biurze", + "You cannot perform this action because the current status of the request :title by user :requester is :status.": "Nie możesz wykonać tej akcji, ponieważ aktualny status wniosku :title użytkownika :requester to :status", "Equipment item created.": "Sprzęt utworzony.", "Equipment item updated.": "Sprzęt zaktualizowany.", "Equipment item deleted.": "Sprzęt usunięty.", diff --git a/tests/Feature/EquipmentTest.php b/tests/Feature/EquipmentTest.php new file mode 100644 index 00000000..10ebe85c --- /dev/null +++ b/tests/Feature/EquipmentTest.php @@ -0,0 +1,271 @@ +count(10)->create(); + $admin = User::factory()->admin()->create(); + + $this->assertDatabaseCount("equipment_items", 10); + + $this->actingAs($admin) + ->get("/equipment-items") + ->assertOk() + ->assertInertia( + fn(Assert $page) => $page + ->component("Equipment/Index") + ->has("equipmentItems.data", 10), + ); + } + + public function testEmployeeCannotSeeEquipmentList(): void + { + $employee = User::factory()->employee()->create(); + + $this->actingAs($employee) + ->get("/equipment-items") + ->assertForbidden(); + } + + public function testAnyUserWithProperPermissionCanSeeEquipmentList(): void + { + $user = User::factory()->create(); + $user->givePermissionTo("manageEquipment"); + + $this->actingAs($user) + ->get("/equipment-items") + ->assertOk() + ->assertInertia( + fn(Assert $page) => $page->component("Equipment/Index"), + ); + } + + public function testAdminCanSearchEquipmentList(): void + { + $admin = User::factory()->admin()->create(); + + EquipmentItem::factory() + ->count(4) + ->sequence( + [ + "name" => "Test1", + ], + [ + "name" => "Test2", + ], + [ + "name" => "Test3", + ], + [ + "name" => "Item1", + ], + ) + ->create(); + + $this->assertDatabaseCount("equipment_items", 4); + + $this->actingAs($admin) + ->get("/equipment-items?search=test") + ->assertOk() + ->assertInertia( + fn(Assert $page) => $page + ->component("Equipment/Index") + ->has("equipmentItems.data", 3), + ); + } + + public function testAdminCanFilterEquipmentListWithLabels(): void + { + $admin = User::factory()->admin()->create(); + + EquipmentItem::factory() + ->count(4) + ->sequence( + [ + "labels" => [ + "Test1", + "Test2", + ], + ], + [ + "labels" => [ + "Test1", + ], + ], + [ + "labels" => [ + "Test2", + ], + ], + [ + "labels" => [ + "Test3", + ], + ], + ) + ->create(); + + $this->assertDatabaseCount("equipment_items", 4); + + $this->actingAs($admin) + ->get("/equipment-items?labels[]=Test1&labels[]=Test2") + ->assertOk() + ->assertInertia( + fn(Assert $page) => $page + ->component("Equipment/Index") + ->has("equipmentItems.data", 3), + ); + } + + public function testEquipmentListIsPaginated(): void + { + EquipmentItem::factory()->count(16)->create(); + $admin = User::factory()->admin()->create(); + + $this->assertDatabaseCount("equipment_items", 16); + + $this->actingAs($admin) + ->get("/equipment-items?page=2") + ->assertInertia( + fn(Assert $page) => $page + ->component("Equipment/Index") + ->has("equipmentItems.data", 1), + ); + } + + public function testAdminCanCreateEquipmentItem(): void + { + $admin = User::factory()->admin()->create(); + $assignee = User::factory()->create(); + + $this->actingAs($admin) + ->post("/equipment-items", [ + "idNumber" => "123", + "name" => "Test", + "isMobile" => true, + "assignee" => $assignee->id, + "assignedAt" => "2023-01-01", + "labels" => ["Test1", "Test2"], + ]) + ->assertSessionHasNoErrors(); + + $equipmentItem = EquipmentItem::query()->where("id_number", "123")->first(); + + $this->assertDatabaseHas("equipment_items", [ + "id_number" => "123", + "name" => "Test", + "is_mobile" => true, + "assignee_id" => $assignee->id, + "assigned_at" => "2023-01-01", + ]); + + $this->assertEquals(["Test1", "Test2"], $equipmentItem->labels->toArray()); + } + + public function testAdminCanEditEquipmentItem(): void + { + $admin = User::factory()->admin()->create(); + + $equipmentItem = EquipmentItem::factory()->create(); + + $this->assertDatabaseHas("equipment_items", [ + "id" => $equipmentItem->id, + "id_number" => $equipmentItem->id_number, + "name" => $equipmentItem->name, + "is_mobile" => $equipmentItem->is_mobile, + "assignee_id" => $equipmentItem->assignee_id, + "assigned_at" => $equipmentItem->assigned_at, + ]); + + $this->actingAs($admin) + ->put("/equipment-items/{$equipmentItem->id}", [ + "idNumber" => "123", + "name" => "Test", + "isMobile" => true, + "assignee" => null, + "assignedAt" => null, + ]) + ->assertSessionHasNoErrors(); + + $this->assertDatabaseHas("equipment_items", [ + "id" => $equipmentItem->id, + "id_number" => "123", + "name" => "Test", + "is_mobile" => true, + "assignee_id" => null, + "assigned_at" => null, + ]); + } + + public function testAdminCanDeleteEquipmentItem(): void + { + $admin = User::factory()->admin()->create(); + + $equipmentItem = EquipmentItem::factory()->create(); + + $this->actingAs($admin) + ->delete("/equipment-items/{$equipmentItem->id}") + ->assertSessionHasNoErrors(); + + $this->assertModelMissing($equipmentItem); + } + + public function testAdminCanSeeEquipmentLabelList(): void + { + EquipmentLabel::factory()->count(10)->create(); + $admin = User::factory()->admin()->create(); + + $this->assertDatabaseCount("equipment_labels", 10); + + $this->actingAs($admin) + ->get("/equipment-labels") + ->assertOk() + ->assertInertia( + fn(Assert $page) => $page + ->component("EquipmentLabels") + ->has("labels.data", 10), + ); + } + + public function testAdminCanCreateEquipmentLabel(): void + { + $admin = User::factory()->admin()->create(); + + $this->actingAs($admin) + ->post("/equipment-labels", [ + "name" => "Test", + ]) + ->assertSessionHasNoErrors(); + + $this->assertDatabaseHas("equipment_labels", [ + "name" => "Test", + ]); + } + + public function testAdminCanDeleteEquipmentLabel(): void + { + $admin = User::factory()->admin()->create(); + + $equipmentLabel = EquipmentLabel::factory()->create(); + + $this->actingAs($admin) + ->delete("/equipment-labels/{$equipmentLabel->id}") + ->assertSessionHasNoErrors(); + + $this->assertModelMissing($equipmentLabel); + } +} From fb59493120e4505c06ae16d71f5603ba821650b0 Mon Sep 17 00:00:00 2001 From: EwelinaSkrzypacz Date: Mon, 9 Oct 2023 08:03:27 +0200 Subject: [PATCH 04/14] #178 - change text in is mobile column to icons --- resources/js/Pages/Equipment/Index.vue | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/resources/js/Pages/Equipment/Index.vue b/resources/js/Pages/Equipment/Index.vue index 2ea48bac..bf723085 100644 --- a/resources/js/Pages/Equipment/Index.vue +++ b/resources/js/Pages/Equipment/Index.vue @@ -12,7 +12,7 @@ import { reactive, watch } from 'vue' import { debounce } from 'lodash' import { Inertia } from '@inertiajs/inertia' import MultipleCombobox from '@/Shared/Forms/MultipleCombobox.vue' -import { EllipsisVerticalIcon, PencilIcon, TrashIcon } from '@heroicons/vue/24/solid' +import { EllipsisVerticalIcon, PencilIcon, TrashIcon, CheckIcon, XMarkIcon } from '@heroicons/vue/24/solid' const props = defineProps({ auth: Object, @@ -157,15 +157,12 @@ watch(form, debounce(() => { - - - - +
+ +
+
+ +
Date: Mon, 9 Oct 2023 08:21:30 +0200 Subject: [PATCH 05/14] #178 - linter fix --- resources/js/Pages/Equipment/Index.vue | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/js/Pages/Equipment/Index.vue b/resources/js/Pages/Equipment/Index.vue index bf723085..b9219a28 100644 --- a/resources/js/Pages/Equipment/Index.vue +++ b/resources/js/Pages/Equipment/Index.vue @@ -2,9 +2,9 @@ import EmptyState from '@/Shared/Feedbacks/EmptyState.vue' import { Menu, - MenuButton, MenuItem, + MenuButton, + MenuItem, MenuItems, - Switch, } from '@headlessui/vue' import Pagination from '@/Shared/Pagination.vue' import { MagnifyingGlassIcon } from '@heroicons/vue/24/outline' @@ -158,10 +158,10 @@ watch(form, debounce(() => {
- +
- +
From 45ddfcfd730c7738f52896c9cb3d5da3b4cf41b5 Mon Sep 17 00:00:00 2001 From: Mateusz Lencki Date: Tue, 10 Oct 2023 15:18:29 +0200 Subject: [PATCH 06/14] CR fixes --- app/Eloquent/Models/EquipmentItem.php | 10 +- .../Http/Controllers/EquipmentController.php | 40 +++- resources/js/Pages/Equipment/Index.vue | 112 ++++++++++- .../js/Pages/Equipment/IndexForEmployee.vue | 176 ++++++++++++++++++ .../js/Shared/Forms/MultipleCombobox.vue | 11 +- resources/js/Shared/MainMenu.vue | 7 + routes/web.php | 2 + tests/Feature/EquipmentTest.php | 18 ++ 8 files changed, 367 insertions(+), 9 deletions(-) create mode 100644 resources/js/Pages/Equipment/IndexForEmployee.vue diff --git a/app/Eloquent/Models/EquipmentItem.php b/app/Eloquent/Models/EquipmentItem.php index 446c16b4..62abf9a8 100644 --- a/app/Eloquent/Models/EquipmentItem.php +++ b/app/Eloquent/Models/EquipmentItem.php @@ -71,9 +71,13 @@ public function scopeLabels(Builder $query, ?array $labels): Builder return $query; } - foreach ($labels as $label) { - $query->orWhereJsonContains("labels", $label); - } + $query->where(function (Builder $query) use ($labels): Builder { + foreach ($labels as $label) { + $query->orWhereJsonContains("labels", $label); + } + + return $query; + }); return $query; } diff --git a/app/Infrastructure/Http/Controllers/EquipmentController.php b/app/Infrastructure/Http/Controllers/EquipmentController.php index aa4d3b39..c3d0bae0 100644 --- a/app/Infrastructure/Http/Controllers/EquipmentController.php +++ b/app/Infrastructure/Http/Controllers/EquipmentController.php @@ -24,7 +24,7 @@ class EquipmentController extends Controller /** * @throws AuthorizationException */ - public function index(Request $request): Response + public function index(Request $request): RedirectResponse|Response { $this->authorize("manageEquipment"); @@ -32,15 +32,53 @@ public function index(Request $request): Response $equipmentItems = EquipmentItem::query() ->search($searchQuery) + ->when( + $request->has("assignee"), + fn($query) => $query->where("assignee_id", $request->query("assignee")), + ) ->labels($request->query("labels")) + ->orderBy("id_number") ->paginate() ->withQueryString(); + $users = User::query() + ->orderByProfileField("last_name") + ->orderByProfileField("first_name") + ->get(); + return inertia("Equipment/Index", [ + "equipmentItems" => EquipmentItemResource::collection($equipmentItems), + "labels" => EquipmentLabel::query()->pluck("name"), + "users" => SimpleUserResource::collection($users), + "filters" => [ + "search" => $searchQuery, + "labels" => $request->query("labels"), + "assignee" => (int)$request->query("assignee"), + ], + ]); + } + + /** + * @throws AuthorizationException + */ + public function indexForEmployee(Request $request): RedirectResponse|Response + { + $searchQuery = $request->query("search"); + + $equipmentItems = EquipmentItem::query() + ->search($searchQuery) + ->labels($request->query("labels")) + ->where("assignee_id", $request->user()->id) + ->orderBy("id_number") + ->paginate() + ->withQueryString(); + + return inertia("Equipment/IndexForEmployee", [ "equipmentItems" => EquipmentItemResource::collection($equipmentItems), "labels" => EquipmentLabel::query()->pluck("name"), "filters" => [ "search" => $searchQuery, + "labels" => $request->query("labels"), ], ]); } diff --git a/resources/js/Pages/Equipment/Index.vue b/resources/js/Pages/Equipment/Index.vue index b9219a28..df5375fa 100644 --- a/resources/js/Pages/Equipment/Index.vue +++ b/resources/js/Pages/Equipment/Index.vue @@ -1,6 +1,10 @@ + + diff --git a/resources/js/Shared/Forms/MultipleCombobox.vue b/resources/js/Shared/Forms/MultipleCombobox.vue index fead3ae7..d69c71a7 100644 --- a/resources/js/Shared/Forms/MultipleCombobox.vue +++ b/resources/js/Shared/Forms/MultipleCombobox.vue @@ -85,7 +85,6 @@ const filteredItems = computed(() => + +
  • + + Brak wyników wyszukiwania + +
  • +
    diff --git a/resources/js/Shared/MainMenu.vue b/resources/js/Shared/MainMenu.vue index 84f65537..3e081c99 100644 --- a/resources/js/Shared/MainMenu.vue +++ b/resources/js/Shared/MainMenu.vue @@ -151,6 +151,13 @@ const miscNavigation = computed(() => [ icon: ComputerDesktopIcon, can: props.auth.can.manageEquipment, }, + { + name: 'Mój sprzęt', + href: '/equipment-items/me', + section: 'Equipment', + icon: ComputerDesktopIcon, + can: !props.auth.can.manageEquipment, + }, ].filter(item => item.can)) diff --git a/routes/web.php b/routes/web.php index 3a9c11da..09f3479d 100644 --- a/routes/web.php +++ b/routes/web.php @@ -44,6 +44,8 @@ Route::resource("equipment-items", EquipmentController::class) ->except("show") ->whereNumber("equipmentItem"); + Route::get("/equipment-items/me", [EquipmentController::class, "indexForEmployee"]) + ->name("equipment-items.indexForEmployee"); Route::get("/equipment-items/download", [EquipmentController::class, "downloadExcel"]) ->name("equipment-items.download"); diff --git a/tests/Feature/EquipmentTest.php b/tests/Feature/EquipmentTest.php index 10ebe85c..a26707d5 100644 --- a/tests/Feature/EquipmentTest.php +++ b/tests/Feature/EquipmentTest.php @@ -268,4 +268,22 @@ public function testAdminCanDeleteEquipmentLabel(): void $this->assertModelMissing($equipmentLabel); } + + public function testEmployeeCanOwnEquipment(): void + { + $employee = User::factory()->employee()->create(); + EquipmentItem::factory() + ->count(10) + ->for($employee, "assignee") + ->create(); + + $this->actingAs($employee) + ->get("/equipment-items/me") + ->assertOk() + ->assertInertia( + fn(Assert $page) => $page + ->component("Equipment/IndexForEmployee") + ->has("equipmentItems.data", 10), + ); + } } From bf4bd9cc95cef3e391d629f0ff72bfd5a7725f61 Mon Sep 17 00:00:00 2001 From: Mateusz Lencki Date: Tue, 10 Oct 2023 15:22:16 +0200 Subject: [PATCH 07/14] Linter fixes --- resources/js/Pages/Equipment/Index.vue | 2 +- resources/js/Pages/Equipment/IndexForEmployee.vue | 15 +-------------- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/resources/js/Pages/Equipment/Index.vue b/resources/js/Pages/Equipment/Index.vue index df5375fa..8ad4214b 100644 --- a/resources/js/Pages/Equipment/Index.vue +++ b/resources/js/Pages/Equipment/Index.vue @@ -2,7 +2,7 @@ import EmptyState from '@/Shared/Feedbacks/EmptyState.vue' import { Listbox, - ListboxButton, ListboxLabel, + ListboxButton, ListboxOption, ListboxOptions, Menu, diff --git a/resources/js/Pages/Equipment/IndexForEmployee.vue b/resources/js/Pages/Equipment/IndexForEmployee.vue index 216d034a..af21aa1e 100644 --- a/resources/js/Pages/Equipment/IndexForEmployee.vue +++ b/resources/js/Pages/Equipment/IndexForEmployee.vue @@ -1,15 +1,6 @@