From acf2eba95dc6ba30d86c20d2964200981bb5539d Mon Sep 17 00:00:00 2001 From: Adrian Hopek Date: Tue, 18 Jan 2022 07:39:03 +0100 Subject: [PATCH] #4- wip --- app/Enums/FormOfEmployment.php | 10 ++ app/Http/Controllers/UserController.php | 56 ++++++ app/Http/Requests/UserRequest.php | 33 ++++ app/Http/Resources/UserFormDataResource.php | 23 +++ app/Http/Resources/UserResource.php | 3 +- app/Models/User.php | 8 +- app/Observers/UserObserver.php | 2 +- database/factories/UserFactory.php | 2 +- .../2014_10_12_000000_create_users_table.php | 3 +- package-lock.json | 34 ++++ package.json | 2 + resources/css/app.css | 46 +++++ resources/js/Pages/Users/Create.vue | 162 +++++++++++++++++ resources/js/Pages/Users/Edit.vue | 166 ++++++++++++++++++ resources/js/Pages/Users/Index.vue | 113 +++++++++--- resources/js/app.js | 11 ++ routes/web.php | 2 +- 17 files changed, 644 insertions(+), 32 deletions(-) create mode 100644 app/Http/Requests/UserRequest.php create mode 100644 app/Http/Resources/UserFormDataResource.php create mode 100644 resources/js/Pages/Users/Create.vue create mode 100644 resources/js/Pages/Users/Edit.vue diff --git a/app/Enums/FormOfEmployment.php b/app/Enums/FormOfEmployment.php index 1b0d9a3..900b20d 100644 --- a/app/Enums/FormOfEmployment.php +++ b/app/Enums/FormOfEmployment.php @@ -15,4 +15,14 @@ enum FormOfEmployment: string { return __($this->value); } + + public static function casesToSelect(): array + { + $cases = collect(FormOfEmployment::cases()); + + return $cases->map(fn(FormOfEmployment $enum) => [ + "label" => $enum->label(), + "value" => $enum->value] + )->toArray(); + } } \ No newline at end of file diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index 113631f..1954f96 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -4,8 +4,12 @@ declare(strict_types=1); namespace Toby\Http\Controllers; +use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Inertia\Response; +use Toby\Enums\FormOfEmployment; +use Toby\Http\Requests\UserRequest; +use Toby\Http\Resources\UserFormDataResource; use Toby\Http\Resources\UserResource; use Toby\Models\User; @@ -14,6 +18,7 @@ class UserController extends Controller public function index(Request $request): Response { $users = User::query() + ->withTrashed() ->search($request->query("search")) ->paginate() ->withQueryString(); @@ -23,4 +28,55 @@ class UserController extends Controller "filters" => $request->only("search"), ]); } + + public function create(): Response + { + return inertia("Users/Create", [ + "employmentForms" => FormOfEmployment::casesToSelect(), + ]); + } + + public function store(UserRequest $request): RedirectResponse + { + User::query()->create($request->data()); + + return redirect() + ->route("users.index") + ->with("success", __("User has been created")); + } + + public function edit(User $user): Response + { + return inertia("Users/Edit", [ + "user" => new UserFormDataResource($user), + "employmentForms" => FormOfEmployment::casesToSelect() + ]); + } + + public function update(UserRequest $request, User $user): RedirectResponse + { + $user->update($request->data()); + + return redirect() + ->route("users.index") + ->with("success", __("User has been updated")); + } + + public function destroy(User $user): RedirectResponse + { + $user->delete(); + + return redirect() + ->route("users.index") + ->with("success", __("User has been deleted")); + } + + public function restore(User $user): RedirectResponse + { + $user->restore(); + + return redirect() + ->route("users.index") + ->with("success", __("User has been restored")); + } } diff --git a/app/Http/Requests/UserRequest.php b/app/Http/Requests/UserRequest.php new file mode 100644 index 0000000..a33d4c2 --- /dev/null +++ b/app/Http/Requests/UserRequest.php @@ -0,0 +1,33 @@ + ["required", "min:3", "max: 150"], + "email" => ["required", "email", Rule::unique("users", "email")->ignore($this->user)], + "employmentForm" => ["required", new Enum(FormOfEmployment::class)], + "employmentDate" => ["required", "date"], + ]; + } + + public function data(): array + { + return [ + "name" => $this->get("name"), + "email" => $this->get("email"), + "employment_form" => $this->get("employmentForm"), + "employment_date" => $this->get("employmentDate"), + ]; + } +} diff --git a/app/Http/Resources/UserFormDataResource.php b/app/Http/Resources/UserFormDataResource.php new file mode 100644 index 0000000..df7da1f --- /dev/null +++ b/app/Http/Resources/UserFormDataResource.php @@ -0,0 +1,23 @@ + $this->id, + "name" => $this->name, + "email" => $this->email, + "employmentForm" => $this->employment_form, + "employmentDate" => $this->employment_date, + ]; + } +} diff --git a/app/Http/Resources/UserResource.php b/app/Http/Resources/UserResource.php index e2b166a..e53c983 100644 --- a/app/Http/Resources/UserResource.php +++ b/app/Http/Resources/UserResource.php @@ -16,8 +16,9 @@ class UserResource extends JsonResource "email" => $this->email, "role" => "Human Resources Manager", "avatar" => asset($this->avatar), + "trashed" => $this->trashed(), "employmentForm" => $this->employment_form->label(), - "employmentStartDate" => $this->employment_start_date->translatedFormat("j F Y"), + "employmentDate" => $this->employment_date->translatedFormat("j F Y"), ]; } } diff --git a/app/Models/User.php b/app/Models/User.php index f9f5d5f..c4bc971 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -6,6 +6,7 @@ namespace Toby\Models; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use Illuminate\Support\Carbon; @@ -17,12 +18,13 @@ use Toby\Enums\FormOfEmployment; * @property string $email * @property string $avatar * @property FormOfEmployment $employment_form - * @property Carbon $empoyment_start_date + * @property Carbon $empoyment_date */ class User extends Authenticatable { use HasFactory; use Notifiable; + use SoftDeletes; protected $perPage = 10; @@ -31,12 +33,12 @@ class User extends Authenticatable "email", "avatar", "employment_form", - "employment_start_date", + "employment_date", ]; protected $casts = [ "employment_form" => FormOfEmployment::class, - "employment_start_date" => "datetime", + "employment_date" => "datetime", ]; protected $hidden = [ diff --git a/app/Observers/UserObserver.php b/app/Observers/UserObserver.php index b81ab0f..60058d3 100644 --- a/app/Observers/UserObserver.php +++ b/app/Observers/UserObserver.php @@ -29,7 +29,7 @@ class UserObserver } } - public function deleted(User $user): void + public function forceDeleted(User $user): void { Storage::delete($user->avatar); } diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index e3f0a21..1daa285 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -16,7 +16,7 @@ class UserFactory extends Factory "name" => "{$this->faker->firstName} {$this->faker->lastName}", "email" => $this->faker->unique()->safeEmail(), "employment_form" => $this->faker->randomElement(FormOfEmployment::cases()), - "employment_start_date" => $this->faker->dateTimeBetween("2020-10-27"), + "employment_date" => $this->faker->dateTimeBetween("2020-10-27"), "remember_token" => Str::random(10), ]; } 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 c057ac9..e7717b5 100644 --- a/database/migrations/2014_10_12_000000_create_users_table.php +++ b/database/migrations/2014_10_12_000000_create_users_table.php @@ -15,8 +15,9 @@ return new class() extends Migration { $table->string("email")->unique(); $table->string("avatar")->nullable(); $table->string("employment_form"); - $table->dateTime("employment_start_date"); + $table->dateTime("employment_date"); $table->rememberToken(); + $table->softDeletes(); $table->timestamps(); }); } diff --git a/package-lock.json b/package-lock.json index e869d61..7a47b79 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,11 +15,13 @@ "@tailwindcss/typography": "^0.5.0", "@vue/compiler-sfc": "^3.2.26", "autoprefixer": "^10.4.2", + "flatpickr": "^4.6.9", "laravel-mix": "^6.0.6", "lodash": "^4.17.21", "postcss": "^8.4.5", "tailwindcss": "^3.0.13", "vue": "^3.2.26", + "vue-flatpickr-component": "^9.0.5", "vue-loader": "^17.0.0" }, "devDependencies": { @@ -4723,6 +4725,11 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/flatpickr": { + "version": "4.6.9", + "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.9.tgz", + "integrity": "sha512-F0azNNi8foVWKSF+8X+ZJzz8r9sE1G4hl06RyceIaLvyltKvDl6vqk9Lm/6AUUCi5HWaIjiUbk7UpeE/fOXOpw==" + }, "node_modules/flatted": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", @@ -8893,6 +8900,20 @@ "node": ">=4.0" } }, + "node_modules/vue-flatpickr-component": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/vue-flatpickr-component/-/vue-flatpickr-component-9.0.5.tgz", + "integrity": "sha512-fKuz/D4ePQKi+jPo4xjYRgBCLTWrTsCoKbx8nam63x4kTDtSqvFOjNwLvy0QgwC0lC+aFpUWa1dNYTH0hgUcCA==", + "dependencies": { + "flatpickr": "^4.6.9" + }, + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, "node_modules/vue-loader": { "version": "17.0.0", "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-17.0.0.tgz", @@ -12983,6 +13004,11 @@ "rimraf": "^3.0.2" } }, + "flatpickr": { + "version": "4.6.9", + "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.9.tgz", + "integrity": "sha512-F0azNNi8foVWKSF+8X+ZJzz8r9sE1G4hl06RyceIaLvyltKvDl6vqk9Lm/6AUUCi5HWaIjiUbk7UpeE/fOXOpw==" + }, "flatted": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.4.tgz", @@ -15971,6 +15997,14 @@ } } }, + "vue-flatpickr-component": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/vue-flatpickr-component/-/vue-flatpickr-component-9.0.5.tgz", + "integrity": "sha512-fKuz/D4ePQKi+jPo4xjYRgBCLTWrTsCoKbx8nam63x4kTDtSqvFOjNwLvy0QgwC0lC+aFpUWa1dNYTH0hgUcCA==", + "requires": { + "flatpickr": "^4.6.9" + } + }, "vue-loader": { "version": "17.0.0", "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-17.0.0.tgz", diff --git a/package.json b/package.json index b858b8c..f0212fa 100644 --- a/package.json +++ b/package.json @@ -22,11 +22,13 @@ "@tailwindcss/typography": "^0.5.0", "@vue/compiler-sfc": "^3.2.26", "autoprefixer": "^10.4.2", + "flatpickr": "^4.6.9", "laravel-mix": "^6.0.6", "lodash": "^4.17.21", "postcss": "^8.4.5", "tailwindcss": "^3.0.13", "vue": "^3.2.26", + "vue-flatpickr-component": "^9.0.5", "vue-loader": "^17.0.0" }, "devDependencies": { diff --git a/resources/css/app.css b/resources/css/app.css index b5c61c9..b34f7de 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -1,3 +1,49 @@ +@import 'flatpickr/dist/themes/light.css'; + @tailwind base; @tailwind components; @tailwind utilities; + + +.flatpickr-months .flatpickr-prev-month:hover svg, +.flatpickr-months .flatpickr-next-month:hover svg { + fill: #4F46E5; +} + +.flatpickr-day.selected, +.flatpickr-day.startRange, +.flatpickr-day.endRange, +.flatpickr-day.selected.inRange, +.flatpickr-day.startRange.inRange, +.flatpickr-day.endRange.inRange, +.flatpickr-day.selected:focus, +.flatpickr-day.startRange:focus, +.flatpickr-day.endRange:focus, +.flatpickr-day.selected:hover, +.flatpickr-day.startRange:hover, +.flatpickr-day.endRange:hover, +.flatpickr-day.selected.prevMonthDay, +.flatpickr-day.startRange.prevMonthDay, +.flatpickr-day.endRange.prevMonthDay, +.flatpickr-day.selected.nextMonthDay, +.flatpickr-day.startRange.nextMonthDay, +.flatpickr-day.endRange.nextMonthDay { + background: #527ABA; + -webkit-box-shadow: none; + box-shadow: none; + color: #fff; + border-color: #527ABA; +} + +.flatpickr-day.selected.startRange + .endRange:not(:nth-child(7n+1)), +.flatpickr-day.startRange.startRange + .endRange:not(:nth-child(7n+1)), +.flatpickr-day.endRange.startRange + .endRange:not(:nth-child(7n+1)) { + -webkit-box-shadow: -10px 0 0 #527ABA; + box-shadow: -10px 0 0 #527ABA; +} + +.flatpickr-day.week.selected { + border-radius: 0; + -webkit-box-shadow: -5px 0 0 #527ABA, 5px 0 0 #527ABA; + box-shadow: -5px 0 0 #527ABA, 5px 0 0 #527ABA; +} \ No newline at end of file diff --git a/resources/js/Pages/Users/Create.vue b/resources/js/Pages/Users/Create.vue new file mode 100644 index 0000000..45b231b --- /dev/null +++ b/resources/js/Pages/Users/Create.vue @@ -0,0 +1,162 @@ + + + diff --git a/resources/js/Pages/Users/Edit.vue b/resources/js/Pages/Users/Edit.vue new file mode 100644 index 0000000..aea3270 --- /dev/null +++ b/resources/js/Pages/Users/Edit.vue @@ -0,0 +1,166 @@ + + + diff --git a/resources/js/Pages/Users/Index.vue b/resources/js/Pages/Users/Index.vue index 6a52049..8d381ea 100644 --- a/resources/js/Pages/Users/Index.vue +++ b/resources/js/Pages/Users/Index.vue @@ -1,13 +1,23 @@