347 lines
12 KiB
PHP
347 lines
12 KiB
PHP
<?php
|
|
|
|
namespace App\Filament\Pages\Posta;
|
|
|
|
use App\Models\CommunicationMessage;
|
|
use App\Models\Stabile;
|
|
use App\Models\Ticket;
|
|
use App\Models\User;
|
|
use App\Services\Posta\ImapMailboxService;
|
|
use App\Support\StabileContext;
|
|
use BackedEnum;
|
|
use Filament\Notifications\Notification;
|
|
use Filament\Pages\Page;
|
|
use Illuminate\Support\Facades\Auth;
|
|
use Illuminate\Support\Str;
|
|
use UnitEnum;
|
|
|
|
/**
|
|
* Pagina Filament per la Webmail di Posta Elettronica Certificata (PEC).
|
|
* Consente la consultazione, archiviazione locale .EML, associazione stabile/unità,
|
|
* apertura ticket e gestione allegati in interfaccia stile Gmail.
|
|
*/
|
|
class PostaPecWebmail extends Page
|
|
{
|
|
protected static ?string $navigationLabel = 'Posta Certificata (PEC)';
|
|
|
|
protected static ?string $title = 'Posta PEC (Webmail)';
|
|
|
|
protected static BackedEnum|string|null $navigationIcon = 'heroicon-o-shield-check';
|
|
|
|
protected static UnitEnum|string|null $navigationGroup = 'Comunicazioni';
|
|
|
|
protected static ?int $navigationSort = 2;
|
|
|
|
protected static ?string $slug = 'comunicazioni/posta-pec';
|
|
|
|
protected string $view = 'filament.pages.posta.webmail';
|
|
|
|
public string $tipoCasella = 'pec';
|
|
|
|
public ?int $selectedStabileId = null;
|
|
|
|
public string $currentFolder = 'inbox'; // 'inbox', 'starred', 'sent', 'drafts', 'trash', 'attachments'
|
|
|
|
public string $searchQuery = '';
|
|
|
|
public ?int $selectedMessageId = null;
|
|
|
|
public bool $isComposing = false;
|
|
|
|
// Campi per la composizione
|
|
public string $composeTo = '';
|
|
public string $composeCc = '';
|
|
public string $composeSubject = '';
|
|
public string $composeBody = '';
|
|
public ?int $composeStabileId = null;
|
|
|
|
// Modal Anteprima Allegato
|
|
public bool $showAttachmentModal = false;
|
|
public ?array $activeAttachment = null;
|
|
|
|
public static function canAccess(): bool
|
|
{
|
|
$user = Auth::user();
|
|
return $user instanceof User
|
|
&& $user->hasAnyRole(['super-admin', 'admin', 'amministratore', 'collaboratore']);
|
|
}
|
|
|
|
public function mount(): void
|
|
{
|
|
$user = Auth::user();
|
|
if ($user instanceof User) {
|
|
$reqStabile = request()->integer('stabile_id');
|
|
if ($reqStabile > 0) {
|
|
$this->selectedStabileId = $reqStabile;
|
|
} else {
|
|
$this->selectedStabileId = StabileContext::resolveActiveStabileId($user);
|
|
}
|
|
}
|
|
}
|
|
|
|
public function getStabiliOptionsProperty(): array
|
|
{
|
|
$user = Auth::user();
|
|
if (! $user instanceof User) {
|
|
return [];
|
|
}
|
|
|
|
return StabileContext::accessibleStabili($user)
|
|
->mapWithKeys(fn(Stabile $s) => [(int) $s->id => trim((string) ($s->codice_stabile . ' - ' . $s->denominazione))])
|
|
->all();
|
|
}
|
|
|
|
public function selectFolder(string $folder): void
|
|
{
|
|
$this->currentFolder = $folder;
|
|
$this->selectedMessageId = null;
|
|
}
|
|
|
|
public function selectMessage(int $messageId): void
|
|
{
|
|
$this->selectedMessageId = $messageId;
|
|
$msg = CommunicationMessage::find($messageId);
|
|
if ($msg && $msg->status === 'received') {
|
|
$msg->update(['status' => 'read']);
|
|
}
|
|
}
|
|
|
|
public function closeMessage(): void
|
|
{
|
|
$this->selectedMessageId = null;
|
|
}
|
|
|
|
public function toggleStar(int $messageId): void
|
|
{
|
|
$msg = CommunicationMessage::find($messageId);
|
|
if ($msg) {
|
|
$meta = is_array($msg->metadata) ? $msg->metadata : [];
|
|
$meta['is_starred'] = ! ($meta['is_starred'] ?? false);
|
|
$msg->metadata = $meta;
|
|
$msg->save();
|
|
}
|
|
}
|
|
|
|
public function deleteMessage(int $messageId): void
|
|
{
|
|
$msg = CommunicationMessage::find($messageId);
|
|
if ($msg) {
|
|
$msg->delete();
|
|
if ($this->selectedMessageId === $messageId) {
|
|
$this->selectedMessageId = null;
|
|
}
|
|
Notification::make()->title('Messaggio PEC eliminato')->success()->send();
|
|
}
|
|
}
|
|
|
|
public function createTicketFromMessage(int $messageId): void
|
|
{
|
|
$msg = CommunicationMessage::find($messageId);
|
|
if (! $msg) {
|
|
return;
|
|
}
|
|
|
|
if ($msg->ticket_id) {
|
|
Notification::make()->title('Ticket già presente')->warning()->send();
|
|
return;
|
|
}
|
|
|
|
$userId = Auth::id() ?: 1;
|
|
|
|
$ticket = Ticket::create([
|
|
'stabile_id' => $msg->stabile_id,
|
|
'unita_immobiliare_id' => $msg->metadata['unita_immobiliare_id'] ?? null,
|
|
'aperto_da_user_id' => $userId,
|
|
'titolo' => Str::limit($msg->metadata['subject'] ?? 'Ticket da PEC', 180, '...'),
|
|
'descrizione' => Str::limit($msg->message_text, 1000),
|
|
'priorita' => 'Alta',
|
|
'stato' => 'Aperto',
|
|
'data_apertura' => now(),
|
|
]);
|
|
|
|
$msg->update(['ticket_id' => $ticket->id]);
|
|
|
|
Notification::make()
|
|
->title('Ticket PEC #' . $ticket->id . ' creato')
|
|
->body('Il ticket è stato associato al messaggio e allo stabile con priorità ALTA.')
|
|
->success()
|
|
->send();
|
|
}
|
|
|
|
public function syncNow(): void
|
|
{
|
|
$user = Auth::user();
|
|
if (! $user instanceof User) {
|
|
return;
|
|
}
|
|
|
|
$service = app(ImapMailboxService::class);
|
|
$stabili = $this->selectedStabileId
|
|
? Stabile::where('id', $this->selectedStabileId)->get()
|
|
: StabileContext::accessibleStabili($user);
|
|
|
|
$totImported = 0;
|
|
foreach ($stabili as $stabile) {
|
|
$config = (array) ($stabile->configurazione_avanzata ?? []);
|
|
$posta = (array) ($config['posta'] ?? []);
|
|
$caselle = array_values(array_filter((array) ($posta['caselle'] ?? []), 'is_array'));
|
|
|
|
foreach ($caselle as $mailbox) {
|
|
if (empty($mailbox['enabled'])) {
|
|
continue;
|
|
}
|
|
$mTipo = strtolower((string) ($mailbox['tipo'] ?? 'imap'));
|
|
if ($mTipo !== 'pec') {
|
|
continue;
|
|
}
|
|
|
|
if (! empty($mailbox['host']) && ! empty($mailbox['username'])) {
|
|
$res = $service->fetchMailbox($stabile, $mailbox, 20);
|
|
$totImported += (int) ($res['imported'] ?? 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
Notification::make()
|
|
->title('Sincronizzazione PEC completata')
|
|
->body($totImported > 0 ? "Importati {$totImported} nuovi messaggi PEC (.EML)" : "Nessun nuovo messaggio PEC trovato.")
|
|
->success()
|
|
->send();
|
|
}
|
|
|
|
public function openCompose(): void
|
|
{
|
|
$this->isComposing = true;
|
|
$this->composeTo = '';
|
|
$this->composeCc = '';
|
|
$this->composeSubject = '';
|
|
$this->composeBody = '';
|
|
$this->composeStabileId = $this->selectedStabileId;
|
|
}
|
|
|
|
public function closeCompose(): void
|
|
{
|
|
$this->isComposing = false;
|
|
}
|
|
|
|
public function sendMessage(): void
|
|
{
|
|
if (trim($this->composeTo) === '') {
|
|
Notification::make()->title('Inserisci almeno un destinatario PEC')->warning()->send();
|
|
return;
|
|
}
|
|
|
|
$stabileId = $this->composeStabileId ?: $this->selectedStabileId;
|
|
if (! $stabileId) {
|
|
$user = Auth::user();
|
|
$stabileId = $user instanceof User ? StabileContext::resolveActiveStabileId($user) : null;
|
|
}
|
|
|
|
CommunicationMessage::create([
|
|
'channel' => 'pec',
|
|
'direction' => 'outbound',
|
|
'stabile_id' => $stabileId,
|
|
'sender_name' => Auth::user()->name ?? 'Amministrazione Condominiale (PEC)',
|
|
'message_text' => $this->composeBody,
|
|
'status' => 'sent',
|
|
'received_at' => now(),
|
|
'metadata' => [
|
|
'subject' => $this->composeSubject ?: '(Nessun oggetto)',
|
|
'to' => [['email' => $this->composeTo, 'name' => '']],
|
|
'cc' => $this->composeCc ? [['email' => $this->composeCc, 'name' => '']] : [],
|
|
'body_html' => nl2br(e($this->composeBody)),
|
|
'is_pec' => true,
|
|
],
|
|
]);
|
|
|
|
$this->isComposing = false;
|
|
Notification::make()->title('Messaggio PEC registrato e pronto per l\'inoltro certificato')->success()->send();
|
|
}
|
|
|
|
public function previewAttachment(string $filename, string $path, string $contentType): void
|
|
{
|
|
$this->activeAttachment = [
|
|
'filename' => $filename,
|
|
'path' => $path,
|
|
'content_type' => $contentType,
|
|
'exists' => file_exists($path),
|
|
];
|
|
$this->showAttachmentModal = true;
|
|
}
|
|
|
|
public function closeAttachmentModal(): void
|
|
{
|
|
$this->showAttachmentModal = false;
|
|
$this->activeAttachment = null;
|
|
}
|
|
|
|
public function getMessagesProperty()
|
|
{
|
|
$user = Auth::user();
|
|
$allowedStabileIds = $user instanceof User
|
|
? StabileContext::accessibleStabili($user)->pluck('id')->toArray()
|
|
: [];
|
|
|
|
$query = CommunicationMessage::with(['stabile', 'ticket'])
|
|
->whereIn('stabile_id', $allowedStabileIds)
|
|
->where('channel', 'pec');
|
|
|
|
if ($this->selectedStabileId) {
|
|
$query->where('stabile_id', $this->selectedStabileId);
|
|
}
|
|
|
|
if ($this->currentFolder === 'sent') {
|
|
$query->where('direction', 'outbound');
|
|
} elseif ($this->currentFolder === 'starred') {
|
|
$query->where('metadata->is_starred', true);
|
|
} elseif ($this->currentFolder === 'attachments') {
|
|
$query->whereNotNull('attachments')->where('attachments', '!=', '[]');
|
|
} else {
|
|
$query->where('direction', 'inbound');
|
|
}
|
|
|
|
if (trim($this->searchQuery) !== '') {
|
|
$like = '%' . trim($this->searchQuery) . '%';
|
|
$query->where(function ($q) use ($like) {
|
|
$q->where('sender_name', 'like', $like)
|
|
->orWhere('message_text', 'like', $like)
|
|
->orWhere('metadata->subject', 'like', $like)
|
|
->orWhere('metadata->sender_email', 'like', $like);
|
|
});
|
|
}
|
|
|
|
return $query->orderByDesc('received_at')->paginate(25);
|
|
}
|
|
|
|
public function getSelectedMessageProperty(): ?CommunicationMessage
|
|
{
|
|
if (! $this->selectedMessageId) {
|
|
return null;
|
|
}
|
|
|
|
return CommunicationMessage::with(['stabile', 'ticket'])->find($this->selectedMessageId);
|
|
}
|
|
|
|
public function getFolderCountsProperty(): array
|
|
{
|
|
$user = Auth::user();
|
|
$allowedStabileIds = $user instanceof User
|
|
? StabileContext::accessibleStabili($user)->pluck('id')->toArray()
|
|
: [];
|
|
|
|
$base = CommunicationMessage::whereIn('stabile_id', $allowedStabileIds)->where('channel', 'pec');
|
|
|
|
if ($this->selectedStabileId) {
|
|
$base->where('stabile_id', $this->selectedStabileId);
|
|
}
|
|
|
|
return [
|
|
'inbox' => (clone $base)->where('direction', 'inbound')->count(),
|
|
'unread' => (clone $base)->where('direction', 'inbound')->where('status', 'received')->count(),
|
|
'sent' => (clone $base)->where('direction', 'outbound')->count(),
|
|
'starred' => (clone $base)->where('metadata->is_starred', true)->count(),
|
|
'attachments' => (clone $base)->whereNotNull('attachments')->where('attachments', '!=', '[]')->count(),
|
|
];
|
|
}
|
|
}
|