From e3998238efd5fc93c85945a0eacade5e90f73aaf Mon Sep 17 00:00:00 2001 From: Adrian Hopek Date: Thu, 14 Apr 2022 14:29:36 +0200 Subject: [PATCH] #118 - keys --- .../Providers/AuthServiceProvider.php | 3 + app/Domain/Policies/KeyPolicy.php | 26 ++ app/Eloquent/Models/Key.php | 12 +- app/Eloquent/Models/User.php | 5 + .../Http/Controllers/KeysController.php | 71 +++- .../Http/Requests/GiveKeyRequest.php | 23 ++ .../Http/Resources/KeyResource.php | 7 +- database/factories/KeyFactory.php | 3 +- .../2022_04_12_152528_create_keys_table.php | 5 +- database/seeders/DemoSeeder.php | 7 + resources/js/Pages/Keys.vue | 349 ++++++++++++++++++ resources/js/Pages/Keys/Index.vue | 162 -------- routes/web.php | 9 +- 13 files changed, 499 insertions(+), 183 deletions(-) create mode 100644 app/Domain/Policies/KeyPolicy.php create mode 100644 app/Infrastructure/Http/Requests/GiveKeyRequest.php create mode 100644 resources/js/Pages/Keys.vue delete mode 100644 resources/js/Pages/Keys/Index.vue diff --git a/app/Architecture/Providers/AuthServiceProvider.php b/app/Architecture/Providers/AuthServiceProvider.php index afa2b3b..cb5b112 100644 --- a/app/Architecture/Providers/AuthServiceProvider.php +++ b/app/Architecture/Providers/AuthServiceProvider.php @@ -7,7 +7,9 @@ namespace Toby\Architecture\Providers; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; use Illuminate\Support\Facades\Gate; use Toby\Domain\Enums\Role; +use Toby\Domain\Policies\KeyPolicy; use Toby\Domain\Policies\VacationRequestPolicy; +use Toby\Eloquent\Models\Key; use Toby\Eloquent\Models\User; use Toby\Eloquent\Models\VacationRequest; @@ -15,6 +17,7 @@ class AuthServiceProvider extends ServiceProvider { protected $policies = [ VacationRequest::class => VacationRequestPolicy::class, + Key::class => KeyPolicy::class, ]; public function boot(): void diff --git a/app/Domain/Policies/KeyPolicy.php b/app/Domain/Policies/KeyPolicy.php new file mode 100644 index 0000000..cb62986 --- /dev/null +++ b/app/Domain/Policies/KeyPolicy.php @@ -0,0 +1,26 @@ +role === Role::AdministrativeApprover; + } + + public function give(User $user, Key $key): bool + { + if ($key->user()->is($user)) { + return true; + } + + return $user->role === Role::AdministrativeApprover; + } +} diff --git a/app/Eloquent/Models/Key.php b/app/Eloquent/Models/Key.php index 5488f19..cb7ee23 100644 --- a/app/Eloquent/Models/Key.php +++ b/app/Eloquent/Models/Key.php @@ -11,8 +11,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; /** * @property int $id - * @property User $owner - * @property User $previousOwner + * @property User $user */ class Key extends Model { @@ -20,14 +19,9 @@ class Key extends Model protected $guarded = []; - public function owner(): BelongsTo + public function user(): BelongsTo { - return $this->belongsTo(User::class, "owner_id"); - } - - public function previousOwner(): BelongsTo - { - return $this->belongsTo(User::class, "previous_owner_id"); + return $this->belongsTo(User::class); } protected static function newFactory(): KeyFactory diff --git a/app/Eloquent/Models/User.php b/app/Eloquent/Models/User.php index d167dd5..4bf2891 100644 --- a/app/Eloquent/Models/User.php +++ b/app/Eloquent/Models/User.php @@ -74,6 +74,11 @@ class User extends Authenticatable return $this->hasMany(Vacation::class); } + public function keys(): HasMany + { + return $this->hasMany(Key::class); + } + public function hasRole(Role $role): bool { return $this->role === $role; diff --git a/app/Infrastructure/Http/Controllers/KeysController.php b/app/Infrastructure/Http/Controllers/KeysController.php index c7711d0..6fa2940 100644 --- a/app/Infrastructure/Http/Controllers/KeysController.php +++ b/app/Infrastructure/Http/Controllers/KeysController.php @@ -4,20 +4,83 @@ declare(strict_types=1); namespace Toby\Infrastructure\Http\Controllers; +use Illuminate\Auth\Access\AuthorizationException; use Illuminate\Http\Request; use Inertia\Response; -use Toby\Eloquent\Helpers\YearPeriodRetriever; +use Symfony\Component\HttpFoundation\RedirectResponse; use Toby\Eloquent\Models\Key; +use Toby\Eloquent\Models\User; +use Toby\Infrastructure\Http\Requests\GiveKeyRequest; use Toby\Infrastructure\Http\Resources\KeyResource; +use Toby\Infrastructure\Http\Resources\SimpleUserResource; class KeysController extends Controller { - public function index(Request $request, YearPeriodRetriever $yearPeriodRetriever): Response + public function index(Request $request): Response { - $keys = Key::query()->get(); + $keys = Key::query() + ->oldest() + ->get(); - return inertia("Keys/Index", [ + $users = User::query() + ->where("id", "!=", $request->user()->id) + ->orderByProfileField("last_name") + ->orderByProfileField("first_name") + ->get(); + + return inertia("Keys", [ "keys" => KeyResource::collection($keys), + "users" => SimpleUserResource::collection($users), + "can" => [ + "manageKeys" => $request->user()->can("manage", Key::class), + ], ]); } + + /** + * @throws AuthorizationException + */ + public function store(Request $request): RedirectResponse + { + $this->authorize("manageKeys"); + + $request->user()->keys()->create(); + + return redirect() + ->back() + ->with("success", __("Key has been created.")); + } + + public function take(Key $key, Request $request): RedirectResponse + { + $key->user()->associate($request->user()); + + $key->save(); + + return redirect() + ->back() + ->with("success", __("Key has been taked.")); + } + + public function give(Key $key, GiveKeyRequest $request): RedirectResponse + { + $key->user()->associate($request->recipient()); + + $key->save(); + + return redirect() + ->back() + ->with("success", __("Key has been given.")); + } + + public function destroy(Key $key): RedirectResponse + { + $this->authorize("manageKeys"); + + $key->delete(); + + return redirect() + ->back() + ->with("success", __("Key has been deleted.")); + } } diff --git a/app/Infrastructure/Http/Requests/GiveKeyRequest.php b/app/Infrastructure/Http/Requests/GiveKeyRequest.php new file mode 100644 index 0000000..7505821 --- /dev/null +++ b/app/Infrastructure/Http/Requests/GiveKeyRequest.php @@ -0,0 +1,23 @@ + ["required", "exists:users,id"], + ]; + } + + public function recipient(): User + { + return User::find($this->get("user")); + } +} diff --git a/app/Infrastructure/Http/Resources/KeyResource.php b/app/Infrastructure/Http/Resources/KeyResource.php index 0b5da33..d6ee471 100644 --- a/app/Infrastructure/Http/Resources/KeyResource.php +++ b/app/Infrastructure/Http/Resources/KeyResource.php @@ -14,9 +14,12 @@ class KeyResource extends JsonResource { return [ "id" => $this->id, - "owner" => new UserResource($this->owner), - "previousOwner" => new UserResource($this->previousOwner), + "user" => new SimpleUserResource($this->user), "updatedAt" => $this->updated_at->toDatetimeString(), + "can" => [ + "give" => $request->user()->can("give", $this->resource), + "take" => !$this->user()->is($request->user()), + ], ]; } } diff --git a/database/factories/KeyFactory.php b/database/factories/KeyFactory.php index 4a3007b..52e1694 100644 --- a/database/factories/KeyFactory.php +++ b/database/factories/KeyFactory.php @@ -15,8 +15,7 @@ class KeyFactory extends Factory public function definition(): array { return [ - "owner_id" => User::factory(), - "previous_owner_id" => User::factory(), + "user_id" => User::factory(), ]; } } diff --git a/database/migrations/2022_04_12_152528_create_keys_table.php b/database/migrations/2022_04_12_152528_create_keys_table.php index de8b97f..1d3ee72 100644 --- a/database/migrations/2022_04_12_152528_create_keys_table.php +++ b/database/migrations/2022_04_12_152528_create_keys_table.php @@ -12,8 +12,9 @@ return new class() extends Migration { { Schema::create("keys", function (Blueprint $table): void { $table->id(); - $table->foreignIdFor(User::class, "owner_id")->constrained("users")->cascadeOnDelete(); - $table->foreignIdFor(User::class, "previous_owner_id")->constrained("users")->cascadeOnDelete(); + $table->foreignIdFor(User::class) + ->constrained() + ->cascadeOnDelete(); $table->timestamps(); }); } diff --git a/database/seeders/DemoSeeder.php b/database/seeders/DemoSeeder.php index 53454e2..41470ba 100644 --- a/database/seeders/DemoSeeder.php +++ b/database/seeders/DemoSeeder.php @@ -19,6 +19,7 @@ use Toby\Domain\States\VacationRequest\Rejected; use Toby\Domain\States\VacationRequest\WaitingForAdministrative; use Toby\Domain\States\VacationRequest\WaitingForTechnical; use Toby\Domain\VacationDaysCalculator; +use Toby\Eloquent\Models\Key; use Toby\Eloquent\Models\User; use Toby\Eloquent\Models\VacationLimit; use Toby\Eloquent\Models\VacationRequest; @@ -328,5 +329,11 @@ class DemoSeeder extends Seeder $vacationRequestRejected->state = new Rejected($vacationRequestRejected); $vacationRequestRejected->save(); + + foreach ($users as $user) { + Key::factory() + ->for($user) + ->create(); + } } } diff --git a/resources/js/Pages/Keys.vue b/resources/js/Pages/Keys.vue new file mode 100644 index 0000000..fb1e025 --- /dev/null +++ b/resources/js/Pages/Keys.vue @@ -0,0 +1,349 @@ + + + diff --git a/resources/js/Pages/Keys/Index.vue b/resources/js/Pages/Keys/Index.vue deleted file mode 100644 index 607f5ad..0000000 --- a/resources/js/Pages/Keys/Index.vue +++ /dev/null @@ -1,162 +0,0 @@ - - -s diff --git a/routes/web.php b/routes/web.php index 950fdb1..f35e8f6 100644 --- a/routes/web.php +++ b/routes/web.php @@ -34,10 +34,15 @@ Route::middleware(["auth", TrackUserLastActivity::class])->group(function (): vo ->except("show") ->whereNumber("holiday"); - Route::post("year-periods/{yearPeriod}/select", SelectYearPeriodController::class) + Route::get("/keys", [KeysController::class, "index"]); + Route::post("/keys", [KeysController::class, "store"]); + Route::delete("/keys/{key}", [KeysController::class, "destroy"]); + Route::post("/keys/{key}/take", [KeysController::class, "take"]); + Route::post("/keys/{key}/give", [KeysController::class, "give"]); + + Route::post("/year-periods/{yearPeriod}/select", SelectYearPeriodController::class) ->whereNumber("yearPeriod") ->name("year-periods.select"); - Route::resource("keys", KeysController::class); Route::prefix("/vacation")->as("vacation.")->group(function (): void { Route::get("/limits", [VacationLimitController::class, "edit"])