netgescon-day0/app/Filament/Pages/Posta/PostaPecWebmail.php

441 lines
15 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 string|int|null $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()->query('stabile_id');
if ($reqStabile === 'studio') {
$this->selectedStabileId = 'studio';
} elseif (is_numeric($reqStabile) && (int) $reqStabile > 0) {
$this->selectedStabileId = (int) $reqStabile;
} else {
$this->selectedStabileId = StabileContext::resolveActiveStabileId($user);
}
}
}
public function getStabiliOptionsProperty(): array
{
$user = Auth::user();
if (! $user instanceof User) {
return [];
}
$options = [
'studio' => '🏢 PEC Generale Studio / Amministrazione',
];
$stabili = StabileContext::accessibleStabili($user)
->mapWithKeys(fn(Stabile $s) => [(int) $s->id => trim((string) ($s->codice_stabile . ' - ' . $s->denominazione))])
->all();
return $options + $stabili;
}
public function getConfigMailboxUrlProperty(): string
{
if ($this->selectedStabileId === 'studio') {
return url('/admin-filament/impostazioni/scheda-amministratore?tab=posta-operativita');
}
if (is_numeric($this->selectedStabileId) && (int) $this->selectedStabileId > 0) {
return url('/admin-filament/condomini/stabile?stabile_id=' . $this->selectedStabileId . '&tab=posta-ufficiale');
}
return url('/admin-filament/condomini/stabile?tab=posta-ufficiale');
}
public function getIsMailboxConfiguredProperty(): bool
{
$user = Auth::user();
if (! $user instanceof User) {
return false;
}
if ($this->selectedStabileId === 'studio') {
$amministratore = $user->amministratore;
if ($amministratore) {
$config = (array) ($amministratore->impostazioni ?? []);
$caselle = (array) ($config['posta']['caselle'] ?? []);
foreach ($caselle as $c) {
if (! empty($c['enabled']) && ! empty($c['host']) && ($c['tipo'] ?? '') === 'pec') {
return true;
}
}
}
return false;
}
if (is_numeric($this->selectedStabileId) && (int) $this->selectedStabileId > 0) {
$stabile = Stabile::find($this->selectedStabileId);
if ($stabile) {
$config = (array) ($stabile->configurazione_avanzata ?? []);
$caselle = (array) ($config['posta']['caselle'] ?? []);
foreach ($caselle as $c) {
if (! empty($c['enabled']) && ! empty($c['host']) && ($c['tipo'] ?? '') === 'pec') {
return true;
}
}
}
return false;
}
return true;
}
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);
$totImported = 0;
if ($this->selectedStabileId === 'studio') {
$amministratore = $user->amministratore;
if ($amministratore) {
$config = (array) ($amministratore->impostazioni ?? []);
$caselle = array_values(array_filter((array) ($config['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->fetchStudioMailbox($amministratore, $mailbox, 20);
$totImported += (int) ($res['imported'] ?? 0);
}
}
}
} else {
$stabili = (is_numeric($this->selectedStabileId) && (int) $this->selectedStabileId > 0)
? Stabile::where('id', (int) $this->selectedStabileId)->get()
: StabileContext::accessibleStabili($user);
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'])
->where('channel', 'pec');
if ($this->selectedStabileId === 'studio') {
$query->whereNull('stabile_id');
} elseif (is_numeric($this->selectedStabileId) && (int) $this->selectedStabileId > 0) {
$query->where('stabile_id', (int) $this->selectedStabileId);
} else {
$query->where(function ($q) use ($allowedStabileIds) {
$q->whereIn('stabile_id', $allowedStabileIds)
->orWhereNull('stabile_id');
});
}
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::query()->where('channel', 'pec');
if ($this->selectedStabileId === 'studio') {
$base->whereNull('stabile_id');
} elseif (is_numeric($this->selectedStabileId) && (int) $this->selectedStabileId > 0) {
$base->where('stabile_id', (int) $this->selectedStabileId);
} else {
$base->where(function ($q) use ($allowedStabileIds) {
$q->whereIn('stabile_id', $allowedStabileIds)
->orWhereNull('stabile_id');
});
}
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(),
];
}
}