netgescon-day0/app/Filament/Pages/Fornitore/RubricaClienti.php

477 lines
19 KiB
PHP

<?php
namespace App\Filament\Pages\Fornitore;
use App\Filament\Pages\Fornitore\Concerns\ResolvesOperatoreContext;
use App\Models\AssistenzaTecnorepairScheda;
use App\Models\Fornitore;
use App\Models\FornitoreCliente;
use App\Models\TicketIntervento;
use App\Models\User;
use App\Support\GoogleAccountStore;
use BackedEnum;
use Filament\Pages\Page;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
use UnitEnum;
class RubricaClienti extends Page
{
use ResolvesOperatoreContext;
protected static ?string $navigationLabel = 'Rubrica clienti';
protected static ?string $title = 'Rubrica clienti fornitore';
protected static BackedEnum|string|null $navigationIcon = 'heroicon-o-phone';
protected static UnitEnum|string|null $navigationGroup = 'Fornitore';
protected static ?int $navigationSort = 3;
protected static ?string $slug = 'fornitore/rubrica-clienti';
protected string $view = 'filament.pages.fornitore.rubrica-clienti';
public ?int $fornitoreId = null;
public ?string $fornitoreLabel = null;
public bool $missingAdminContext = false;
public string $search = '';
public string $activeTab = 'elenco';
public ?int $selectedClienteId = null;
/** @var array<int, array<string, mixed>> */
public array $rows = [];
/** @var array<string, int> */
public array $counters = [
'totali' => 0,
'collegati' => 0,
'con_email' => 0,
'con_cellulare' => 0,
];
/** @var array<string, mixed>|null */
public ?array $googleWorkspaceStatus = null;
public static function canAccess(): bool
{
$user = Auth::user();
return $user instanceof User
&& $user->hasAnyRole(['super-admin', 'admin', 'amministratore', 'fornitore']);
}
public function mount(): void
{
[$fornitore] = $this->resolveOperatoreContext(allowAdminWithoutSupplier: true);
if (! $fornitore instanceof Fornitore) {
$this->missingAdminContext = true;
return;
}
$this->fornitoreId = (int) $fornitore->id;
$this->fornitoreLabel = $this->getFornitoreLabel($fornitore);
$this->googleWorkspaceStatus = $this->resolveGoogleWorkspaceStatus($fornitore);
$this->refreshRows();
}
public function updatedSearch(): void
{
$this->refreshRows();
}
public function selectTab(string $tab): void
{
if (! in_array($tab, ['elenco', 'scheda'], true)) {
return;
}
$this->activeTab = $tab;
}
public function selectCliente(int $clienteId): void
{
$this->selectedClienteId = $clienteId > 0 ? $clienteId : null;
$this->activeTab = 'scheda';
}
public function refreshRows(): void
{
if (! $this->fornitoreId) {
$this->rows = [];
$this->counters = ['totali' => 0, 'collegati' => 0, 'con_email' => 0, 'con_cellulare' => 0];
return;
}
$term = Str::lower(trim($this->search));
$legacyCounts = AssistenzaTecnorepairScheda::query()
->where('fornitore_id', $this->fornitoreId)
->selectRaw('legacy_cliente_id, COUNT(*) as totale_schede, MAX(date_received) as ultima_data')
->groupBy('legacy_cliente_id')
->get()
->mapWithKeys(fn(AssistenzaTecnorepairScheda $row): array=> [
(int) ($row->legacy_cliente_id ?? 0) => [
'totale_schede' => (int) ($row->totale_schede ?? 0),
'ultima_data' => optional($row->ultima_data)->format('d/m/Y') ?: '-',
],
])
->all();
$items = FornitoreCliente::query()
->with('rubrica:id,nome,cognome,ragione_sociale,email,telefono_cellulare,telefono_ufficio,categoria')
->where('fornitore_id', $this->fornitoreId)
->when($term !== '', function ($query) use ($term): void {
$like = '%' . $term . '%';
$query->where(function ($inner) use ($like): void {
$inner->where('display_name', 'like', $like)
->orWhere('phone', 'like', $like)
->orWhere('phone_alt', 'like', $like)
->orWhere('email', 'like', $like)
->orWhere('citta', 'like', $like)
->orWhere('indirizzo', 'like', $like);
});
})
->orderByRaw('CASE WHEN rubrica_id IS NULL THEN 1 ELSE 0 END')
->orderBy('display_name')
->get();
if ($items->isEmpty()) {
$this->rows = $this->buildTicketFallbackRows($term);
$this->counters = [
'totali' => count($this->rows),
'collegati' => (int) collect($this->rows)->filter(fn(array $row): bool => filled($row['rubrica_id'] ?? null))->count(),
'con_email' => (int) collect($this->rows)->filter(fn(array $row): bool => filled($row['email'] ?? null))->count(),
'con_cellulare' => (int) collect($this->rows)->filter(fn(array $row): bool => filled($row['phone'] ?? null))->count(),
];
$this->selectedClienteId = null;
return;
}
$this->rows = $items->map(function (FornitoreCliente $cliente) use ($legacyCounts): array {
$legacy = $legacyCounts[(int) ($cliente->legacy_cliente_id ?? 0)] ?? ['totale_schede' => 0, 'ultima_data' => '-'];
return [
'id' => (int) $cliente->id,
'legacy_cliente_id' => (int) ($cliente->legacy_cliente_id ?? 0),
'display_name' => (string) ($cliente->display_name ?: 'Cliente TecnoRepair'),
'phone' => (string) ($cliente->phone ?? ''),
'phone_alt' => (string) ($cliente->phone_alt ?? ''),
'email' => (string) ($cliente->email ?? ''),
'indirizzo' => (string) ($cliente->indirizzo ?? ''),
'citta' => (string) ($cliente->citta ?? ''),
'provincia' => (string) ($cliente->provincia ?? ''),
'rubrica_id' => (int) ($cliente->rubrica_id ?? 0),
'rubrica_url' => $cliente->rubrica_id ? $this->buildRubricaUrlForCurrentUser((int) $cliente->rubrica_id, (string) ($cliente->display_name ?? '')) : null,
'rubrica_label' => (string) ($cliente->rubrica?->categoria ?? ''),
'totale_schede' => (int) ($legacy['totale_schede'] ?? 0),
'ultima_data' => (string) ($legacy['ultima_data'] ?? '-'),
'updated_at' => optional($cliente->updated_at)->format('d/m/Y H:i') ?: '-',
'source' => 'tecnorepair_tclienti',
];
})->all();
$this->counters = [
'totali' => count($this->rows),
'collegati' => (int) collect($this->rows)->filter(fn(array $row): bool => (int) ($row['rubrica_id'] ?? 0) > 0)->count(),
'con_email' => (int) collect($this->rows)->filter(fn(array $row): bool => trim((string) ($row['email'] ?? '')) !== '')->count(),
'con_cellulare' => (int) collect($this->rows)->filter(fn(array $row): bool => trim((string) ($row['phone'] ?? '')) !== '')->count(),
];
if ((int) ($this->selectedClienteId ?? 0) <= 0 && $this->rows !== []) {
$this->selectedClienteId = (int) $this->rows[0]['id'];
}
}
public function getSelectedClienteProperty(): ?FornitoreCliente
{
$selectedId = (int) ($this->selectedClienteId ?? 0);
if ($selectedId <= 0 || ! $this->fornitoreId) {
return null;
}
return FornitoreCliente::query()
->with('rubrica')
->where('fornitore_id', $this->fornitoreId)
->find($selectedId);
}
/** @return array<int, array<string, mixed>> */
public function getSelectedClienteSchedeProperty(): array
{
$cliente = $this->selectedCliente;
if (! $cliente instanceof FornitoreCliente || ! $this->fornitoreId) {
return [];
}
return AssistenzaTecnorepairScheda::query()
->where('fornitore_id', $this->fornitoreId)
->when((int) ($cliente->legacy_cliente_id ?? 0) > 0, fn($query) => $query->where('legacy_cliente_id', (int) $cliente->legacy_cliente_id))
->orderByDesc('date_received')
->orderByDesc('id')
->limit(30)
->get()
->map(fn(AssistenzaTecnorepairScheda $scheda): array=> [
'id' => (int) $scheda->id,
'numero' => (string) ($scheda->legacy_numero_scheda ?: ('#' . $scheda->id)),
'prodotto' => (string) ($scheda->product_model ?: '-'),
'codice' => (string) ($scheda->product_code ?: ''),
'seriali' => (string) $scheda->serial_label,
'stato' => (string) ($scheda->status_label ?: '-'),
'status_badge' => (string) $scheda->status_badge_classes,
'ingresso' => optional($scheda->date_received)->format('d/m/Y') ?: '-',
])->all();
$items = TicketIntervento::query()
->with(['ticket.stabile', 'ticket.soggettoRichiedente', 'ticket.apertoDaUser'])
->where('fornitore_id', $this->fornitoreId)
->latest('updated_at')
->limit(250)
->get();
$grouped = [];
foreach ($items as $intervento) {
$contact = $this->resolveCallerDataForRubrica($intervento);
$key = $contact['identity_key'];
if ($key === '') {
$key = 'ticket-' . (int) $intervento->ticket_id;
}
if ($term !== '') {
$haystack = Str::lower(implode(' ', [
$contact['contatto'],
$contact['telefono'],
$contact['email'],
$contact['stabile'],
$contact['origine'],
]));
if (! Str::contains($haystack, $term)) {
continue;
}
}
if (! isset($grouped[$key])) {
$grouped[$key] = [
'contatto' => $contact['contatto'],
'telefono' => $contact['telefono'],
'email' => $contact['email'],
'stabile' => $contact['stabile'],
'origine' => $contact['origine'],
'rubrica_url' => $contact['rubrica_url'],
'rubrica_label' => $contact['rubrica_label'],
'ultimo_ticket_id' => (int) $intervento->ticket_id,
'ultima_scheda_url' => TicketInterventoScheda::getUrl(['record' => (int) $intervento->id], panel: 'admin-filament'),
'updated_at' => optional($intervento->updated_at)->format('d/m/Y H:i') ?: '-',
'totale_interventi' => 1,
];
continue;
}
$grouped[$key]['totale_interventi']++;
}
$this->rows = array_values($grouped);
}
public function getLavorazioniUrl(): string
{
return LavorazioniOperative::getUrl(['fornitore' => (int) $this->fornitoreId], panel: 'admin-filament');
}
public function getProdottiUrl(): string
{
return ProdottiCatalogo::getUrl(['fornitore' => (int) $this->fornitoreId], panel: 'admin-filament');
}
public function getImportCommandHintProperty(): string
{
$fornitore = (int) ($this->fornitoreId ?? 0) > 0
? Fornitore::query()->select(['id', 'amministratore_id'])->find((int) $this->fornitoreId)
: null;
if (! $fornitore instanceof Fornitore) {
return 'php artisan tecnorepair:import-rubrica-clienti {amministratore} --fornitore-id={id}';
}
return 'php artisan tecnorepair:import-rubrica-clienti ' . (int) ($fornitore->amministratore_id ?? 0) . ' --fornitore-id=' . (int) $fornitore->id;
}
public function getGoogleConnectUrl(): string
{
return route('oauth.google.redirect', [
'account_key' => 'fornitore-' . (int) ($this->fornitoreId ?? 0),
'label' => trim((string) ($this->fornitoreLabel ?? '')) ?: ('Fornitore #' . (int) ($this->fornitoreId ?? 0)),
'return_to' => request()->getRequestUri(),
]);
}
public function getGoogleDisconnectUrl(): string
{
return route('oauth.google.disconnect', [
'account_key' => 'fornitore-' . (int) ($this->fornitoreId ?? 0),
'return_to' => request()->getRequestUri(),
]);
}
/**
* @return array<string, string>
*/
protected function resolveCallerDataForRubrica(TicketIntervento $intervento): array
{
$ticket = $intervento->ticket;
$parsed = $this->extractCallerData((string) ($ticket?->descrizione ?? ''));
$contatto = $parsed['contatto'];
$telefono = $parsed['telefono'];
$email = '';
$origine = 'Descrizione ticket';
$rubricaUrl = null;
$rubricaLabel = '';
if ($ticket?->soggettoRichiedente) {
$soggetto = $ticket->soggettoRichiedente;
$contatto = trim((string) ($soggetto->ragione_sociale ?: trim(($soggetto->nome ?? '') . ' ' . ($soggetto->cognome ?? '')))) ?: $contatto;
$telefono = trim((string) ($soggetto->telefono ?? '')) ?: $telefono;
$email = trim((string) ($soggetto->email ?? ''));
$origine = 'Richiedente ticket';
$rubricaUrl = $this->buildRubricaUrlForCurrentUser((int) $soggetto->id);
$rubricaLabel = trim((string) ($soggetto->categoria ?? ''));
} elseif ($ticket?->apertoDaUser) {
$contatto = trim((string) ($ticket->apertoDaUser->name ?? '')) ?: $contatto;
$email = trim((string) ($ticket->apertoDaUser->email ?? ''));
$origine = 'Utente apertura ticket';
}
$identityKey = '';
if ($ticket?->soggetto_richiedente_id) {
$identityKey = 'soggetto-' . (int) $ticket->soggetto_richiedente_id;
} elseif ($email !== '') {
$identityKey = 'email-' . Str::lower($email);
} elseif ($telefono !== '') {
$identityKey = 'phone-' . preg_replace('/\D+/', '', $telefono);
}
return [
'contatto' => $contatto !== '' ? $contatto : 'Contatto non identificato',
'telefono' => $telefono,
'email' => $email,
'origine' => $origine,
'stabile' => (string) ($ticket?->stabile?->denominazione ?? '-'),
'rubrica_url' => $rubricaUrl,
'rubrica_label' => $rubricaLabel,
'identity_key' => $identityKey,
];
}
protected function buildRubricaUrlForCurrentUser(int $rubricaId, string $search = ''): ?string
{
$user = Auth::user();
if (! $user instanceof User) {
return null;
}
if ($user->hasAnyRole(['super-admin', 'admin', 'amministratore', 'collaboratore'])) {
return \App\Filament\Pages\Gescon\RubricaUniversaleScheda::getUrl(['record' => $rubricaId], panel: 'admin-filament');
}
$url = self::getUrl(['fornitore' => (int) ($this->fornitoreId ?? 0)], panel: 'admin-filament');
$search = trim($search);
return $search !== '' ? ($url . '?search=' . urlencode($search)) : $url;
}
/**
* @return array<int, array<string, mixed>>
*/
private function buildTicketFallbackRows(string $term = ''): array
{
$items = TicketIntervento::query()
->with(['ticket.stabile', 'ticket.soggettoRichiedente', 'ticket.apertoDaUser'])
->where('fornitore_id', $this->fornitoreId)
->latest('updated_at')
->limit(250)
->get();
$grouped = [];
foreach ($items as $intervento) {
$contact = $this->resolveCallerDataForRubrica($intervento);
$key = $contact['identity_key'] !== '' ? $contact['identity_key'] : ('ticket-' . (int) $intervento->ticket_id);
if ($term !== '') {
$haystack = Str::lower(implode(' ', [$contact['contatto'], $contact['telefono'], $contact['email'], $contact['stabile'], $contact['origine']]));
if (! Str::contains($haystack, $term)) {
continue;
}
}
if (! isset($grouped[$key])) {
$grouped[$key] = [
'id' => 0,
'legacy_cliente_id' => 0,
'display_name' => $contact['contatto'],
'phone' => $contact['telefono'],
'phone_alt' => '',
'email' => $contact['email'],
'indirizzo' => '',
'citta' => '',
'provincia' => '',
'rubrica_id' => 0,
'rubrica_url' => $contact['rubrica_url'],
'rubrica_label' => $contact['rubrica_label'],
'totale_schede' => 1,
'ultima_data' => optional($intervento->updated_at)->format('d/m/Y') ?: '-',
'updated_at' => optional($intervento->updated_at)->format('d/m/Y H:i') ?: '-',
'source' => 'ticket_fallback',
];
continue;
}
$grouped[$key]['totale_schede']++;
}
return array_values($grouped);
}
/**
* @return array<string, mixed>|null
*/
protected function resolveGoogleWorkspaceStatus(Fornitore $fornitore): ?array
{
$amministratore = $fornitore->amministratore;
if (! $amministratore) {
return null;
}
$account = app(GoogleAccountStore::class)->get($amministratore, 'fornitore-' . (int) $fornitore->id);
if (! is_array($account)) {
return [
'connected' => false,
'email' => '',
'name' => '',
'connected_at' => '',
];
}
return [
'connected' => (bool) ($account['connected'] ?? false),
'email' => (string) ($account['email'] ?? ''),
'name' => (string) ($account['name'] ?? ''),
'connected_at' => (string) ($account['connected_at'] ?? ''),
];
}
}