#27 - separate fields for name and surname (#29)

* #27 - separate fields for name and surname

* #27 -cr fix
This commit is contained in:
Adrian Hopek 2022-01-24 12:40:56 +01:00 committed by GitHub
parent e147d24365
commit 6854c7a9f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 127 additions and 48 deletions

View File

@ -29,11 +29,11 @@ class UserAvatarGenerator
protected function generate(User $user): SVG
{
return $this->generator->rounded()
->background($this->getColor($user->name))
->background($this->getColor($user->fullName))
->color("#F4F8FD")
->smooth()
->fontSize(0.33)
->generateSvg($user->name);
->generateSvg($user->fullName);
}
protected function getColor(string $name): string

View File

@ -20,7 +20,8 @@ class UserController extends Controller
$users = User::query()
->withTrashed()
->search($request->query("search"))
->latest()
->orderBy("last_name")
->orderBy("first_name")
->paginate()
->withQueryString();

View File

@ -14,8 +14,14 @@ class VacationLimitController extends Controller
{
public function edit(): Response
{
$limits = VacationLimit::query()
->with("user")
->orderByUserField("last_name")
->orderByUserField("first_name")
->get();
return inertia("VacationLimits", [
"limits" => VacationLimitResource::collection(VacationLimit::query()->with("user")->get()),
"limits" => VacationLimitResource::collection($limits),
]);
}

View File

@ -14,7 +14,8 @@ class UserRequest extends FormRequest
public function rules(): array
{
return [
"name" => ["required", "min:3", "max: 150"],
"firstName" => ["required", "min:3", "max:80"],
"lastName" => ["required", "min:3", "max:80"],
"email" => ["required", "email", Rule::unique("users", "email")->ignore($this->user)],
"employmentForm" => ["required", new Enum(EmploymentForm::class)],
"employmentDate" => ["required", "date"],
@ -24,7 +25,8 @@ class UserRequest extends FormRequest
public function data(): array
{
return [
"name" => $this->get("name"),
"first_name" => $this->get("firstName"),
"last_name" => $this->get("lastName"),
"email" => $this->get("email"),
"employment_form" => $this->get("employmentForm"),
"employment_date" => $this->get("employmentDate"),

View File

@ -14,7 +14,8 @@ class UserFormDataResource extends JsonResource
{
return [
"id" => $this->id,
"name" => $this->name,
"firstName" => $this->first_name,
"lastName" => $this->last_name,
"email" => $this->email,
"employmentForm" => $this->employment_form,
"employmentDate" => $this->employment_date,

View File

@ -14,7 +14,7 @@ class UserResource extends JsonResource
{
return [
"id" => $this->id,
"name" => $this->name,
"name" => $this->fullName,
"email" => $this->email,
"role" => "Human Resources Manager",
"avatar" => asset($this->avatar),

View File

@ -16,7 +16,8 @@ use Toby\Enums\EmploymentForm;
/**
* @property int $id
* @property string $name
* @property string $first_name
* @property string $last_name
* @property string $email
* @property string $avatar
* @property EmploymentForm $employment_form
@ -29,13 +30,7 @@ class User extends Authenticatable
use Notifiable;
use SoftDeletes;
protected $fillable = [
"name",
"email",
"avatar",
"employment_form",
"employment_date",
];
protected $guarded = [];
protected $casts = [
"employment_form" => EmploymentForm::class,
@ -58,7 +53,8 @@ class User extends Authenticatable
}
return $query
->where("name", "LIKE", "%{$text}%")
->where("first_name", "LIKE", "%{$text}%")
->orWhere("last_name", "LIKE", "%{$text}%")
->orWhere("email", "LIKE", "%{$text}%");
}
@ -68,4 +64,9 @@ class User extends Authenticatable
$this->save();
}
public function getFullNameAttribute(): string
{
return "{$this->first_name} {$this->last_name}";
}
}

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Toby\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -34,4 +35,11 @@ class VacationLimit extends Model
{
return $this->belongsTo(YearPeriod::class);
}
public function scopeOrderByUserField(Builder $query, string $field): Builder
{
$userQuery = User::query()->select($field)->whereColumn("vacation_limits.user_id", "users.id");
return $query->orderBy($userQuery);
}
}

View File

@ -28,7 +28,7 @@ class UserObserver
public function updating(User $user): void
{
if ($user->isDirty("name")) {
if ($user->isDirty(["first_name", "last_name"])) {
Storage::delete($user->avatar);
$user->avatar = $this->generator->generateFor($user);
}

View File

@ -13,7 +13,8 @@ class UserFactory extends Factory
public function definition(): array
{
return [
"name" => "{$this->faker->firstName} {$this->faker->lastName}",
"first_name" => $this->faker->firstName(),
"last_name" => $this->faker->lastName(),
"email" => $this->faker->unique()->safeEmail(),
"employment_form" => $this->faker->randomElement(EmploymentForm::cases()),
"employment_date" => $this->faker->dateTimeBetween("2020-10-27"),

View File

@ -17,7 +17,6 @@ class VacationLimitFactory extends Factory
return [
"user_id" => User::factory(),
"year_period_id" => YearPeriod::factory(),
"has_vacation" => $hasVacation,
"days" => $hasVacation ? $this->faker->numberBetween(20, 26) : null,
];
}

View File

@ -11,7 +11,8 @@ return new class() extends Migration {
{
Schema::create("users", function (Blueprint $table): void {
$table->id();
$table->string("name");
$table->string("first_name");
$table->string("last_name");
$table->string("email")->unique();
$table->string("avatar")->nullable();
$table->string("employment_form");

View File

@ -15,24 +15,47 @@
>
<div class="sm:grid sm:grid-cols-3 py-4 items-center">
<label
for="name"
for="firstName"
class="block text-sm font-medium text-gray-700 sm:mt-px"
>
Imię i nazwisko
Imię
</label>
<div class="mt-1 sm:mt-0 sm:col-span-2">
<input
id="name"
v-model="form.name"
id="firstName"
v-model="form.firstName"
type="text"
class="block w-full max-w-lg shadow-sm rounded-md sm:text-sm"
:class="{ 'border-red-300 text-red-900 focus:outline-none focus:ring-red-500 focus:border-red-500': form.errors.name, 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors.name }"
:class="{ 'border-red-300 text-red-900 focus:outline-none focus:ring-red-500 focus:border-red-500': form.errors.firstName, 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors.firstName }"
>
<p
v-if="form.errors.name"
v-if="form.errors.firstName"
class="mt-2 text-sm text-red-600"
>
{{ form.errors.name }}
{{ form.errors.firstName }}
</p>
</div>
</div>
<div class="sm:grid sm:grid-cols-3 py-4 items-center">
<label
for="lastName"
class="block text-sm font-medium text-gray-700 sm:mt-px"
>
Nazwisko
</label>
<div class="mt-1 sm:mt-0 sm:col-span-2">
<input
id="lastName"
v-model="form.lastName"
type="text"
class="block w-full max-w-lg shadow-sm rounded-md sm:text-sm"
:class="{ 'border-red-300 text-red-900 focus:outline-none focus:ring-red-500 focus:border-red-500': form.errors.lastName, 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors.lastName }"
>
<p
v-if="form.errors.lastName"
class="mt-2 text-sm text-red-600"
>
{{ form.errors.lastName }}
</p>
</div>
</div>
@ -184,7 +207,8 @@ export default {
},
setup(props) {
const form = useForm({
name: null,
firstName: null,
lastName: null,
email: null,
employmentForm: props.employmentForms[0],
employmentDate: new Date(),

View File

@ -15,24 +15,47 @@
>
<div class="sm:grid sm:grid-cols-3 py-4 items-center">
<label
for="name"
for="firstName"
class="block text-sm font-medium text-gray-700 sm:mt-px"
>
Imię i nazwisko
Imię
</label>
<div class="mt-1 sm:mt-0 sm:col-span-2">
<input
id="name"
v-model="form.name"
id="firstName"
v-model="form.firstName"
type="text"
class="block w-full max-w-lg shadow-sm rounded-md sm:text-sm"
:class="{ 'border-red-300 text-red-900 focus:outline-none focus:ring-red-500 focus:border-red-500': form.errors.name, 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors.name }"
:class="{ 'border-red-300 text-red-900 focus:outline-none focus:ring-red-500 focus:border-red-500': form.errors.firstName, 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors.firstName }"
>
<p
v-if="form.errors.name"
v-if="form.errors.firstName"
class="mt-2 text-sm text-red-600"
>
{{ form.errors.name }}
{{ form.errors.firstName }}
</p>
</div>
</div>
<div class="sm:grid sm:grid-cols-3 py-4 items-center">
<label
for="lastName"
class="block text-sm font-medium text-gray-700 sm:mt-px"
>
Nazwisko
</label>
<div class="mt-1 sm:mt-0 sm:col-span-2">
<input
id="lastName"
v-model="form.lastName"
type="text"
class="block w-full max-w-lg shadow-sm rounded-md sm:text-sm"
:class="{ 'border-red-300 text-red-900 focus:outline-none focus:ring-red-500 focus:border-red-500': form.errors.lastName, 'focus:ring-blumilk-500 focus:border-blumilk-500 sm:text-sm border-gray-300': !form.errors.lastName }"
>
<p
v-if="form.errors.lastName"
class="mt-2 text-sm text-red-600"
>
{{ form.errors.lastName }}
</p>
</div>
</div>
@ -188,7 +211,8 @@ export default {
},
setup(props) {
const form = useForm({
name: props.user.name,
firstName: props.user.firstName,
lastName: props.user.lastName,
email: props.user.email,
employmentForm: props.employmentForms.find(form => form.value === props.user.employmentForm),
employmentDate: new Date(props.user.employmentDate),

View File

@ -200,7 +200,7 @@
</tbody>
</table>
<div
v-if="users.data.length"
v-if="users.data.length && users.meta.last_page !== 1"
class="bg-white px-4 py-3 flex items-center justify-between border-t border-gray-200 sm:px-6 rounded-b-lg"
>
<div class="flex-1 flex justify-between sm:hidden">

View File

@ -34,22 +34,27 @@ class UserTest extends FeatureTestCase
public function testAdminCanSearchUsersList(): void
{
User::factory([
"name" => "Test User1",
"first_name" => "Test",
"last_name" => "User1",
])->create();
User::factory([
"name" => "Test User2",
"first_name" => "Test",
"last_name" => "User2",
])->create();
User::factory([
"name" => "Test User3",
"first_name" => "Test",
"last_name" => "User3",
])->create();
$admin = User::factory([
"name" => "John Doe",
"first_name" => "John",
"last_name" => "Doe",
])->create();
$this->assertDatabaseCount("users", 4);
$this->actingAs($admin)
->get("/users?search=test")
->assertOk()
->assertInertia(
fn(Assert $page) => $page
->component("Users/Index")
@ -80,7 +85,8 @@ class UserTest extends FeatureTestCase
$this->actingAs($admin)
->post("/users", [
"name" => "John Doe",
"firstName" => "John",
"lastName" => "Doe",
"email" => "john.doe@example.com",
"employmentForm" => EmploymentForm::B2B_CONTRACT->value,
"employmentDate" => Carbon::now()->toDateTimeString(),
@ -88,7 +94,8 @@ class UserTest extends FeatureTestCase
->assertSessionHasNoErrors();
$this->assertDatabaseHas("users", [
"name" => "John Doe",
"first_name" => "John",
"last_name" => "Doe",
"email" => "john.doe@example.com",
"employment_form" => EmploymentForm::B2B_CONTRACT->value,
"employment_date" => Carbon::now()->toDateTimeString(),
@ -103,7 +110,8 @@ class UserTest extends FeatureTestCase
Carbon::setTestNow();
$this->assertDatabaseHas("users", [
"name" => $user->name,
"first_name" => $user->first_name,
"last_name" => $user->last_name,
"email" => $user->email,
"employment_form" => $user->employment_form->value,
"employment_date" => $user->employment_date->toDateTimeString(),
@ -111,7 +119,8 @@ class UserTest extends FeatureTestCase
$this->actingAs($admin)
->put("/users/{$user->id}", [
"name" => "John Doe",
"firstName" => "John",
"lastName" => "Doe",
"email" => "john.doe@example.com",
"employmentForm" => EmploymentForm::B2B_CONTRACT->value,
"employmentDate" => Carbon::now()->toDateTimeString(),
@ -119,7 +128,8 @@ class UserTest extends FeatureTestCase
->assertSessionHasNoErrors();
$this->assertDatabaseHas("users", [
"name" => "John Doe",
"first_name" => "John",
"last_name" => "Doe",
"email" => "john.doe@example.com",
"employment_form" => EmploymentForm::B2B_CONTRACT->value,
"employment_date" => Carbon::now()->toDateTimeString(),

View File

@ -49,7 +49,8 @@ class AvatarTest extends TestCase
Storage::assertExists($oldAvatar);
$user->update([
"name" => "John Doe",
"first_name" => "John",
"last_name" => "Doe",
]);
Storage::assertMissing($oldAvatar);