CluPilotCloud/app/Livewire/Admin/CustomerDetail.php

144 lines
4.9 KiB
PHP

<?php
namespace App\Livewire\Admin;
use App\Mail\OperatorMessageMail;
use App\Models\Customer;
use App\Models\SentMail;
use App\Models\SupportRequest;
use Illuminate\Support\Facades\Mail;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Validate;
use Livewire\Component;
/**
* One customer, everything about them, and a box to write to them.
*
* The console had a customer LIST and nothing behind it: an operator answering
* a question had the name in one window, the mail client in another, and no
* record anywhere afterwards that an answer had been given at all. So the
* conversation happened outside the system that knows who the customer is.
*
* This page is the other half. What they bought, what they asked, what we sent
* them — and a field to write the next message, which goes out as mail from the
* support mailbox and is written into the register on its way.
*
* Reading their INCOMING mail is not here yet. That needs the mailbox itself
* polled over IMAP, which is a mail-server credential and a dependency; until
* then the requests a customer files through the portal appear here in full,
* and a mail they write instead still has to be read in the mail client.
*/
#[Layout('layouts.admin')]
class CustomerDetail extends Component
{
public Customer $customer;
/** What the operator is writing. Empty is the normal state of this page. */
#[Validate('required|string|max:200')]
public string $subject = '';
#[Validate('required|string|min:3|max:5000')]
public string $body = '';
/**
* The request being answered, if this message is an answer to one.
*
* Answering marks it answered — which is the whole reason to answer from
* here rather than from a mail client, where the request stays open for
* ever because nothing told it otherwise.
*/
public ?string $answering = null;
public function mount(string $uuid): void
{
$this->authorize('customers.manage');
$this->customer = Customer::query()->where('uuid', $uuid)->firstOrFail();
}
/**
* Start an answer to one request: subject prefilled, the request remembered.
*/
public function answer(string $uuid): void
{
$this->authorize('customers.manage');
$request = $this->customer->supportRequests()->where('uuid', $uuid)->first();
if ($request === null) {
return;
}
$this->answering = $request->uuid;
$this->subject = __('customer_message.re', ['subject' => $request->subject]);
$this->body = '';
}
public function cancelAnswer(): void
{
$this->answering = null;
$this->subject = '';
$this->body = '';
}
/**
* Send it, record it, and close the request it answers.
*
* The register row is written here rather than left to the MessageSent
* listener, because this is the one mail whose BODY is worth keeping: an
* operator typed it, and "what exactly did I write to them in March" is a
* question the mail server's log cannot answer either.
*/
public function send(): void
{
$this->authorize('customers.manage');
$data = $this->validate();
Mail::to($this->customer->email)->send(
new OperatorMessageMail($this->customer, $data['subject'], $data['body'])
);
// The listener has already written a row for the delivery — it fires on
// every mail this application sends. This adds what only the console
// knows: that a person wrote it, and what they wrote.
SentMail::query()
->where('customer_id', $this->customer->id)
->where('to', $this->customer->email)
->latest('id')
->limit(1)
->update(['from_operator' => true, 'body' => $data['body']]);
if ($this->answering !== null) {
$this->customer->supportRequests()
->where('uuid', $this->answering)
->update(['status' => 'answered', 'answered_at' => now()]);
}
$this->cancelAnswer();
$this->dispatch('notify', message: __('customer_message.sent'));
}
public function render()
{
$this->authorize('customers.manage');
return view('livewire.admin.customer-detail', [
'requests' => $this->customer->supportRequests()
->with('instance')
->latest('id')
->get(),
// The whole conversation as it left here, newest first. Capped:
// this is a page, not an export, and a customer of three years has
// a few hundred rows nobody scrolls.
'mails' => SentMail::query()
->where('customer_id', $this->customer->id)
->latest('sent_at')
->limit(50)
->get(),
'instance' => $this->customer->instances()->latest('id')->first(),
'subscription' => $this->customer->subscriptions()->latest('id')->first(),
]);
}
}