#4 - users CRUD (#24)

* #2 - wip

* #2 - wip

* #2 - ui fixes to login page

* #2 - fix

* #2 - fix

* #4- wip

* #4 - wip

* #4 - wip

* #4 - wip

* #4- wip

* #4 - wip

* #4 - wip

* #4 - tests

* #4 - ecs fix

* #4 - fix

* #4 - wip

* #4 - fix

* #4 - fix

* #4 - fix composer

* #4 - cr fix

Co-authored-by: EwelinaLasowy <ewelina.lasowy@blumilk.pl>
This commit is contained in:
Adrian Hopek
2022-01-19 10:55:10 +01:00
committed by GitHub
parent 91bd46cc36
commit 9aa2d5ec0f
41 changed files with 1977 additions and 63 deletions

View File

@@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace Toby\Console\Commands;
use Illuminate\Console\Command;
use Toby\Models\User;
class CreateUserCommand extends Command
{
protected $signature = "user:create
{email : an email for the user}";
protected $description = "Creates a user";
public function handle(): void
{
$email = $this->argument("email");
User::factory([
"email" => $email,
])->create();
$this->info("The user has been created");
}
}

View File

@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Toby\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";
public function label(): string
{
return __($this->value);
}
public static function casesToSelect(): array
{
$cases = collect(EmploymentForm::cases());
return $cases->map(
fn(EmploymentForm $enum) => [
"label" => $enum->label(),
"value" => $enum->value,
],
)->toArray();
}
}

View File

@@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace Toby\Helpers;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use LasseRafn\InitialAvatarGenerator\InitialAvatar;
use SVG\SVG;
use Toby\Models\User;
class UserAvatarGenerator
{
public function __construct(
protected InitialAvatar $generator,
) {
}
public function generateFor(User $user): string
{
$path = "avatars/{$this->generateUuid()}.svg";
Storage::put($path, $this->generate($user));
return $path;
}
protected function generate(User $user): SVG
{
return $this->generator->rounded()
->background($this->getColor($user->name))
->color("#F4F8FD")
->smooth()
->fontSize(0.33)
->generateSvg($user->name);
}
protected function getColor(string $name): string
{
$colors = config("colors");
return $colors[strlen($name) % count($colors)];
}
protected function generateUuid(): string
{
return Str::uuid()->toString();
}
}

View File

@@ -0,0 +1,83 @@
<?php
declare(strict_types=1);
namespace Toby\Http\Controllers;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Inertia\Response;
use Toby\Enums\EmploymentForm;
use Toby\Http\Requests\UserRequest;
use Toby\Http\Resources\UserFormDataResource;
use Toby\Http\Resources\UserResource;
use Toby\Models\User;
class UserController extends Controller
{
public function index(Request $request): Response
{
$users = User::query()
->withTrashed()
->search($request->query("search"))
->latest()
->paginate()
->withQueryString();
return inertia("Users/Index", [
"users" => UserResource::collection($users),
"filters" => $request->only("search"),
]);
}
public function create(): Response
{
return inertia("Users/Create", [
"employmentForms" => EmploymentForm::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" => EmploymentForm::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"));
}
}

View File

@@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Toby\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Rules\Enum;
use Toby\Enums\EmploymentForm;
class UserRequest extends FormRequest
{
public function rules(): array
{
return [
"name" => ["required", "min:3", "max: 150"],
"email" => ["required", "email", Rule::unique("users", "email")->ignore($this->user)],
"employmentForm" => ["required", new Enum(EmploymentForm::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"),
];
}
}

View File

@@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
namespace Toby\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class UserFormDataResource extends JsonResource
{
public static $wrap = false;
public function toArray($request): array
{
return [
"id" => $this->id,
"name" => $this->name,
"email" => $this->email,
"employmentForm" => $this->employment_form,
"employmentDate" => $this->employment_date,
];
}
}

View File

@@ -8,6 +8,8 @@ use Illuminate\Http\Resources\Json\JsonResource;
class UserResource extends JsonResource
{
public static $wrap = false;
public function toArray($request): array
{
return [
@@ -15,7 +17,10 @@ class UserResource extends JsonResource
"name" => $this->name,
"email" => $this->email,
"role" => "Human Resources Manager",
"avatar" => "https://images.unsplash.com/photo-1550525811-e5869dd03032?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80",
"avatar" => asset($this->avatar),
"deleted" => $this->trashed(),
"employmentForm" => $this->employment_form->label(),
"employmentDate" => $this->employment_date->toDisplayString(),
];
}
}

View File

@@ -4,26 +4,53 @@ declare(strict_types=1);
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;
use Toby\Enums\EmploymentForm;
/**
* @property int $id
* @property string $name
* @property string $email
* @property string $avatar
* @property EmploymentForm $employment_form
* @property Carbon $employment_date
*/
class User extends Authenticatable
{
use HasFactory;
use Notifiable;
use SoftDeletes;
protected $fillable = [
"name",
"email",
"avatar",
"employment_form",
"employment_date",
];
protected $casts = [
"employment_form" => EmploymentForm::class,
"employment_date" => "datetime",
];
protected $hidden = [
"remember_token",
];
public function scopeSearch(Builder $query, ?string $text): Builder
{
if ($text === null) {
return $query;
}
return $query
->where("name", "LIKE", "%{$text}%")
->orWhere("email", "LIKE", "%{$text}%");
}
}

View File

@@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace Toby\Observers;
use Illuminate\Support\Facades\Storage;
use Toby\Helpers\UserAvatarGenerator;
use Toby\Models\User;
class UserObserver
{
public function __construct(
protected UserAvatarGenerator $generator,
) {
}
public function created(User $user): void
{
$user->avatar = $this->generator->generateFor($user);
$user->save();
}
public function updating(User $user): void
{
if ($user->isDirty("name")) {
Storage::delete($user->avatar);
$user->avatar = $this->generator->generateFor($user);
}
}
public function forceDeleted(User $user): void
{
Storage::delete($user->avatar);
}
}

View File

@@ -4,8 +4,17 @@ declare(strict_types=1);
namespace Toby\Providers;
use Illuminate\Support\Carbon;
use Illuminate\Support\ServiceProvider;
use Toby\Models\User;
use Toby\Observers\UserObserver;
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
User::observe(UserObserver::class);
Carbon::macro("toDisplayString", fn() => $this->translatedFormat("j F Y"));
}
}