refactor(user): menambahkan 1 method gradies di enum status
-merubah cara menampilkan data dari menggunakan tabel menjadi foreach card -menghapus dispatch datatable -membuat referral code factory -menyesuaikan test
This commit is contained in:
parent
5a49bc8ba4
commit
bfe5207d7f
@ -30,4 +30,13 @@ public function color()
|
|||||||
self::SUSPENDED => 'red',
|
self::SUSPENDED => 'red',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function gradient(): string
|
||||||
|
{
|
||||||
|
return match ($this) {
|
||||||
|
self::ACTIVE => 'bg-gradient-to-b from-emerald-800 to-emerald-400',
|
||||||
|
self::INACTIVE => 'bg-gradient-to-b from-yellow-800 to-yellow-400',
|
||||||
|
self::SUSPENDED => 'bg-gradient-to-b from-red-800 to-red-400',
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,192 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Livewire\Datatable\Studio\Master;
|
|
||||||
|
|
||||||
use App\Enums\UserStatus;
|
|
||||||
use App\Models\Employee;
|
|
||||||
use App\Traits\Datatable\WithAppendColumn;
|
|
||||||
use App\Traits\Datatable\WithConfiguration;
|
|
||||||
use App\Traits\Datatable\WithPrependColumn;
|
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
|
||||||
use Illuminate\Support\Facades\Blade;
|
|
||||||
use Rappasoft\LaravelLivewireTables\DataTableComponent;
|
|
||||||
use Rappasoft\LaravelLivewireTables\Views\Column;
|
|
||||||
use Rappasoft\LaravelLivewireTables\Views\Columns\ArrayColumn;
|
|
||||||
use Rappasoft\LaravelLivewireTables\Views\Filters\MultiSelectFilter;
|
|
||||||
|
|
||||||
class UsersTable extends DataTableComponent
|
|
||||||
{
|
|
||||||
use WithAppendColumn, WithConfiguration, WithPrependColumn;
|
|
||||||
|
|
||||||
protected $model = Employee::class;
|
|
||||||
|
|
||||||
public function columns(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
Column::make('Nama Lengkap')
|
|
||||||
->label(
|
|
||||||
fn ($row) => <<<HTML
|
|
||||||
<div>
|
|
||||||
<div>{$row->full_name}</div>
|
|
||||||
<span class="text-xs text-gray-400">{$row->code}</span>
|
|
||||||
</div>
|
|
||||||
HTML
|
|
||||||
)
|
|
||||||
->searchable(
|
|
||||||
fn ($query, $searchTerm) => $query->where('full_name', 'like', "%{$searchTerm}%")
|
|
||||||
->orWhere('code', 'like', "%{$searchTerm}%")
|
|
||||||
)
|
|
||||||
->html(),
|
|
||||||
|
|
||||||
Column::make('Kredensial')
|
|
||||||
->label(
|
|
||||||
fn ($row) => <<<HTML
|
|
||||||
<div>
|
|
||||||
<div>{$row?->user?->email}</div>
|
|
||||||
<span class="text-xs text-gray-400">{$row?->user?->username}</span>
|
|
||||||
</div>
|
|
||||||
HTML
|
|
||||||
)
|
|
||||||
->searchable(
|
|
||||||
fn ($query, $searchTerm) => $query->whereHas(
|
|
||||||
'user',
|
|
||||||
fn ($q) => $q->where('email', 'like', "%{$searchTerm}%")
|
|
||||||
->orWhere('username', 'like', "%{$searchTerm}%")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
->html(),
|
|
||||||
|
|
||||||
Column::make('Nomor Telepon', 'phone_number')->searchable(),
|
|
||||||
|
|
||||||
Column::make('Jenis Kelamin', 'gender')
|
|
||||||
->format(fn ($value) => Blade::render(
|
|
||||||
'<flux:badge color="'.$value->color().'">'
|
|
||||||
.$value->label()
|
|
||||||
.'</flux:badge>'
|
|
||||||
))
|
|
||||||
->html(),
|
|
||||||
|
|
||||||
Column::make('Kode Referral', 'user.referralCode.code')
|
|
||||||
->format(
|
|
||||||
fn ($value) => $value
|
|
||||||
? Blade::render('<flux:badge color="zinc">'.$value.'</flux:badge>')
|
|
||||||
: ''
|
|
||||||
)
|
|
||||||
->html(),
|
|
||||||
|
|
||||||
Column::make('Gaji Pokok', 'base_salary')
|
|
||||||
->searchable()
|
|
||||||
->sortable()
|
|
||||||
->format(fn ($value) => currency($value, 'Rp')),
|
|
||||||
|
|
||||||
Column::make('Alamat', 'address')->searchable(),
|
|
||||||
|
|
||||||
ArrayColumn::make('Outlet')
|
|
||||||
->data(fn ($value, $row) => $row->user->outlets->pluck('name')->toArray())
|
|
||||||
->outputFormat(fn ($index, $value) => Blade::render('<span class="text-xs text-gray-400 border border-gray-400 rounded px-1">'.$value.'</span>'))
|
|
||||||
->flexRow(['class' => 'gap-2 flex-wrap']),
|
|
||||||
|
|
||||||
Column::make('Tanggal')
|
|
||||||
->label(function ($row) {
|
|
||||||
$birthdate = formatDate($row->birthdate);
|
|
||||||
$birthAgo = getAge($row->birthdate);
|
|
||||||
|
|
||||||
$hireDate = formatDate($row?->user?->hire_date);
|
|
||||||
$openedAgo = timeAgo($row?->user?->hire_date);
|
|
||||||
|
|
||||||
$resigndate = formatDate($row?->user?->resign_date);
|
|
||||||
$resignAgo = timeAgo($row?->user?->resign_date);
|
|
||||||
|
|
||||||
return <<<HTML
|
|
||||||
<div class="flex flex-col text-xs text-gray-700 dark:text-gray-300 gap-0.5">
|
|
||||||
<div>
|
|
||||||
<span class="font-medium text-gray-900 dark:text-white">Tgl Lahir:</span>
|
|
||||||
<span>{$birthdate}</span>
|
|
||||||
<span class="text-gray-500">({$birthAgo})</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span class="font-medium text-gray-900 dark:text-white">Tgl Bergabung:</span>
|
|
||||||
<span>{$hireDate}</span>
|
|
||||||
<span class="text-gray-500">({$openedAgo})</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<span class="font-medium text-gray-900 dark:text-white">Tgl Resign:</span>
|
|
||||||
<span>{$resigndate}</span>
|
|
||||||
<span class="text-gray-500">({$resignAgo})</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
HTML;
|
|
||||||
})
|
|
||||||
->html(),
|
|
||||||
|
|
||||||
Column::make('Status', 'user.status')
|
|
||||||
->format(
|
|
||||||
fn ($value) => Blade::render('<flux:badge color="'.UserStatus::tryFrom($value)->color().'">'.UserStatus::tryFrom($value)->label().'</flux:badge>')
|
|
||||||
)
|
|
||||||
->html(),
|
|
||||||
|
|
||||||
Column::make('Aksi')
|
|
||||||
->label(function ($row) {
|
|
||||||
$actions = '';
|
|
||||||
|
|
||||||
if (auth()->id() !== $row?->user?->id) {
|
|
||||||
if (auth()->user()->can('update user')) {
|
|
||||||
$actions .= view('components.datatables.edit', [
|
|
||||||
'id' => $row->hash,
|
|
||||||
'editRoute' => route('studio.master.user.edit', $row->hash),
|
|
||||||
])->render();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (auth()->user()->can('delete user')) {
|
|
||||||
$actions .= view('components.datatables.delete', [
|
|
||||||
'id' => $row->hash,
|
|
||||||
'deleteRoute' => route('studio.master.user.delete', $row->hash),
|
|
||||||
])->render();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $actions;
|
|
||||||
})
|
|
||||||
->html(),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function builder(): Builder
|
|
||||||
{
|
|
||||||
return Employee::query()
|
|
||||||
->select(
|
|
||||||
'employees.id',
|
|
||||||
'employees.user_id',
|
|
||||||
'employees.code',
|
|
||||||
'employees.full_name',
|
|
||||||
'employees.phone_number',
|
|
||||||
'employees.gender',
|
|
||||||
'employees.birthdate',
|
|
||||||
'employees.base_salary',
|
|
||||||
'employees.address'
|
|
||||||
)
|
|
||||||
->with([
|
|
||||||
'user',
|
|
||||||
'user.referralCode',
|
|
||||||
'user.outlets',
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function filters(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
MultiSelectFilter::make('Status')
|
|
||||||
->options(
|
|
||||||
collect(UserStatus::cases())
|
|
||||||
->mapWithKeys(fn ($case) => [$case->value => $case->label()])
|
|
||||||
->toArray()
|
|
||||||
)
|
|
||||||
->filter(function (Builder $builder, array $values) {
|
|
||||||
$builder->whereHas(
|
|
||||||
'user',
|
|
||||||
fn ($q) => $q->whereIn('status', $values)
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -31,8 +31,6 @@ public function save()
|
|||||||
|
|
||||||
$this->form->store();
|
$this->form->store();
|
||||||
|
|
||||||
$this->dispatch('refreshDatatable');
|
|
||||||
|
|
||||||
$this->toast('Pegawai berhasil ditambahkan.');
|
$this->toast('Pegawai berhasil ditambahkan.');
|
||||||
|
|
||||||
$this->redirectRoute('studio.master.user.index', navigate: true);
|
$this->redirectRoute('studio.master.user.index', navigate: true);
|
||||||
|
|||||||
@ -33,8 +33,6 @@ public function save()
|
|||||||
|
|
||||||
$this->form->update();
|
$this->form->update();
|
||||||
|
|
||||||
$this->dispatch('refreshDatatable');
|
|
||||||
|
|
||||||
$this->toast('Pegawai berhasil diperbarui.');
|
$this->toast('Pegawai berhasil diperbarui.');
|
||||||
|
|
||||||
$this->redirectRoute('studio.master.user.index', navigate: true);
|
$this->redirectRoute('studio.master.user.index', navigate: true);
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
namespace App\Livewire\Studio\Master\User;
|
namespace App\Livewire\Studio\Master\User;
|
||||||
|
|
||||||
|
use App\Enums\UserStatus;
|
||||||
|
use App\Models\Employee;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Traits\WithConfirmation;
|
use App\Traits\WithConfirmation;
|
||||||
use App\Traits\WithToast;
|
use App\Traits\WithToast;
|
||||||
@ -14,12 +16,69 @@ class Index extends Component
|
|||||||
{
|
{
|
||||||
use WithConfirmation, WithToast;
|
use WithConfirmation, WithToast;
|
||||||
|
|
||||||
|
public array $employees = [];
|
||||||
|
|
||||||
|
public array $status = [];
|
||||||
|
|
||||||
|
public function mount()
|
||||||
|
{
|
||||||
|
$this->loadEmployees();
|
||||||
|
|
||||||
|
$this->status = UserStatus::cases();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function loadEmployees()
|
||||||
|
{
|
||||||
|
$this->employees = Employee::with(['user', 'user.referralCode', 'user.outlets'])
|
||||||
|
->when(! empty($this->status), fn ($query) => $query->whereHas('user', fn ($q) => $q->whereIn('status', $this->status)))
|
||||||
|
->latest()
|
||||||
|
->get()
|
||||||
|
->map(fn ($employee) => [
|
||||||
|
'full_name' => $employee->full_name,
|
||||||
|
'code' => $employee->code,
|
||||||
|
'phone_number' => $employee->phone_number,
|
||||||
|
'address' => $employee->address,
|
||||||
|
'base_salary' => currency($employee->base_salary, 'Rp'),
|
||||||
|
'gender' => [
|
||||||
|
'label' => $employee->gender->label(),
|
||||||
|
'color' => $employee->gender->color(),
|
||||||
|
],
|
||||||
|
'birthdate' => formatDate($employee->birthdate),
|
||||||
|
'birth_age' => getAge($employee->birthdate),
|
||||||
|
'user' => [
|
||||||
|
'id' => $employee->user?->id,
|
||||||
|
'hash' => $employee->user?->hash,
|
||||||
|
'email' => $employee->user?->email,
|
||||||
|
'username' => $employee->user?->username,
|
||||||
|
'status' => [
|
||||||
|
'gradient' => $employee->user?->status->gradient(),
|
||||||
|
],
|
||||||
|
'hire_date' => formatDate($employee->user?->hire_date),
|
||||||
|
'hire_ago' => timeAgo($employee->user?->hire_date),
|
||||||
|
'resign_date' => formatDate($employee->user?->resign_date),
|
||||||
|
'resign_ago' => timeAgo($employee->user?->resign_date),
|
||||||
|
],
|
||||||
|
'referral_code' => $employee->user?->referralCode?->code,
|
||||||
|
'outlets' => $employee->user?->outlets?->pluck('name')->toArray() ?? [],
|
||||||
|
])
|
||||||
|
->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updatedStatus()
|
||||||
|
{
|
||||||
|
if (empty($this->status)) {
|
||||||
|
$this->employees = [];
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->loadEmployees();
|
||||||
|
}
|
||||||
|
|
||||||
public function delete(User $user)
|
public function delete(User $user)
|
||||||
{
|
{
|
||||||
$user->delete();
|
$user->delete();
|
||||||
|
|
||||||
$this->dispatch('refreshDatatable');
|
|
||||||
|
|
||||||
$this->toast('Pegawai berhasil dihapus.');
|
$this->toast('Pegawai berhasil dihapus.');
|
||||||
|
|
||||||
Flux::modals()->close();
|
Flux::modals()->close();
|
||||||
|
|||||||
18
database/factories/ReferralCodeFactory.php
Normal file
18
database/factories/ReferralCodeFactory.php
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Factories;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\ReferralCode>
|
||||||
|
*/
|
||||||
|
class ReferralCodeFactory extends Factory
|
||||||
|
{
|
||||||
|
public function definition(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'code' => $this->faker->unique()->randomNumber(6),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -3,18 +3,185 @@
|
|||||||
<div>
|
<div>
|
||||||
<flux:heading size="xl">{{ $pageTitle }}</flux:heading>
|
<flux:heading size="xl">{{ $pageTitle }}</flux:heading>
|
||||||
</div>
|
</div>
|
||||||
@if (auth()->user()->can('create user'))
|
@can('create user')
|
||||||
<div>
|
<div>
|
||||||
<flux:button href="{{ route('studio.master.user.create') }}" variant="primary" wire:navigate.hover
|
<flux:button href="{{ route('studio.master.user.create') }}" variant="primary" wire:navigate.hover
|
||||||
class="text-sm">
|
class="text-sm">
|
||||||
Tambah
|
Tambah
|
||||||
</flux:button>
|
</flux:button>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endcan
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-6">
|
<div class="mt-6">
|
||||||
<livewire:datatable.studio.master.users-table />
|
<div class="mb-6 grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||||
|
<flux:card class="flex flex-col justify-center">
|
||||||
|
<div class="flex items-center gap-4 flex-wrap">
|
||||||
|
<span class="text-sm font-semibold text-gray-700 dark:text-gray-300">Status:</span>
|
||||||
|
@foreach (\App\Enums\UserStatus::cases() as $status)
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<flux:badge color="{{ $status->color() }}" size="sm">
|
||||||
|
{{ $status->label() }}
|
||||||
|
</flux:badge>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
</flux:card>
|
||||||
|
|
||||||
|
<flux:card>
|
||||||
|
<flux:checkbox.group wire:model.live="status" variant="buttons">
|
||||||
|
@foreach (\App\Enums\UserStatus::cases() as $status)
|
||||||
|
<flux:checkbox value="{{ $status->value }}" label="{{ $status->label() }}" />
|
||||||
|
@endforeach
|
||||||
|
</flux:checkbox.group>
|
||||||
|
</flux:card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
|
||||||
|
@foreach ($employees as $employee)
|
||||||
|
<flux:card
|
||||||
|
class="relative space-y-4 transition-transform duration-300 ease-out hover:-translate-y-1 hover:shadow-lg">
|
||||||
|
<div
|
||||||
|
class="flex flex-col items-center text-center mb-4 p-4 rounded-t-lg {{ $employee['user']['status']['gradient'] }}">
|
||||||
|
<flux:avatar circle size="xl" initials="{{ substr($employee['full_name'], 0, 2) }}"
|
||||||
|
class="mb-3" />
|
||||||
|
<flux:heading size="lg" class="mb-1 text-white">{{ $employee['full_name'] }}
|
||||||
|
</flux:heading>
|
||||||
|
<div class="text-sm text-white/80 mb-2">{{ $employee['code'] }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<flux:separator></flux:separator>
|
||||||
|
|
||||||
|
<div class="space-y-3">
|
||||||
|
<div>
|
||||||
|
<flux:heading class="uppercase font-semibold text-gray-500 dark:text-gray-400 ">Kredensial
|
||||||
|
</flux:heading>
|
||||||
|
<div class="flex items-start gap-2 text-sm">
|
||||||
|
<flux:icon.envelope
|
||||||
|
class="w-4 h-4 text-gray-500 dark:text-white flex-shrink-0 mt-0.5" />
|
||||||
|
<div class="text-gray-700 dark:text-gray-300">
|
||||||
|
<flux:text>{{ $employee['user']['email'] }}</flux:text>
|
||||||
|
<flux:text class="text-xs">{{ $employee['user']['username'] }}</flux:text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<flux:heading class="uppercase font-semibold text-gray-500 dark:text-gray-400 ">Telepon
|
||||||
|
</flux:heading>
|
||||||
|
<div class="flex items-center gap-2 text-sm">
|
||||||
|
<flux:icon.phone class="w-4 h-4 text-gray-500 dark:text-white" />
|
||||||
|
<flux:text>{{ $employee['phone_number'] }}</flux:text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<flux:heading class="uppercase font-semibold text-gray-500 dark:text-gray-400 ">Alamat
|
||||||
|
</flux:heading>
|
||||||
|
<div class="flex items-start gap-2 text-sm">
|
||||||
|
<flux:icon.map-pin class="w-4 h-4 text-gray-500 dark:text-white flex-shrink-0 mt-0.5" />
|
||||||
|
<flux:text>{{ $employee['address'] }}</flux:text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-2 gap-3">
|
||||||
|
<div>
|
||||||
|
<flux:heading class="uppercase font-semibold text-gray-500 dark:text-gray-400 ">Jenis
|
||||||
|
Kelamin</flux:heading>
|
||||||
|
<flux:badge color="{{ $employee['gender']['color'] }}" size="sm">
|
||||||
|
{{ $employee['gender']['label'] }}
|
||||||
|
</flux:badge>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<flux:heading class="uppercase font-semibold text-gray-500 dark:text-gray-400 ">Gaji
|
||||||
|
</flux:heading>
|
||||||
|
<flux:text>{{ $employee['base_salary'] }}</flux:text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if ($employee['referral_code'])
|
||||||
|
<div>
|
||||||
|
<flux:heading class="uppercase font-semibold text-gray-500 dark:text-gray-400 ">Kode
|
||||||
|
Referral</flux:heading>
|
||||||
|
<flux:badge color="zinc" size="sm">{{ $employee['referral_code'] }}</flux:badge>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
@if (count($employee['outlets']) > 0)
|
||||||
|
<div>
|
||||||
|
<flux:heading class="uppercase font-semibold text-gray-500 dark:text-gray-400 ">Outlet
|
||||||
|
</flux:heading>
|
||||||
|
<div class="flex flex-wrap gap-2">
|
||||||
|
@foreach ($employee['outlets'] as $outlet)
|
||||||
|
<span class="text-xs text-gray-400 border border-gray-400 rounded px-2 py-1">
|
||||||
|
{{ $outlet }}
|
||||||
|
</span>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<flux:heading class="uppercase font-semibold text-gray-500 dark:text-gray-400 ">
|
||||||
|
Informasi
|
||||||
|
Tanggal</flux:heading>
|
||||||
|
<div class="bg-gray-50 dark:bg-gray-800 rounded-lg p-3">
|
||||||
|
<div class="flex flex-col gap-2 text-xs">
|
||||||
|
<div>
|
||||||
|
<span class="font-medium text-gray-900 dark:text-white">Tgl Lahir:</span>
|
||||||
|
<span
|
||||||
|
class="text-gray-700 dark:text-gray-300">{{ $employee['birthdate'] }}</span>
|
||||||
|
<span class="text-gray-500">({{ $employee['birth_age'] }})</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span class="font-medium text-gray-900 dark:text-white">Tgl
|
||||||
|
Bergabung:</span>
|
||||||
|
<span
|
||||||
|
class="text-gray-700 dark:text-gray-300">{{ $employee['user']['hire_date'] }}</span>
|
||||||
|
<span class="text-gray-500">({{ $employee['user']['hire_ago'] }})</span>
|
||||||
|
</div>
|
||||||
|
@if ($employee['user']['resign_date'])
|
||||||
|
<div>
|
||||||
|
<span class="font-medium text-gray-900 dark:text-white">Tgl
|
||||||
|
Resign:</span>
|
||||||
|
<span
|
||||||
|
class="text-gray-700 dark:text-gray-300">{{ $employee['user']['resign_date'] }}</span>
|
||||||
|
<span class="text-gray-500">({{ $employee['user']['resign_ago'] }})</span>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@canany(['update user', 'delete user'])
|
||||||
|
@if (auth()->id() !== $employee['user']['id'])
|
||||||
|
<flux:separator></flux:separator>
|
||||||
|
<div class="flex gap-2 pt-2">
|
||||||
|
@can('update user')
|
||||||
|
<flux:tooltip content="Ubah">
|
||||||
|
<flux:button href="{{ route('studio.master.user.edit', $employee['user']['hash']) }}"
|
||||||
|
wire:navigate.hover variant="primary" color="yellow" icon="pencil-square"
|
||||||
|
size="sm"></flux:button>
|
||||||
|
</flux:tooltip>
|
||||||
|
@endcan
|
||||||
|
|
||||||
|
@can('delete user')
|
||||||
|
<flux:modal.trigger name="delete">
|
||||||
|
<flux:tooltip content="Hapus">
|
||||||
|
<flux:button variant="danger" icon="trash" size="sm"
|
||||||
|
wire:click="$dispatch('fn:confirmDelete', {id: '{{ $employee['user']['hash'] }}'})">
|
||||||
|
</flux:button>
|
||||||
|
</flux:tooltip>
|
||||||
|
</flux:modal.trigger>
|
||||||
|
@endcan
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
@endcanany
|
||||||
|
</flux:card>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@include('components.confirmation.delete')
|
@include('components.confirmation.delete')
|
||||||
|
|||||||
@ -1,28 +1,74 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use App\Enums\UserStatus;
|
||||||
use App\Livewire\Studio\Master\User\Create;
|
use App\Livewire\Studio\Master\User\Create;
|
||||||
use App\Livewire\Studio\Master\User\Index;
|
use App\Livewire\Studio\Master\User\Index;
|
||||||
use App\Models\Employee;
|
use App\Models\Employee;
|
||||||
|
use App\Models\Outlet;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Illuminate\Support\Facades\Gate;
|
||||||
use Livewire\Livewire;
|
use Livewire\Livewire;
|
||||||
|
use Spatie\Permission\Models\Permission;
|
||||||
|
|
||||||
uses(RefreshDatabase::class);
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
it('renders successfully', function () {
|
beforeEach(function () {
|
||||||
Livewire::test(Create::class)
|
$this->user = User::factory()->create();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders create user page', function () {
|
||||||
|
Livewire::actingAs($this->user)
|
||||||
|
->test(Create::class)
|
||||||
->assertViewIs('livewire.studio.master.user.form')
|
->assertViewIs('livewire.studio.master.user.form')
|
||||||
->assertViewHas('pageTitle', 'Tambah Pegawai');
|
->assertViewHas('pageTitle', 'Tambah Pegawai');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can create an user', function () {
|
it('mounts outlets correctly', function () {
|
||||||
$user = User::factory()->raw();
|
$outlets = Outlet::factory()->count(3)->create();
|
||||||
|
|
||||||
$employee = Employee::factory()->raw();
|
$component = Livewire::actingAs($this->user)
|
||||||
|
->test(Create::class);
|
||||||
|
|
||||||
$data = array_merge($user, $employee);
|
expect($component->outlets)->toHaveCount(3);
|
||||||
|
foreach ($outlets as $outlet) {
|
||||||
|
expect($component->outlets)->toHaveKey($outlet->id);
|
||||||
|
expect($component->outlets[$outlet->id])->toBe($outlet->name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Livewire::test(Create::class)
|
it('prevents creation when unauthorized', function () {
|
||||||
|
Gate::define('create user', fn () => false);
|
||||||
|
|
||||||
|
Livewire::actingAs($this->user)
|
||||||
|
->test(Create::class)
|
||||||
|
->call('save')
|
||||||
|
->assertForbidden();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('saves outlet successfully when authorized', function () {
|
||||||
|
$permission = Permission::create(['name' => 'create user']);
|
||||||
|
$this->user->givePermissionTo($permission);
|
||||||
|
|
||||||
|
$outlets = Outlet::factory()->count(2)->create();
|
||||||
|
|
||||||
|
$userRaw = User::factory()->make()->toArray();
|
||||||
|
$employeeRaw = Employee::factory()->make()->toArray();
|
||||||
|
|
||||||
|
$data = array_merge($userRaw, [
|
||||||
|
'full_name' => $employeeRaw['full_name'],
|
||||||
|
'phone_number' => $employeeRaw['phone_number'],
|
||||||
|
'base_salary' => $employeeRaw['base_salary'],
|
||||||
|
'birthdate' => $employeeRaw['birthdate'],
|
||||||
|
'hire_date' => $employeeRaw['hire_date'],
|
||||||
|
'address' => $employeeRaw['address'],
|
||||||
|
'gender' => $employeeRaw['gender'],
|
||||||
|
'status' => UserStatus::ACTIVE->value,
|
||||||
|
'outlet_ids' => $outlets->pluck('id')->toArray(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
Livewire::actingAs($this->user)
|
||||||
|
->test(Create::class)
|
||||||
->set('form.full_name', $data['full_name'])
|
->set('form.full_name', $data['full_name'])
|
||||||
->set('form.username', $data['username'])
|
->set('form.username', $data['username'])
|
||||||
->set('form.email', $data['email'])
|
->set('form.email', $data['email'])
|
||||||
@ -31,15 +77,26 @@
|
|||||||
->set('form.birthdate', $data['birthdate'])
|
->set('form.birthdate', $data['birthdate'])
|
||||||
->set('form.hire_date', $data['hire_date'])
|
->set('form.hire_date', $data['hire_date'])
|
||||||
->set('form.address', $data['address'])
|
->set('form.address', $data['address'])
|
||||||
->set('form.gender', $data['gender']->value)
|
->set('form.gender', $data['gender'])
|
||||||
->set('form.status', $data['status']->value)
|
->set('form.status', $data['status'])
|
||||||
|
->set('form.outlet_ids', $data['outlet_ids'])
|
||||||
->call('save')
|
->call('save')
|
||||||
->assertHasNoErrors()
|
->assertHasNoErrors()
|
||||||
->assertDispatched('refreshDatatable')
|
|
||||||
->assertRedirect(Index::class);
|
->assertRedirect(Index::class);
|
||||||
|
|
||||||
|
$user = User::where('email', $data['email'])->first();
|
||||||
|
|
||||||
$this->assertDatabaseHas('users', [
|
$this->assertDatabaseHas('users', [
|
||||||
'email' => $data['email'],
|
'id' => $user->id,
|
||||||
'username' => $data['username'],
|
'username' => $data['username'],
|
||||||
|
'email' => $data['email'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$this->assertDatabaseHas('employees', [
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'full_name' => $data['full_name'],
|
||||||
|
'phone_number' => $data['phone_number'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect($user->outlets->pluck('id')->toArray())->toMatchArray($data['outlet_ids']);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,53 +1,114 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use App\Enums\Gender;
|
||||||
|
use App\Enums\UserStatus;
|
||||||
use App\Livewire\Studio\Master\User\Edit;
|
use App\Livewire\Studio\Master\User\Edit;
|
||||||
use App\Livewire\Studio\Master\User\Index;
|
use App\Livewire\Studio\Master\User\Index;
|
||||||
use App\Models\Employee;
|
use App\Models\Employee;
|
||||||
|
use App\Models\Outlet;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Illuminate\Support\Facades\Gate;
|
||||||
use Livewire\Livewire;
|
use Livewire\Livewire;
|
||||||
|
use Spatie\Permission\Models\Permission;
|
||||||
|
|
||||||
uses(RefreshDatabase::class);
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
it('renders successfully', function () {
|
beforeEach(function () {
|
||||||
$user = User::factory()
|
$this->admin = User::factory()->create();
|
||||||
->has(Employee::factory())
|
});
|
||||||
->create();
|
|
||||||
|
|
||||||
Livewire::test(Edit::class, ['user' => $user])
|
function mountEditUser(User $admin, User $user)
|
||||||
|
{
|
||||||
|
return Livewire::actingAs($admin)->test(Edit::class, ['user' => $user]);
|
||||||
|
}
|
||||||
|
|
||||||
|
it('renders page successfully', function () {
|
||||||
|
$userToEdit = User::factory()->has(Employee::factory())->create();
|
||||||
|
|
||||||
|
mountEditUser($this->admin, $userToEdit)
|
||||||
->assertViewIs('livewire.studio.master.user.form')
|
->assertViewIs('livewire.studio.master.user.form')
|
||||||
->assertViewHas('pageTitle', 'Ubah Pegawai');
|
->assertViewHas('pageTitle', 'Ubah Pegawai');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can update an user', function () {
|
it('mounts form with existing user data', function () {
|
||||||
$existingUser = User::factory()
|
$userToEdit = User::factory()->has(Employee::factory())->create();
|
||||||
->has(Employee::factory())
|
|
||||||
->create();
|
|
||||||
|
|
||||||
$user = User::factory()->raw();
|
$component = mountEditUser($this->admin, $userToEdit);
|
||||||
$employee = Employee::factory()->raw();
|
|
||||||
|
|
||||||
$data = array_merge($user, $employee);
|
expect($component->form->username)->toBe($userToEdit->username);
|
||||||
|
expect($component->form->email)->toBe($userToEdit->email);
|
||||||
|
expect($component->form->full_name)->toBe($userToEdit->employee->full_name);
|
||||||
|
expect($component->form->phone_number)->toBe($userToEdit->employee->phone_number);
|
||||||
|
expect($component->form->outlet_ids)->toBe($userToEdit->outlets->pluck('id')->toArray());
|
||||||
|
});
|
||||||
|
|
||||||
Livewire::test(Edit::class, ['user' => $existingUser])
|
it('prevents update when user is unauthorized', function () {
|
||||||
->set('form.full_name', $data['full_name'])
|
$userToEdit = User::factory()->has(Employee::factory())->create();
|
||||||
|
Gate::define('update user', fn () => false);
|
||||||
|
|
||||||
|
mountEditUser($this->admin, $userToEdit)
|
||||||
|
->call('save')
|
||||||
|
->assertForbidden();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('updates user successfully when authorized', function () {
|
||||||
|
$permission = Permission::create(['name' => 'update user']);
|
||||||
|
$this->admin->givePermissionTo($permission);
|
||||||
|
|
||||||
|
$userToEdit = User::factory()->has(Employee::factory())->create();
|
||||||
|
$outlets = Outlet::factory()->count(3)->create();
|
||||||
|
|
||||||
|
$userRaw = User::factory()->raw();
|
||||||
|
$employeeRaw = Employee::factory()->raw();
|
||||||
|
|
||||||
|
$data = array_merge($userRaw, $employeeRaw, [
|
||||||
|
'outlet_ids' => $outlets->pluck('id')->toArray(),
|
||||||
|
'status' => UserStatus::ACTIVE->value,
|
||||||
|
'gender' => $employeeRaw['gender'] ?? Gender::MALE->value,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$data = array_merge($data, [
|
||||||
|
'full_name' => $data['full_name'],
|
||||||
|
'phone_number' => $data['phone_number'],
|
||||||
|
'outlet_ids' => $outlets->pluck('id')->toArray(),
|
||||||
|
'status' => UserStatus::ACTIVE->value,
|
||||||
|
'gender' => Gender::MALE->value,
|
||||||
|
'birthdate' => $data['birthdate'],
|
||||||
|
'hire_date' => $data['hire_date'],
|
||||||
|
'address' => $data['address'],
|
||||||
|
'base_salary' => $data['base_salary'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
mountEditUser($this->admin, $userToEdit)
|
||||||
->set('form.username', $data['username'])
|
->set('form.username', $data['username'])
|
||||||
->set('form.email', $data['email'])
|
->set('form.email', $data['email'])
|
||||||
|
->set('form.full_name', $data['full_name'])
|
||||||
->set('form.phone_number', $data['phone_number'])
|
->set('form.phone_number', $data['phone_number'])
|
||||||
->set('form.base_salary', $data['base_salary'])
|
->set('form.address', $data['address'])
|
||||||
->set('form.birthdate', $data['birthdate'])
|
->set('form.birthdate', $data['birthdate'])
|
||||||
->set('form.hire_date', $data['hire_date'])
|
->set('form.hire_date', $data['hire_date'])
|
||||||
->set('form.address', $data['address'])
|
->set('form.base_salary', $data['base_salary'])
|
||||||
->set('form.gender', $data['gender']->value)
|
->set('form.gender', $data['gender'])
|
||||||
->set('form.status', $data['status']->value)
|
->set('form.status', $data['status'])
|
||||||
|
->set('form.outlet_ids', $data['outlet_ids'])
|
||||||
->call('save')
|
->call('save')
|
||||||
->assertHasNoErrors()
|
->assertHasNoErrors()
|
||||||
->assertDispatched('refreshDatatable')
|
|
||||||
->assertRedirect(Index::class);
|
->assertRedirect(Index::class);
|
||||||
|
|
||||||
|
$userToEdit->refresh();
|
||||||
|
|
||||||
$this->assertDatabaseHas('users', [
|
$this->assertDatabaseHas('users', [
|
||||||
'id' => $existingUser->id,
|
'id' => $userToEdit->id,
|
||||||
'email' => $data['email'],
|
|
||||||
'username' => $data['username'],
|
'username' => $data['username'],
|
||||||
|
'email' => $data['email'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$this->assertDatabaseHas('employees', [
|
||||||
|
'user_id' => $userToEdit->id,
|
||||||
|
'full_name' => $data['full_name'],
|
||||||
|
'phone_number' => $data['phone_number'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect($userToEdit->outlets->pluck('id')->toArray())->toBe($data['outlet_ids']);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,39 +1,68 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use App\Enums\UserStatus;
|
||||||
use App\Livewire\Studio\Master\User\Index;
|
use App\Livewire\Studio\Master\User\Index;
|
||||||
use App\Models\Employee;
|
use App\Models\Employee;
|
||||||
|
use App\Models\ReferralCode;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Livewire\Livewire;
|
use Livewire\Livewire;
|
||||||
|
|
||||||
uses(RefreshDatabase::class);
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
it('renders successfully', function () {
|
beforeEach(function () {
|
||||||
Livewire::test(Index::class)
|
$this->user = User::factory()->create();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders user index page successfully', function () {
|
||||||
|
Livewire::actingAs($this->user)
|
||||||
|
->test(Index::class)
|
||||||
->assertViewIs('livewire.studio.master.user.index')
|
->assertViewIs('livewire.studio.master.user.index')
|
||||||
->assertViewHas('pageTitle', 'Pegawai');
|
->assertViewHas('pageTitle', 'Pegawai');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('displays all users', function () {
|
it('mounts employees correctly', function () {
|
||||||
User::factory()
|
$userWithEmployee = User::factory()
|
||||||
->count(10)
|
->has(Employee::factory())
|
||||||
|
->has(ReferralCode::factory())
|
||||||
|
->create();
|
||||||
|
|
||||||
|
$employee = $userWithEmployee->employee;
|
||||||
|
|
||||||
|
Livewire::actingAs($this->user)
|
||||||
|
->test(Index::class)
|
||||||
|
->assertSee($employee->full_name)
|
||||||
|
->assertSet('employees.0.full_name', $employee->full_name)
|
||||||
|
->assertSet('employees.0.user.id', $userWithEmployee->id)
|
||||||
|
->assertSet('employees.0.referral_code', $userWithEmployee?->referralCode?->code);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('filters employees by status', function () {
|
||||||
|
$activeUser = User::factory()->create(['status' => UserStatus::ACTIVE->value]);
|
||||||
|
$inactiveUser = User::factory()->create(['status' => UserStatus::INACTIVE->value]);
|
||||||
|
|
||||||
|
$activeUser->employee()->save(Employee::factory()->make());
|
||||||
|
$inactiveUser->employee()->save(Employee::factory()->make());
|
||||||
|
|
||||||
|
$component = Livewire::actingAs($this->user)
|
||||||
|
->test(Index::class);
|
||||||
|
|
||||||
|
$component->set('status', [UserStatus::ACTIVE->value]);
|
||||||
|
|
||||||
|
$employees = $component->employees;
|
||||||
|
expect(count($employees))->toBe(1);
|
||||||
|
expect($employees[0]['user']['id'])->toBe($activeUser->id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('deletes a user successfully', function () {
|
||||||
|
$userToDelete = User::factory()
|
||||||
->has(Employee::factory())
|
->has(Employee::factory())
|
||||||
->create();
|
->create();
|
||||||
|
|
||||||
$user = User::first();
|
Livewire::actingAs($this->user)
|
||||||
|
->test(Index::class)
|
||||||
|
->call('delete', $userToDelete)
|
||||||
|
->assertHasNoErrors();
|
||||||
|
|
||||||
Livewire::test(Index::class)->assertSee($user->email);
|
$this->assertSoftDeleted('users', ['id' => $userToDelete->id]);
|
||||||
});
|
|
||||||
|
|
||||||
it('can delete an user', function () {
|
|
||||||
$user = User::factory()
|
|
||||||
->has(Employee::factory())
|
|
||||||
->create();
|
|
||||||
|
|
||||||
Livewire::test(Index::class)
|
|
||||||
->call('delete', $user)
|
|
||||||
->assertHasNoErrors()
|
|
||||||
->assertDispatched('refreshDatatable');
|
|
||||||
|
|
||||||
expect(User::count())->toBe(0);
|
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user