CluPilotCloud/app/Livewire/Admin/Inbox.php

133 lines
4.3 KiB
PHP

<?php
namespace App\Livewire\Admin;
use App\Models\Customer;
use App\Models\InboundMail;
use App\Services\Mail\IngestInboundMail;
use App\Services\Mail\InboundMailbox;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Url;
use Livewire\Component;
/**
* What customers wrote to us.
*
* The half that was missing: outgoing mail has a register, portal requests have
* a page, and a customer who simply replied by mail was invisible here — the
* question sat in a mail client on somebody's desktop and the console, which
* knows who they are and what they bought, knew nothing about it.
*
* Unassigned mail is shown FIRST and never hidden. A message from an address
* nobody recognises is a new enquiry or a customer writing from a private
* account, and it is exactly the one that must not be missed.
*
* Attachments are names and sizes. The files are left on the mail server on
* purpose — storing whatever a stranger chooses to send would make this a
* malware store with a console in front of it.
*/
#[Layout('layouts.admin')]
class Inbox extends Component
{
/** Show what has been dealt with as well. */
#[Url(except: false)]
public bool $archived = false;
public function mount(): void
{
$this->authorize('customers.manage');
}
/**
* Look now rather than at the next tick.
*
* The scheduler polls every couple of minutes; this is for the operator on
* the phone with somebody who says "ich habe es gerade geschickt".
*/
public function fetchNow(IngestInboundMail $ingest): void
{
$this->authorize('customers.manage');
$taken = $ingest();
$this->dispatch('notify', message: $taken === 0
? __('inbox.nothing_new')
: trans_choice('inbox.fetched', $taken, ['count' => $taken]));
}
/** Put one away. It stays in the mailbox; only this list forgets it. */
public function archive(string $uuid): void
{
$this->authorize('customers.manage');
InboundMail::query()->where('uuid', $uuid)->update([
'archived_at' => now(),
'read_at' => now(),
]);
}
public function unarchive(string $uuid): void
{
$this->authorize('customers.manage');
InboundMail::query()->where('uuid', $uuid)->update(['archived_at' => null]);
}
/**
* Attach a mail nobody could place to a customer.
*
* The address decides automatically and this is the manual override: a
* customer who writes from their private account is one an operator
* recognises and the matcher never will.
*/
public function assign(string $uuid, string $customerUuid): void
{
$this->authorize('customers.manage');
$mail = InboundMail::query()->where('uuid', $uuid)->first();
if ($mail === null) {
return;
}
if ($customerUuid === '') {
$mail->update(['customer_id' => null]);
return;
}
$customer = Customer::query()->where('uuid', $customerUuid)->first();
if ($customer !== null) {
$mail->update(['customer_id' => $customer->id]);
$this->dispatch('notify', message: __('inbox.assigned', ['name' => $customer->name]));
}
}
public function render()
{
$this->authorize('customers.manage');
$mails = InboundMail::query()
->with('customer')
->when(! $this->archived, fn ($q) => $q->open())
->when($this->archived, fn ($q) => $q->whereNotNull('archived_at'))
// Unassigned first: the mail nobody has placed is the one that gets
// forgotten, and sorting it under the rest is how it stays that way.
->orderByRaw('customer_id IS NULL DESC')
->latest('received_at')
->limit(100)
->get();
return view('livewire.admin.inbox', [
'mails' => $mails,
'customers' => Customer::query()->whereNull('closed_at')->orderBy('name')->get(['uuid', 'name', 'email']),
'openCount' => InboundMail::query()->open()->count(),
// Said on the page rather than left as an empty list nobody can
// explain: a mailbox that was never configured looks exactly like a
// mailbox with no mail in it.
'configured' => app(InboundMailbox::class)->isConfigured(),
]);
}
}