109 lines
3.1 KiB
PHP
109 lines
3.1 KiB
PHP
<?php
|
|
|
|
namespace App\Livewire;
|
|
|
|
use App\Livewire\Concerns\ResolvesCustomer;
|
|
use App\Models\Seat;
|
|
use LivewireUI\Modal\ModalComponent;
|
|
|
|
/**
|
|
* Edit one seat, in a modal.
|
|
*
|
|
* Editing used to happen in the row itself. It worked and it looked wrong: the
|
|
* row grew, the columns beside it jumped, and a table half in edit mode reads
|
|
* as a rendering fault rather than as a form. Anything with fields of its own
|
|
* gets a modal — see R20.
|
|
*
|
|
* A modal is reachable without passing the page's route middleware, so the
|
|
* customer is resolved here rather than trusted from the caller: the uuid comes
|
|
* from the browser and must never reach across customers.
|
|
*/
|
|
class EditSeat extends ModalComponent
|
|
{
|
|
use ResolvesCustomer;
|
|
|
|
public string $uuid = '';
|
|
|
|
public string $name = '';
|
|
|
|
public string $email = '';
|
|
|
|
/** Owners cannot be suspended or removed; everyone can be renamed. */
|
|
public bool $isOwner = false;
|
|
|
|
/**
|
|
* Whether the address may still be corrected.
|
|
*
|
|
* Only while the invitation is in flight. Once someone has accepted, the
|
|
* address IS the person: editing it would hand one employee's access to
|
|
* another with nobody told — a transfer of access wearing the clothes of a
|
|
* rename.
|
|
*/
|
|
public bool $addressEditable = false;
|
|
|
|
public function mount(string $uuid): void
|
|
{
|
|
$seat = $this->seat($uuid);
|
|
|
|
abort_if($seat === null, 404);
|
|
|
|
$this->uuid = $uuid;
|
|
$this->name = (string) $seat->name;
|
|
$this->email = (string) $seat->email;
|
|
$this->isOwner = $seat->role === 'owner';
|
|
$this->addressEditable = $seat->status === 'invited';
|
|
}
|
|
|
|
public function save()
|
|
{
|
|
$customer = $this->requireCustomer();
|
|
$seat = $this->seat($this->uuid);
|
|
|
|
if ($customer === null || $seat === null) {
|
|
return $this->closeModal();
|
|
}
|
|
|
|
$rules = ['name' => 'nullable|string|max:255'];
|
|
|
|
// Re-read from the record, never from the hydrated property: a forged
|
|
// addressEditable would otherwise open the address of an accepted seat.
|
|
if ($seat->status === 'invited') {
|
|
$rules['email'] = 'required|email|max:255';
|
|
}
|
|
|
|
$data = $this->validate($rules);
|
|
|
|
$changes = ['name' => trim($data['name'] ?? '') ?: null];
|
|
|
|
if ($seat->status === 'invited') {
|
|
$address = trim($data['email']);
|
|
|
|
if ($address !== $seat->email) {
|
|
if ($customer->seats()->where('email', $address)->whereKeyNot($seat->id)->exists()) {
|
|
$this->addError('email', __('users.duplicate'));
|
|
|
|
return null;
|
|
}
|
|
|
|
$changes['email'] = $address;
|
|
}
|
|
}
|
|
|
|
$seat->update($changes);
|
|
|
|
$this->dispatch('notify', message: __('users.saved'));
|
|
|
|
return $this->redirectRoute('users', navigate: true);
|
|
}
|
|
|
|
private function seat(string $uuid): ?Seat
|
|
{
|
|
return $this->customer()?->seats()->where('uuid', $uuid)->first();
|
|
}
|
|
|
|
public function render()
|
|
{
|
|
return view('livewire.edit-seat');
|
|
}
|
|
}
|