Replace employee user_ref text field with Ladill user select.
Deploy Ladill Frontdesk / deploy (push) Successful in 40s
Deploy Ladill Frontdesk / deploy (push) Successful in 40s
Populate the dropdown from team members and linked hosts, show name and email labels, and block linking a user already tied to another employee. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -8,11 +8,14 @@ use App\Models\Branch;
|
|||||||
use App\Models\Employee;
|
use App\Models\Employee;
|
||||||
use App\Models\EmployeePresenceEvent;
|
use App\Models\EmployeePresenceEvent;
|
||||||
use App\Models\Host;
|
use App\Models\Host;
|
||||||
|
use App\Models\Member;
|
||||||
|
use App\Models\User;
|
||||||
use App\Services\Frontdesk\EmployeePresenceService;
|
use App\Services\Frontdesk\EmployeePresenceService;
|
||||||
use App\Services\Frontdesk\QrCodeService;
|
use App\Services\Frontdesk\QrCodeService;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Carbon;
|
use Illuminate\Support\Carbon;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||||
|
|
||||||
@@ -44,6 +47,7 @@ class EmployeeController extends Controller
|
|||||||
'organization' => $organization,
|
'organization' => $organization,
|
||||||
'branches' => $this->branches($request, $organization->id),
|
'branches' => $this->branches($request, $organization->id),
|
||||||
'hosts' => $this->hosts($request, $organization->id),
|
'hosts' => $this->hosts($request, $organization->id),
|
||||||
|
'linkableUsers' => $this->linkableUsers($request, $organization->id),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +56,7 @@ class EmployeeController extends Controller
|
|||||||
$this->authorizeAbility($request, 'employees.manage');
|
$this->authorizeAbility($request, 'employees.manage');
|
||||||
$organization = $this->organization($request);
|
$organization = $this->organization($request);
|
||||||
|
|
||||||
$validated = $this->validatedEmployee($request);
|
$validated = $this->validatedEmployee($request, $organization->id);
|
||||||
$pin = $validated['pin'];
|
$pin = $validated['pin'];
|
||||||
unset($validated['pin']);
|
unset($validated['pin']);
|
||||||
|
|
||||||
@@ -77,6 +81,7 @@ class EmployeeController extends Controller
|
|||||||
'employee' => $employee->load('presence'),
|
'employee' => $employee->load('presence'),
|
||||||
'branches' => $this->branches($request, $employee->organization_id),
|
'branches' => $this->branches($request, $employee->organization_id),
|
||||||
'hosts' => $this->hosts($request, $employee->organization_id),
|
'hosts' => $this->hosts($request, $employee->organization_id),
|
||||||
|
'linkableUsers' => $this->linkableUsers($request, $employee->organization_id, $employee),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,7 +90,7 @@ class EmployeeController extends Controller
|
|||||||
$this->authorizeAbility($request, 'employees.manage');
|
$this->authorizeAbility($request, 'employees.manage');
|
||||||
$this->authorizeOwner($request, $employee);
|
$this->authorizeOwner($request, $employee);
|
||||||
|
|
||||||
$validated = $this->validatedEmployee($request, updating: true);
|
$validated = $this->validatedEmployee($request, $employee->organization_id, updating: true, employee: $employee);
|
||||||
|
|
||||||
$payload = $this->employeePayload($validated);
|
$payload = $this->employeePayload($validated);
|
||||||
if (! empty($validated['pin'])) {
|
if (! empty($validated['pin'])) {
|
||||||
@@ -277,12 +282,17 @@ class EmployeeController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @return array<string, mixed> */
|
/** @return array<string, mixed> */
|
||||||
protected function validatedEmployee(Request $request, bool $updating = false): array
|
protected function validatedEmployee(Request $request, int $organizationId, bool $updating = false, ?Employee $employee = null): array
|
||||||
{
|
{
|
||||||
$pinRules = $updating
|
$pinRules = $updating
|
||||||
? ['nullable', 'string', 'regex:/^\d{4,6}$/']
|
? ['nullable', 'string', 'regex:/^\d{4,6}$/']
|
||||||
: ['required', 'string', 'regex:/^\d{4,6}$/'];
|
: ['required', 'string', 'regex:/^\d{4,6}$/'];
|
||||||
|
|
||||||
|
$linkableRefs = collect($this->linkableUsers($request, $organizationId, $employee))
|
||||||
|
->reject(fn (array $user) => $user['taken'])
|
||||||
|
->pluck('user_ref')
|
||||||
|
->all();
|
||||||
|
|
||||||
return $request->validate([
|
return $request->validate([
|
||||||
'employee_code' => ['required', 'string', 'max:32'],
|
'employee_code' => ['required', 'string', 'max:32'],
|
||||||
'full_name' => ['required', 'string', 'max:255'],
|
'full_name' => ['required', 'string', 'max:255'],
|
||||||
@@ -292,10 +302,82 @@ class EmployeeController extends Controller
|
|||||||
'pin' => $pinRules,
|
'pin' => $pinRules,
|
||||||
'branch_id' => ['nullable', 'integer'],
|
'branch_id' => ['nullable', 'integer'],
|
||||||
'host_id' => ['nullable', 'integer'],
|
'host_id' => ['nullable', 'integer'],
|
||||||
'user_ref' => ['nullable', 'string', 'max:64'],
|
'user_ref' => [
|
||||||
|
'nullable',
|
||||||
|
'string',
|
||||||
|
'max:64',
|
||||||
|
Rule::when(
|
||||||
|
filled($request->input('user_ref')),
|
||||||
|
[Rule::in($linkableRefs)]
|
||||||
|
),
|
||||||
|
],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ladill users that can be linked to an employee (team members and linked hosts).
|
||||||
|
*
|
||||||
|
* @return list<array{user_ref: string, label: string, taken: bool}>
|
||||||
|
*/
|
||||||
|
protected function linkableUsers(Request $request, int $organizationId, ?Employee $employee = null): array
|
||||||
|
{
|
||||||
|
$ownerRef = $this->ownerRef($request);
|
||||||
|
|
||||||
|
$refs = collect()
|
||||||
|
->merge(
|
||||||
|
Member::owned($ownerRef)
|
||||||
|
->where('organization_id', $organizationId)
|
||||||
|
->pluck('user_ref')
|
||||||
|
)
|
||||||
|
->merge(
|
||||||
|
Host::owned($ownerRef)
|
||||||
|
->where('organization_id', $organizationId)
|
||||||
|
->whereNotNull('user_ref')
|
||||||
|
->pluck('user_ref')
|
||||||
|
)
|
||||||
|
->merge(
|
||||||
|
Employee::owned($ownerRef)
|
||||||
|
->where('organization_id', $organizationId)
|
||||||
|
->whereNotNull('user_ref')
|
||||||
|
->pluck('user_ref')
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($employee?->user_ref) {
|
||||||
|
$refs->push($employee->user_ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
$refs = $refs->filter()->unique()->values();
|
||||||
|
|
||||||
|
$users = User::query()
|
||||||
|
->whereIn('public_id', $refs)
|
||||||
|
->get(['public_id', 'name', 'email'])
|
||||||
|
->keyBy('public_id');
|
||||||
|
|
||||||
|
$takenRefs = Employee::owned($ownerRef)
|
||||||
|
->where('organization_id', $organizationId)
|
||||||
|
->whereNotNull('user_ref')
|
||||||
|
->when($employee, fn ($query) => $query->where('id', '!=', $employee->id))
|
||||||
|
->pluck('user_ref')
|
||||||
|
->flip();
|
||||||
|
|
||||||
|
return $refs
|
||||||
|
->map(function (string $ref) use ($users, $takenRefs, $employee) {
|
||||||
|
$user = $users->get($ref);
|
||||||
|
$label = $user
|
||||||
|
? trim($user->name.' · '.$user->email)
|
||||||
|
: $ref;
|
||||||
|
|
||||||
|
return [
|
||||||
|
'user_ref' => $ref,
|
||||||
|
'label' => $label,
|
||||||
|
'taken' => $takenRefs->has($ref) && $employee?->user_ref !== $ref,
|
||||||
|
];
|
||||||
|
})
|
||||||
|
->sortBy('label', SORT_NATURAL | SORT_FLAG_CASE)
|
||||||
|
->values()
|
||||||
|
->all();
|
||||||
|
}
|
||||||
|
|
||||||
protected function employeePayload(array $validated): array
|
protected function employeePayload(array $validated): array
|
||||||
{
|
{
|
||||||
$payload = $validated;
|
$payload = $validated;
|
||||||
|
|||||||
@@ -51,10 +51,7 @@
|
|||||||
</select>
|
</select>
|
||||||
<p class="mt-1 text-xs text-slate-500">When stepped out, linked hosts show as unavailable to visitors.</p>
|
<p class="mt-1 text-xs text-slate-500">When stepped out, linked hosts show as unavailable to visitors.</p>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
@include('frontdesk.employees.partials.user-ref-select', ['selectedUserRef' => old('user_ref')])
|
||||||
<label class="block text-sm font-medium text-slate-700">Linked Ladill user (optional)</label>
|
|
||||||
<input type="text" name="user_ref" value="{{ old('user_ref') }}" placeholder="User public ID" class="mt-1 w-full rounded-lg border-slate-300 font-mono text-sm">
|
|
||||||
</div>
|
|
||||||
<button type="submit" class="btn-primary w-full">Save employee</button>
|
<button type="submit" class="btn-primary w-full">Save employee</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -49,11 +49,7 @@
|
|||||||
@endforeach
|
@endforeach
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
@include('frontdesk.employees.partials.user-ref-select', ['selectedUserRef' => old('user_ref', $employee->user_ref)])
|
||||||
<label class="block text-sm font-medium text-slate-700">Linked Ladill user (for mobile step-out)</label>
|
|
||||||
<input type="text" name="user_ref" value="{{ old('user_ref', $employee->user_ref) }}" placeholder="User public ID" class="mt-1 w-full rounded-lg border-slate-300 font-mono text-sm">
|
|
||||||
<p class="mt-1 text-xs text-slate-400">Enables the My presence portal for this employee.</p>
|
|
||||||
</div>
|
|
||||||
<label class="flex items-center gap-2 text-sm">
|
<label class="flex items-center gap-2 text-sm">
|
||||||
<input type="checkbox" name="active" value="1" @checked(old('active', $employee->active)) class="rounded border-slate-300">
|
<input type="checkbox" name="active" value="1" @checked(old('active', $employee->active)) class="rounded border-slate-300">
|
||||||
Active (can use kiosk)
|
Active (can use kiosk)
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
@php
|
||||||
|
$selectedUserRef = old('user_ref', $selectedUserRef ?? '');
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label class="block text-sm font-medium text-slate-700">Linked Ladill user (optional)</label>
|
||||||
|
<select name="user_ref" class="mt-1 w-full rounded-lg border-slate-300 text-sm">
|
||||||
|
<option value="">None</option>
|
||||||
|
@foreach ($linkableUsers as $linkableUser)
|
||||||
|
<option value="{{ $linkableUser['user_ref'] }}"
|
||||||
|
@selected($selectedUserRef === $linkableUser['user_ref'])
|
||||||
|
@disabled($linkableUser['taken'] && $selectedUserRef !== $linkableUser['user_ref'])>
|
||||||
|
{{ $linkableUser['label'] }}@if ($linkableUser['taken']) (linked to another employee) @endif
|
||||||
|
</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
@if (count($linkableUsers) === 0)
|
||||||
|
<p class="mt-1 text-xs text-amber-700">
|
||||||
|
No Ladill users available yet.
|
||||||
|
<a href="{{ route('frontdesk.members.create') }}" class="font-medium underline">Add a team member</a>
|
||||||
|
so they can be linked here.
|
||||||
|
</p>
|
||||||
|
@else
|
||||||
|
<p class="mt-1 text-xs text-slate-500">Enables the My presence portal when this person signs in with the selected Ladill account.</p>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
Reference in New Issue
Block a user