netgescon-day0/resources/views/filament/pages/gescon/fornitore-scheda.blade.php

784 lines
52 KiB
PHP
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<x-filament-panels::page>
@php
$tabs = [
'profilo' => 'Dati condivisi',
'accessi' => 'Dipendenti e accessi',
'assistenza' => 'Assistenza interna',
'catalogo' => 'Catalogo e prodotti',
'contabilita' => 'Contabilita',
];
$catalogoActions = [
['action' => 'nuovo_prodotto', 'label' => 'Nuovo prodotto', 'color' => 'gray'],
['action' => 'estrai_prodotti_fe', 'label' => 'Estrai da FE', 'color' => 'primary'],
['action' => 'importa_listino_csv', 'label' => 'Importa listino CSV', 'color' => 'success'],
['action' => 'aggiorna_listino_remoto', 'label' => 'Aggiorna listino remoto', 'color' => 'success'],
['action' => 'importa_listino_xlsx', 'label' => 'Importa listino XLSX', 'color' => 'success'],
['action' => 'scarica_asset_catalogo', 'label' => 'Internalizza asset', 'color' => 'warning'],
['action' => 'genera_referral_amazon', 'label' => 'Prepara link Amazon', 'color' => 'gray'],
];
$assistenzaActions = [
['action' => 'impostazioni_fe', 'label' => 'Impostazioni FE', 'color' => 'gray'],
['action' => 'acqua_config', 'label' => 'Configura acqua', 'color' => 'gray'],
['action' => 'acqua_scan', 'label' => 'Scansiona FE acqua', 'color' => 'primary'],
];
@endphp
@php
$catalogImport = is_array($fornitore->operational_config_safe['catalog_import'] ?? null) ? $fornitore->operational_config_safe['catalog_import'] : [];
$csvPath = trim((string) ($catalogImport['wholesale_csv_path'] ?? ''));
$remoteCsvUrl = trim((string) ($catalogImport['remote_csv_url'] ?? ''));
$publicationMode = (string) ($catalogImport['publication_mode'] ?? 'internal_catalog');
$publicationLabels = [
'internal_only' => 'Solo interno',
'internal_catalog' => 'Catalogo interno',
'referral_ready' => 'Pronto referral',
];
@endphp
<div class="space-y-6 overflow-x-hidden" x-data="{ tab: @js($this->sectionTab) }">
<div class="rounded-xl border bg-white p-4">
<div class="flex flex-wrap items-start justify-between gap-4">
<div>
<div class="text-lg font-semibold">Scheda fornitore</div>
<div class="mt-1 text-sm text-slate-600">La scheda e divisa in tab operative per separare dati condivisi, accessi, assistenza, catalogo e contabilita senza perdere il contesto del fornitore.</div>
</div>
<div class="rounded-lg bg-slate-900 px-3 py-2 text-right text-white">
<div class="text-[10px] uppercase tracking-[0.18em] text-slate-300">Fornitore</div>
<div class="text-sm font-semibold">{{ $fornitore->ragione_sociale ?? trim(($fornitore->nome ?? '') . ' ' . ($fornitore->cognome ?? '')) ?: ('Fornitore #' . $fornitore->id) }}</div>
</div>
</div>
<div class="mt-4 flex flex-wrap gap-2">
@foreach($tabs as $tabKey => $tabLabel)
<button
type="button"
wire:click="setSectionTab('{{ $tabKey }}')"
x-on:click="tab = '{{ $tabKey }}'"
class="inline-flex items-center rounded-full px-4 py-2 text-sm font-medium"
x-bind:class="tab === '{{ $tabKey }}' ? 'bg-slate-900 text-white shadow-sm' : 'bg-slate-100 text-slate-700 hover:bg-slate-200'"
>
{{ $tabLabel }}
</button>
@endforeach
</div>
</div>
<div class="rounded-xl border border-slate-200 bg-slate-50 px-4 py-3 text-sm text-slate-700" x-show="tab === 'catalogo'" x-cloak>
Qui restano visibili modulo fornitore e catalogo operativo. Il magazzino e la gestione completa del prodotto possono essere spostati in menu dedicati mantenendo qui il quadro sintetico e i collegamenti rapidi.
</div>
<div class="rounded-xl border border-slate-200 bg-white p-4" x-show="tab === 'catalogo' || tab === 'assistenza'" x-cloak>
<div class="flex flex-wrap items-start justify-between gap-3">
<div>
<div class="text-sm font-semibold text-slate-900" x-show="tab === 'catalogo'">Azioni catalogo</div>
<div class="text-sm font-semibold text-slate-900" x-show="tab === 'assistenza'">Azioni automazioni</div>
<div class="mt-1 text-xs text-slate-500" x-show="tab === 'catalogo'">Un solo punto per le azioni operative del catalogo, senza duplicarle in più riquadri.</div>
<div class="mt-1 text-xs text-slate-500" x-show="tab === 'assistenza'">FE e acqua raccolte in una toolbar compatta, così la colonna destra non spinge più il layout.</div>
</div>
<div class="flex max-w-full flex-wrap justify-end gap-2">
@foreach($catalogoActions as $action)
<x-filament::button
x-show="tab === 'catalogo'"
size="sm"
:color="$action['color']"
type="button"
wire:click="mountAction('{{ $action['action'] }}')"
>{{ $action['label'] }}</x-filament::button>
@endforeach
@foreach($assistenzaActions as $action)
<x-filament::button
x-show="tab === 'assistenza'"
size="sm"
:color="$action['color']"
type="button"
wire:click="mountAction('{{ $action['action'] }}')"
>{{ $action['label'] }}</x-filament::button>
@endforeach
</div>
</div>
</div>
<div class="rounded-xl border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm text-emerald-900" x-show="tab === 'assistenza'" x-cloak>
Questa tab concentra TecnoRepair, seriali e automazioni collegate al fornitore. I ticket operativi e il protocollo comunicazioni possono poi evolvere come viste dedicate, senza ricadere in una scheda unica confusa.
</div>
<div class="rounded-xl border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-900" x-show="tab === 'contabilita'" x-cloak>
Qui si prepara il perimetro economico del fornitore: pagamenti, fatture, anteprime contabili e ritenute. Preventivi, ordini e fatturazione completa possono innestarsi in questa area senza mischiarsi con catalogo o assistenza.
</div>
<div class="grid min-w-0 grid-cols-1 gap-6 lg:grid-cols-3">
<div class="min-w-0 space-y-6 lg:col-span-2">
<div class="rounded-lg border p-4" x-show="tab === 'profilo'" x-cloak>
<div class="text-lg font-semibold">Fornitore</div>
<div class="mt-2 grid grid-cols-1 gap-2 md:grid-cols-2">
<div><span class="font-medium">Codice:</span> {{ $fornitore->codice_univoco ?? '-' }}</div>
<div><span class="font-medium">Ragione sociale:</span> {{ $fornitore->ragione_sociale ?? '-' }}</div>
<div><span class="font-medium">Nome:</span> {{ trim(($fornitore->nome ?? '') . ' ' . ($fornitore->cognome ?? '')) ?: '-' }}</div>
<div><span class="font-medium">Sito:</span> {{ $fornitore->sito_web ?? '-' }}</div>
<div><span class="font-medium">P.IVA:</span> {{ $fornitore->partita_iva ?? '-' }}</div>
<div><span class="font-medium">CF:</span> {{ $fornitore->codice_fiscale ?? '-' }}</div>
<div class="md:col-span-2"><span class="font-medium">Indirizzo:</span> {{ $fornitore->indirizzo_completo ?? '-' }}</div>
<div class="md:col-span-2"><span class="font-medium">Note:</span> {{ $this->cleanDisplayValue($fornitore->note) }}</div>
</div>
</div>
<div class="rounded-lg border p-4" x-show="tab === 'profilo'" x-cloak>
<div class="text-lg font-semibold">Contatti</div>
<div class="mt-3 grid grid-cols-1 gap-4 md:grid-cols-2">
<div class="space-y-1">
<div class="text-sm font-medium text-gray-700">Fornitore</div>
<div><span class="font-medium">Email:</span> {{ $this->cleanDisplayValue($fornitore->email) }}</div>
<div><span class="font-medium">PEC:</span> {{ $this->cleanDisplayValue($fornitore->pec) }}</div>
<div><span class="font-medium">Telefono:</span> {{ $fornitore->telefono ?? '-' }}</div>
<div><span class="font-medium">Cellulare:</span> {{ $fornitore->cellulare ?? '-' }}</div>
</div>
<div class="space-y-1">
<div class="text-sm font-medium text-gray-700">Rubrica</div>
@if($fornitore->rubrica)
<div><span class="font-medium">Email:</span> {{ $fornitore->rubrica->email ?? '-' }}</div>
<div><span class="font-medium">PEC:</span> {{ $fornitore->rubrica->pec ?? '-' }}</div>
<div><span class="font-medium">Tel. ufficio:</span> {{ $fornitore->rubrica->telefono_ufficio ?? '-' }}</div>
<div><span class="font-medium">Tel. cellulare:</span> {{ $fornitore->rubrica->telefono_cellulare ?? '-' }}</div>
<div><span class="font-medium">Tel. casa:</span> {{ $fornitore->rubrica->telefono_casa ?? '-' }}</div>
<div><span class="font-medium">Fax:</span> {{ $fornitore->rubrica->fax ?? '-' }}</div>
@else
<div class="text-sm text-gray-600">Nessun collegamento presente.</div>
@endif
</div>
</div>
</div>
<div class="rounded-lg border p-4" x-show="tab === 'accessi'" x-cloak>
<div class="text-lg font-semibold">Dipendenti e accessi fornitore</div>
<div class="mt-1 text-xs text-gray-500">La gestione accessi dei dipendenti fornitore e centralizzata qui.</div>
@if(filled($lastGeneratedPassword ?? null))
<div class="mt-3 rounded-md border border-amber-300 bg-amber-50 px-3 py-2 text-xs text-amber-900">
Ultima password temporanea generata: <span class="font-mono font-semibold">{{ $lastGeneratedPassword }}</span>
</div>
@endif
<div class="mt-4 rounded-xl border border-slate-200 bg-slate-50 p-3">
<label class="block text-sm">
<span class="mb-1 block font-medium">Ricerca nominativo in anagrafica unica</span>
<input type="text" wire:model.live.debounce.250ms="dipendenteRubricaSearch" class="w-full rounded-lg border-gray-300" placeholder="Ricerca per telefono, nome, cognome, ragione sociale o email e seleziona il contatto con un click. Es. +39 333 1234567 oppure Mario Rossi" />
</label>
<div class="mt-2 text-xs text-gray-500">Usa lo stesso lookup operativo del ticket per agganciare un nominativo già presente nella rubrica unica.</div>
@if($dipendenteRubricaId)
<div class="mt-3 rounded-lg border border-emerald-200 bg-emerald-50 p-3 text-xs text-emerald-800">
Nominativo selezionato: <span class="font-semibold">{{ $dipendenteRubricaSearch }}</span>
<button type="button" wire:click="resetDipendenteRubricaSelection" class="ml-2 inline-flex items-center rounded-md bg-white px-2 py-1 text-[11px] font-medium text-emerald-700 ring-1 ring-inset ring-emerald-300 hover:bg-emerald-100">Cambia</button>
</div>
@endif
@if(count($dipendenteRubricaMatches) > 0)
<div class="mt-3 space-y-2">
@foreach($dipendenteRubricaMatches as $match)
<button type="button" wire:click="selezionaDipendenteRubrica({{ (int) $match['id'] }})" class="w-full rounded-lg border p-3 text-left {{ (int) $dipendenteRubricaId === (int) $match['id'] ? 'border-emerald-400 bg-emerald-50 ring-1 ring-emerald-200' : 'bg-white hover:bg-gray-50' }}">
<div class="text-sm font-medium">{{ $match['label'] }}</div>
<div class="mt-1 text-xs text-gray-600">
{{ $match['phone'] ?: 'Telefono non valorizzato' }}
@if($match['email'])
· {{ $match['email'] }}
@endif
@if($match['cf'])
· CF {{ $match['cf'] }}
@endif
</div>
@if($match['category'])
<div class="mt-1 text-xs text-indigo-700">Categoria: {{ $match['category'] }}</div>
@endif
@if($match['note'])
<div class="mt-1 text-xs text-gray-500">{{ $match['note'] }}</div>
@endif
</button>
@endforeach
</div>
@elseif(filled($dipendenteRubricaSearch) && ! $dipendenteRubricaId)
<div class="mt-3 text-xs text-gray-500">Nessun nominativo trovato con questa ricerca.</div>
@endif
<div class="mt-3">
<x-filament::button size="sm" color="primary" type="button" wire:click="collegaDipendenteDaRubrica">Collega nominativo selezionato</x-filament::button>
</div>
</div>
<div class="mt-3 grid grid-cols-1 gap-2 md:grid-cols-4">
<input type="text" wire:model.defer="nuovoDipendenteNome" class="rounded-md border-gray-300 text-xs" placeholder="Nome" />
<input type="text" wire:model.defer="nuovoDipendenteCognome" class="rounded-md border-gray-300 text-xs" placeholder="Cognome" />
<input type="email" wire:model.defer="nuovoDipendenteEmail" class="rounded-md border-gray-300 text-xs" placeholder="Email" />
<input type="text" wire:model.defer="nuovoDipendenteTelefono" class="rounded-md border-gray-300 text-xs" placeholder="Telefono" />
</div>
<div class="mt-2">
<x-filament::button size="sm" color="primary" type="button" wire:click="creaDipendenteFornitore">Aggiungi dipendente</x-filament::button>
</div>
<div class="mt-3 overflow-x-auto">
<table class="min-w-full border-collapse border text-xs">
<thead>
<tr class="bg-slate-100 text-slate-700">
<th class="border px-2 py-1.5 text-left">Dipendente</th>
<th class="border px-2 py-1.5 text-left">Email</th>
<th class="border px-2 py-1.5 text-left">Telefono</th>
<th class="border px-2 py-1.5 text-left">Utente collegato</th>
<th class="border px-2 py-1.5 text-left">Stato</th>
<th class="border px-2 py-1.5 text-left">Azioni</th>
</tr>
</thead>
<tbody>
@forelse($dipendentiRows as $row)
<tr class="hover:bg-slate-50">
<td class="border px-2 py-1.5">{{ $row['nome'] }}</td>
<td class="border px-2 py-1.5">{{ $row['email'] !== '' ? $row['email'] : '-' }}</td>
<td class="border px-2 py-1.5">{{ $row['telefono'] !== '' ? $row['telefono'] : '-' }}</td>
<td class="border px-2 py-1.5">
@if((int) ($row['user_id'] ?? 0) > 0)
{{ $row['user_label'] }} (ID {{ (int) $row['user_id'] }})
@if((int) ($row['rubrica_id'] ?? 0) > 0)
<div class="mt-1 text-[11px] text-slate-500">Rubrica: {{ $row['rubrica'] ?: ('#' . (int) $row['rubrica_id']) }}</div>
@endif
@else
@if((int) ($row['rubrica_id'] ?? 0) > 0)
<div>{{ $row['rubrica'] ?: ('Rubrica #' . (int) $row['rubrica_id']) }}</div>
@else
-
@endif
@endif
</td>
<td class="border px-2 py-1.5">
@if($row['attivo'])
<span class="rounded bg-emerald-100 px-1.5 py-0.5 text-[10px] font-semibold text-emerald-800">attivo</span>
@else
<span class="rounded bg-rose-100 px-1.5 py-0.5 text-[10px] font-semibold text-rose-800">non attivo</span>
@endif
</td>
<td class="border px-2 py-1.5">
<div class="flex flex-wrap gap-1">
<x-filament::button size="xs" color="primary" type="button" wire:click="abilitaAccessoDipendente({{ (int) $row['id'] }})">Abilita accesso</x-filament::button>
@if((int) ($row['user_id'] ?? 0) > 0)
<x-filament::button size="xs" color="warning" type="button" wire:click="resetPasswordDipendenteUser({{ (int) $row['user_id'] }})">Reset password</x-filament::button>
@endif
<x-filament::button size="xs" color="gray" type="button" wire:click="toggleDipendenteAttivo({{ (int) $row['id'] }})">Toggle attivo</x-filament::button>
</div>
</td>
</tr>
@empty
<tr>
<td colspan="6" class="border px-2 py-3 text-center text-gray-500">Nessun dipendente registrato per questo fornitore.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
<div class="rounded-lg border p-4" x-show="tab === 'catalogo'" x-cloak>
<div class="flex items-start justify-between gap-4">
<div>
<div class="text-lg font-semibold">Controlli listino e gestione catalogo</div>
<div class="mt-1 text-xs text-gray-500">Qui governiamo il perimetro dell'importazione: CSV predefinito, gerarchia categorie, seriali da FE, pubblicazione interna e protezione dei riferimenti fornitore.</div>
</div>
<div class="flex flex-wrap gap-2">
<x-filament::button size="sm" color="gray" type="button" wire:click="mountAction('impostazioni_listino')">Parametri listino</x-filament::button>
<a href="{{ $this->getContabilitaFornitoriUrl() }}" class="inline-flex items-center rounded-md bg-white px-3 py-2 text-xs font-medium text-slate-700 ring-1 ring-inset ring-slate-300 hover:bg-slate-50">Menu Contabilità</a>
<a href="{{ $this->getProdottiHubUrl() }}" class="inline-flex items-center rounded-md bg-slate-900 px-3 py-2 text-xs font-medium text-white hover:bg-slate-800">Hub prodotti</a>
<a href="{{ $this->getSerialiHubUrl() }}" class="inline-flex items-center rounded-md bg-white px-3 py-2 text-xs font-medium text-slate-700 ring-1 ring-inset ring-slate-300 hover:bg-slate-50">Ricerca seriali</a>
</div>
</div>
<div class="mt-4 grid grid-cols-1 gap-3 md:grid-cols-4">
<div class="rounded-md bg-slate-50 p-3">
<div class="text-[11px] uppercase tracking-wide text-slate-500">CSV predefinito</div>
<div class="mt-1 text-sm font-semibold text-slate-900">{{ $csvPath !== '' ? 'Configurato' : 'Da impostare' }}</div>
<div class="mt-1 break-all text-[11px] text-slate-500">{{ $csvPath !== '' ? $csvPath : 'Nessun percorso salvato' }}</div>
</div>
<div class="rounded-md bg-slate-50 p-3">
<div class="text-[11px] uppercase tracking-wide text-slate-500">URL listino remoto</div>
<div class="mt-1 text-sm font-semibold text-slate-900">{{ $remoteCsvUrl !== '' ? 'Configurato' : 'Da impostare' }}</div>
<div class="mt-1 break-all text-[11px] text-slate-500">{{ $remoteCsvUrl !== '' ? 'URL salvato nelle impostazioni fornitore' : 'Nessun URL remoto salvato' }}</div>
</div>
<div class="rounded-md bg-slate-50 p-3">
<div class="text-[11px] uppercase tracking-wide text-slate-500">Limite import</div>
<div class="mt-1 text-xl font-semibold text-slate-900">{{ (int) ($catalogImport['default_limit'] ?? 0) > 0 ? (int) ($catalogImport['default_limit'] ?? 0) : 'Tutto' }}</div>
</div>
<div class="rounded-md bg-slate-50 p-3">
<div class="text-[11px] uppercase tracking-wide text-slate-500">Pubblicazione</div>
<div class="mt-1 text-sm font-semibold text-slate-900">{{ $publicationLabels[$publicationMode] ?? 'Catalogo interno' }}</div>
<div class="mt-1 text-[11px] text-slate-500">Riferimenti fornitore: {{ !empty($catalogImport['hide_supplier_references_public']) ? 'nascosti' : 'visibili' }}</div>
</div>
<div class="rounded-md bg-slate-50 p-3">
<div class="text-[11px] uppercase tracking-wide text-slate-500">Pipeline</div>
<div class="mt-1 text-[11px] text-slate-600">Asset locali: {{ !empty($catalogImport['auto_internalize_assets']) ? 'si' : 'no' }}</div>
<div class="mt-1 text-[11px] text-slate-600">Categorie gerarchiche: {{ !empty($catalogImport['parse_category_hierarchy']) ? 'si' : 'no' }}</div>
<div class="mt-1 text-[11px] text-slate-600">Seriali da FE: {{ !empty($catalogImport['track_serials_from_fe']) ? 'si' : 'no' }}</div>
</div>
</div>
</div>
<div class="rounded-lg border p-4" x-show="tab === 'catalogo'" x-cloak>
<div class="flex items-start justify-between gap-4">
<div>
<div class="text-lg font-semibold">Modulo fornitore separato</div>
<div class="mt-1 text-xs text-gray-500">Catalogo isolato per questo fornitore tramite codice univoco, con riferimenti pubblici interni e sorgenti esterne archiviate solo nel gestionale.</div>
</div>
<div class="rounded-md bg-slate-900 px-3 py-2 text-right text-white">
<div class="text-[10px] uppercase tracking-[0.18em] text-slate-300">Scope</div>
<div class="font-mono text-sm font-semibold">{{ $box['catalogo_modulo']['scope_code'] ?? ($fornitore->codice_univoco ?? ('FORN-' . $fornitore->id)) }}</div>
</div>
</div>
<div class="mt-4 grid grid-cols-2 gap-3 md:grid-cols-7">
<div class="rounded-md bg-slate-50 p-3">
<div class="text-[11px] uppercase tracking-wide text-slate-500">Da FE</div>
<div class="mt-1 text-xl font-semibold text-slate-900">{{ (int) ($box['catalogo_modulo']['fe_products'] ?? 0) }}</div>
</div>
<div class="rounded-md bg-slate-50 p-3">
<div class="text-[11px] uppercase tracking-wide text-slate-500">Da CSV</div>
<div class="mt-1 text-xl font-semibold text-slate-900">{{ (int) ($box['catalogo_modulo']['csv_products'] ?? 0) }}</div>
</div>
<div class="rounded-md bg-slate-50 p-3">
<div class="text-[11px] uppercase tracking-wide text-slate-500">Manuali</div>
<div class="mt-1 text-xl font-semibold text-slate-900">{{ (int) ($box['catalogo_modulo']['manual_products'] ?? 0) }}</div>
</div>
<div class="rounded-md bg-slate-50 p-3">
<div class="text-[11px] uppercase tracking-wide text-slate-500">Solo interni</div>
<div class="mt-1 text-xl font-semibold text-slate-900">{{ (int) ($box['catalogo_modulo']['internal_only'] ?? 0) }}</div>
</div>
<div class="rounded-md bg-slate-50 p-3">
<div class="text-[11px] uppercase tracking-wide text-slate-500">Link sorgente</div>
<div class="mt-1 text-xl font-semibold text-slate-900">{{ (int) ($box['catalogo_modulo']['private_links'] ?? 0) }}</div>
</div>
<div class="rounded-md bg-slate-50 p-3">
<div class="text-[11px] uppercase tracking-wide text-slate-500">Offerte</div>
<div class="mt-1 text-xl font-semibold text-slate-900">{{ (int) ($box['catalogo_modulo']['offers'] ?? 0) }}</div>
</div>
<div class="rounded-md bg-slate-50 p-3">
<div class="text-[11px] uppercase tracking-wide text-slate-500">Amazon link</div>
<div class="mt-1 text-xl font-semibold text-slate-900">{{ (int) ($box['catalogo_modulo']['amazon_links'] ?? 0) }}</div>
</div>
</div>
</div>
<div class="rounded-lg border p-4" x-show="tab === 'catalogo'" x-cloak>
<div class="flex items-start justify-between gap-4">
<div>
<div class="text-lg font-semibold">Catalogo operativo interno</div>
<div class="mt-1 text-xs text-gray-500">Anagrafica prodotti/servizi del fornitore. I riferimenti esterni importati restano interni al gestionale e la pubblicazione usa il codice interno del catalogo.</div>
</div>
<div class="rounded-md bg-slate-100 px-3 py-2 text-xs text-slate-600">Le azioni operative sono state raccolte sopra.</div>
</div>
<div class="mt-4 grid grid-cols-2 gap-3 md:grid-cols-4">
<div class="rounded-md bg-slate-50 p-3">
<div class="text-[11px] uppercase tracking-wide text-slate-500">Prodotti</div>
<div class="mt-1 text-xl font-semibold text-slate-900">{{ (int) ($box['prodotti']['count'] ?? 0) }}</div>
</div>
<div class="rounded-md bg-slate-50 p-3">
<div class="text-[11px] uppercase tracking-wide text-slate-500">Serializzati</div>
<div class="mt-1 text-xl font-semibold text-slate-900">{{ (int) ($box['prodotti']['serializzati'] ?? 0) }}</div>
</div>
<div class="rounded-md bg-slate-50 p-3">
<div class="text-[11px] uppercase tracking-wide text-slate-500">Codici agganciati</div>
<div class="mt-1 text-xl font-semibold text-slate-900">{{ (int) ($box['prodotti']['identifiers'] ?? 0) }}</div>
</div>
<div class="rounded-md bg-slate-50 p-3">
<div class="text-[11px] uppercase tracking-wide text-slate-500">Media</div>
<div class="mt-1 text-xl font-semibold text-slate-900">{{ (int) ($box['prodotti']['with_media'] ?? 0) }}</div>
</div>
</div>
<div class="mt-4">
@include('filament.components.catalog.product-hub-panel', [
'title' => 'Hub prodotti fornitore',
'description' => 'Qui trovi anche i prodotti già importati. La scheda completa del catalogo ora vive nellhub prodotti dedicato, con TAB per panoramica, catalogo, seriali e righe acquisto.',
'rows' => $catalogoProdottiRows,
'emptyMessage' => 'Nessun prodotto presente per questo fornitore.',
'showStableUsage' => false,
])
<div class="mt-3 flex flex-wrap gap-2">
<a href="{{ $this->getProdottiHubUrl() }}" class="inline-flex items-center rounded-md bg-slate-900 px-3 py-2 text-xs font-medium text-white hover:bg-slate-800">Apri scheda hub prodotti</a>
<a href="{{ $this->getSerialiHubUrl() }}" class="inline-flex items-center rounded-md bg-white px-3 py-2 text-xs font-medium text-slate-700 ring-1 ring-inset ring-slate-300 hover:bg-slate-50">Apri scheda seriali</a>
</div>
</div>
</div>
<div class="rounded-lg border p-4" x-show="tab === 'profilo' || tab === 'contabilita'" x-cloak>
<div class="text-lg font-semibold">Pagamenti</div>
<div class="mt-2 grid grid-cols-1 gap-2 md:grid-cols-2">
<div><span class="font-medium">IBAN:</span> {{ $fornitore->iban ?? '-' }}</div>
<div><span class="font-medium">BIC:</span> {{ $fornitore->bic ?? '-' }}</div>
<div><span class="font-medium">SIA:</span> {{ $fornitore->sia_effettivo ?? '-' }}</div>
<div class="md:col-span-2"><span class="font-medium">Modalità predefinita:</span> {{ $fornitore->modalita_pagamento_predefinita ?? '-' }}</div>
</div>
</div>
<div class="rounded-lg border p-4" x-show="tab === 'profilo'" x-cloak>
<div class="text-lg font-semibold">Anagrafica unica (Rubrica)</div>
@if($fornitore->rubrica)
<div class="mt-2 grid grid-cols-1 gap-2 md:grid-cols-2">
<div><span class="font-medium">Codice:</span> {{ $fornitore->rubrica->codice_univoco ?? '-' }}</div>
<div><span class="font-medium">Nominativo:</span> {{ $fornitore->rubrica->nome_completo ?? '-' }}</div>
<div><span class="font-medium">Categoria:</span> {{ $fornitore->rubrica->categoria ?? '-' }}</div>
<div><span class="font-medium">Stato:</span> {{ $fornitore->rubrica->stato ?? '-' }}</div>
</div>
@else
<div class="mt-2 text-sm text-gray-600">Nessun collegamento presente.</div>
@endif
</div>
<div class="rounded-lg border p-4" x-data="fornitoreTagsBox(@js($tagSuggestions))" x-show="tab === 'profilo'" x-cloak>
<div class="text-lg font-semibold">TAG Fornitore</div>
<div class="mt-1 text-xs text-gray-500">Digita tag separati da virgola. Premi TAB per accettare il suggerimento corrente e aggiungere automaticamente una virgola.</div>
<div class="mt-3">
<input
id="fornitore-tags-input"
type="text"
list="fornitore-tags-suggestions"
wire:model.defer="tagsInput"
autocomplete="off"
class="w-full rounded-lg border-gray-300 text-sm"
placeholder="es. idr, manut, assicur..."
x-on:input="updateSuggestion($event.target.value)"
x-on:keydown.tab.prevent="acceptSuggestion($event.target)"
x-on:keydown.comma="clearHint()"
/>
<datalist id="fornitore-tags-suggestions">
@foreach($tagSuggestions as $tag)
<option value="{{ $tag }}"></option>
@endforeach
</datalist>
<div class="mt-1 text-xs text-gray-500" x-show="hint !== ''" x-text="'Suggerimento: ' + hint"></div>
</div>
<div class="mt-3 flex flex-wrap gap-2">
<x-filament::button color="primary" type="button" wire:click="saveTags">Salva TAG</x-filament::button>
<x-filament::button color="gray" type="button" wire:click="importLegacyTags">Importa da legacy</x-filament::button>
</div>
</div>
</div>
<div class="min-w-0 space-y-6">
@php
$features = is_array($box['fornitore_features'] ?? null) ? $box['fornitore_features'] : [];
$acqua = is_array($features['acqua'] ?? null) ? $features['acqua'] : [];
$acquaEnabled = (bool) ($acqua['enabled'] ?? false);
$acquaScan = (bool) ($acqua['scan_enabled'] ?? false);
@endphp
<div class="rounded-lg border p-4" x-show="tab === 'assistenza'" x-cloak>
<div class="flex items-start justify-between gap-4">
<div>
<div class="text-base font-semibold">TecnoRepair / seriali</div>
<div class="mt-1 text-sm text-gray-600">Import MDB in anagrafica fornitore e aggancio automatico di seriali e prodotti assistenza.</div>
</div>
<x-filament::button size="sm" color="gray" type="button" wire:click="mountAction('importa_tecnorepair')">
Importa TecnoRepair
</x-filament::button>
</div>
<div class="mt-4 grid grid-cols-2 gap-3 text-sm">
<div class="flex items-center justify-between rounded-md bg-slate-50 px-3 py-2">
<span>Schede</span>
<span class="font-semibold">{{ (int) ($box['tecnorepair']['schede'] ?? 0) }}</span>
</div>
<div class="flex items-center justify-between rounded-md bg-slate-50 px-3 py-2">
<span>Aperte / attesa</span>
<span class="font-semibold">{{ (int) ($box['tecnorepair']['aperte'] ?? 0) }}</span>
</div>
<div class="flex items-center justify-between rounded-md bg-slate-50 px-3 py-2">
<span>Chiuse</span>
<span class="font-semibold">{{ (int) ($box['tecnorepair']['chiuse'] ?? 0) }}</span>
</div>
<div class="flex items-center justify-between rounded-md bg-slate-50 px-3 py-2">
<span>Seriali</span>
<span class="font-semibold">{{ (int) ($box['tecnorepair']['seriali'] ?? 0) }}</span>
</div>
</div>
<div class="mt-4 space-y-2 text-sm">
@forelse($tecnorepairRows as $row)
<div class="rounded-md border px-3 py-2">
<div class="flex items-start justify-between gap-3">
<div class="min-w-0">
<div class="font-medium">{{ $row['legacy_numero'] !== '' ? $row['legacy_numero'] : ('Scheda #' . $row['id']) }}</div>
<div class="truncate text-gray-600">{{ $row['product_model'] !== '' ? $row['product_model'] : '-' }}</div>
<div class="truncate text-xs text-gray-500">Codice: {{ $row['product_code'] !== '' ? $row['product_code'] : '-' }} | Seriale: {{ $row['serial_number'] !== '' ? $row['serial_number'] : '-' }}</div>
</div>
<div class="text-right">
<div class="rounded px-2 py-0.5 text-[10px] font-semibold {{ ($row['status_bucket'] ?? '') === 'closed' ? 'bg-emerald-100 text-emerald-800' : ((($row['status_bucket'] ?? '') === 'waiting') ? 'bg-amber-100 text-amber-800' : 'bg-rose-100 text-rose-800') }}">
{{ $row['status_label'] !== '' ? $row['status_label'] : ($row['status_bucket'] !== '' ? $row['status_bucket'] : 'stato') }}
</div>
<div class="mt-1 text-xs text-gray-500">Allegati: {{ (int) ($row['allegati_count'] ?? 0) }}</div>
</div>
</div>
</div>
@empty
<div class="text-sm text-gray-600">Nessuna scheda TecnoRepair collegata a questo fornitore.</div>
@endforelse
</div>
</div>
<div class="rounded-lg border p-4" x-show="tab === 'assistenza'" x-cloak>
<div class="text-base font-semibold">Automazioni / Consumi</div>
<div class="mt-1 text-sm text-gray-600">Configura acquisizione dati (PDF) e scansione massiva sulle FE già importate.</div>
<div class="mt-3 space-y-1 text-sm">
<div class="flex items-center justify-between">
<span class="font-medium">Acqua</span>
<span>{{ $acquaEnabled ? 'ATTIVA' : 'disattiva' }}</span>
</div>
<div class="flex items-center justify-between">
<span class="font-medium">Scansione batch</span>
<span>{{ $acquaScan ? 'ATTIVA' : 'disattiva' }}</span>
</div>
<div class="flex items-center justify-between">
<span class="font-medium">Righe FE</span>
<span>{{ (bool) ($fornitore->escludi_righe_fe ?? false) ? 'escluse' : 'incluse' }}</span>
</div>
</div>
<div class="mt-4 rounded-md bg-slate-100 px-3 py-2 text-xs text-slate-600">Le azioni FE/acqua sono state raccolte nella toolbar in alto.</div>
</div>
<div class="rounded-lg border p-4" x-show="tab === 'contabilita'" x-cloak>
<div class="flex items-start justify-between gap-4">
<div>
<div class="text-lg font-semibold">Fatture (stabile attivo)</div>
<div class="text-sm text-gray-600">Riepilogo AdE / FE / Contabilità / RA</div>
</div>
<a
class="text-sm font-medium text-primary-600 hover:underline"
href="{{ \App\Filament\Pages\Contabilita\FattureElettronicheP7mRicevute::getUrl(panel: 'admin-filament') }}"
>
Apri FE
</a>
</div>
<div class="mt-4 space-y-2 text-sm">
<div class="flex items-center justify-between">
<span class="font-semibold">Totale da pagare</span>
<span class="font-semibold">{{ number_format((float) ($box['da_pagare']['importo'] ?? 0), 2, ',', '.') }} </span>
</div>
<div class="text-xs text-gray-600">
Fonte: {{ (string) ($box['da_pagare']['source'] ?? 'n/d') }}
</div>
<div class="border-t pt-2"></div>
<div class="flex items-center justify-between">
<span class="font-medium">AdE (staging)</span>
<span>{{ (int) ($box['ade']['count'] ?? 0) }} {{ number_format((float) ($box['ade']['lordo'] ?? 0), 2, ',', '.') }} </span>
</div>
<div class="flex items-center justify-between">
<span class="font-medium">FE importate</span>
<span>{{ (int) ($box['fe']['count'] ?? 0) }} {{ number_format((float) ($box['fe']['totale'] ?? 0), 2, ',', '.') }} </span>
</div>
<div class="flex items-center justify-between">
<span class="font-medium">FE contabilizzate</span>
<span>{{ (int) ($box['fe']['contabilizzate'] ?? 0) }}</span>
</div>
<div class="mt-2 border-t pt-2">
<div class="flex items-center justify-between">
<span class="font-medium">Da pagare (contabilità)</span>
<span>{{ (int) ($box['contabilita']['aperte_count'] ?? 0) }} {{ number_format((float) ($box['contabilita']['aperte_netto'] ?? 0), 2, ',', '.') }} </span>
</div>
<div class="flex items-center justify-between">
<span class="font-medium">Pagate (contabilità)</span>
<span>{{ (int) ($box['contabilita']['pagate_count'] ?? 0) }} {{ number_format((float) ($box['contabilita']['pagate_netto'] ?? 0), 2, ',', '.') }} </span>
</div>
</div>
<div class="mt-2 border-t pt-2">
<div class="flex items-center justify-between">
<span class="font-medium">RA da versare</span>
<span>{{ number_format((float) ($box['ra']['da_versare'] ?? 0), 2, ',', '.') }} </span>
</div>
<div class="flex items-center justify-between">
<span class="font-medium">RA versata</span>
<span>{{ number_format((float) ($box['ra']['versata'] ?? 0), 2, ',', '.') }} </span>
</div>
<div class="flex items-center justify-between">
<span class="font-medium">RA compensata</span>
<span>{{ number_format((float) ($box['ra']['compensata'] ?? 0), 2, ',', '.') }} </span>
</div>
</div>
</div>
</div>
<div class="rounded-lg border p-4" x-show="tab === 'contabilita'" x-cloak>
<div class="text-base font-semibold">Anteprima AdE</div>
@if(!empty($anteprimaAde))
<div class="mt-3 space-y-2 text-sm">
@foreach($anteprimaAde as $r)
<div class="flex items-start justify-between gap-3">
<div class="min-w-0">
<div class="font-medium">{{ $r['data'] ?: '-' }} {{ $r['numero'] ?: '-' }}</div>
<div class="truncate text-gray-600">SDI: {{ $r['sdi_file'] ?: '-' }}</div>
</div>
<div class="whitespace-nowrap">{{ number_format((float) ($r['lordo'] ?? 0), 2, ',', '.') }} </div>
</div>
@endforeach
</div>
@else
<div class="mt-2 text-sm text-gray-600">Nessuna fattura AdE trovata per questo fornitore.</div>
@endif
</div>
<div class="rounded-lg border p-4" x-show="tab === 'contabilita'" x-cloak>
<div class="text-base font-semibold">Anteprima FE</div>
@if(!empty($anteprimaFe))
<div class="mt-3 space-y-2 text-sm">
@foreach($anteprimaFe as $r)
<a
class="block rounded-md px-2 py-1 hover:bg-gray-50"
href="{{ \App\Filament\Pages\Contabilita\FatturaElettronicaScheda::getUrl(['record' => (int) $r['id']], panel: 'admin-filament') }}"
>
<div class="flex items-start justify-between gap-3">
<div class="min-w-0">
<div class="font-medium">{{ $r['data'] ?: '-' }} {{ $r['numero'] ?: '-' }}</div>
<div class="truncate text-gray-600">SDI: {{ $r['sdi_file'] ?: '-' }} @if(($r['stato'] ?? '') !== '') {{ $r['stato'] }} @endif</div>
</div>
<div class="whitespace-nowrap">{{ number_format((float) ($r['totale'] ?? 0), 2, ',', '.') }} </div>
</div>
</a>
@endforeach
</div>
@else
<div class="mt-2 text-sm text-gray-600">Nessuna FE importata trovata per questo fornitore.</div>
@endif
</div>
<div class="rounded-lg border p-4" x-show="tab === 'contabilita'" x-cloak>
<div class="text-base font-semibold">Anteprima contabilità</div>
@if(!empty($anteprimaContabilita))
<div class="mt-3 space-y-2 text-sm">
@foreach($anteprimaContabilita as $r)
<a
class="block rounded-md px-2 py-1 hover:bg-gray-50"
href="{{ \App\Filament\Pages\Contabilita\FatturaFornitoreScheda::getUrl(['record' => (int) $r['id']], panel: 'admin-filament') }}"
>
<div class="flex items-start justify-between gap-3">
<div class="min-w-0">
<div class="font-medium">{{ $r['data'] ?: '-' }} {{ $r['numero'] ?: '-' }}</div>
<div class="truncate text-gray-600">Stato: {{ $r['stato'] ?: '-' }}</div>
</div>
<div class="whitespace-nowrap">{{ number_format((float) ($r['netto'] ?? 0), 2, ',', '.') }} </div>
</div>
</a>
@endforeach
</div>
@else
<div class="mt-2 text-sm text-gray-600">Nessuna registrazione contabile trovata per questo fornitore.</div>
@endif
</div>
<div class="rounded-lg border p-4" x-show="tab === 'contabilita'" x-cloak>
<div class="text-base font-semibold">Fatture associate (fornitore)</div>
@if(!empty($fattureAssociate))
<div class="mt-3 space-y-2 text-sm">
@foreach($fattureAssociate as $r)
<a
class="block rounded-md px-2 py-1 hover:bg-gray-50"
href="{{ \App\Filament\Pages\Contabilita\FatturaFornitoreScheda::getUrl(['record' => (int) $r['id']], panel: 'admin-filament') }}"
>
<div class="flex items-start justify-between gap-3">
<div class="min-w-0">
<div class="font-medium">{{ $r['data'] ?: '-' }} {{ $r['numero'] ?: '-' }}</div>
<div class="truncate text-gray-600">{{ $r['source'] }} @if(($r['stato'] ?? '') !== '') {{ $r['stato'] }} @endif</div>
</div>
<div class="whitespace-nowrap text-right">
<div>{{ number_format((float) ($r['totale'] ?? 0), 2, ',', '.') }} </div>
<div class="text-xs text-gray-500">Netto {{ number_format((float) ($r['netto'] ?? 0), 2, ',', '.') }} </div>
</div>
</div>
</a>
@endforeach
</div>
@else
<div class="mt-2 text-sm text-gray-600">Nessuna fattura associata trovata per questo fornitore.</div>
@endif
</div>
<div class="rounded-lg border p-4" x-show="tab === 'contabilita'" x-cloak>
<div class="text-base font-semibold">RA versate/compensate</div>
@if(!empty($raVersateRows))
<div class="mt-3 space-y-2 text-sm">
@foreach($raVersateRows as $ra)
<div class="rounded-md px-2 py-1">
<div class="flex items-start justify-between gap-3">
<div class="min-w-0">
<div class="font-medium">{{ $ra['data_versamento'] ?: '-' }} {{ strtoupper($ra['stato'] ?: '-') }}</div>
<div class="truncate text-gray-600">Tributo {{ $ra['tributo'] ?: '1040' }} @if(($ra['f24'] ?? '') !== '') F24 {{ $ra['f24'] }} @endif</div>
</div>
<div class="whitespace-nowrap">{{ number_format((float) ($ra['importo'] ?? 0), 2, ',', '.') }} </div>
</div>
</div>
@endforeach
</div>
@else
<div class="mt-2 text-sm text-gray-600">Nessuna RA versata/compensata trovata per questo fornitore.</div>
@endif
</div>
</div>
</div>
</div>
<script>
function fornitoreTagsBox(suggestions) {
return {
hint: '',
suggestions: Array.isArray(suggestions) ? suggestions : [],
clearHint() {
this.hint = '';
},
updateSuggestion(value) {
const token = this.currentToken(value);
if (!token) {
this.hint = '';
return;
}
const lower = token.toLowerCase();
const found = this.suggestions.find((item) => item.toLowerCase().startsWith(lower));
this.hint = found && found.toLowerCase() !== lower ? found : '';
},
acceptSuggestion(inputEl) {
const value = inputEl.value || '';
const token = this.currentToken(value);
if (!token) {
inputEl.value = value.endsWith(',') ? value : value + ', ';
inputEl.dispatchEvent(new Event('input'));
this.hint = '';
return;
}
const lower = token.toLowerCase();
const found = this.suggestions.find((item) => item.toLowerCase().startsWith(lower));
if (!found) {
inputEl.value = value.endsWith(',') ? value : value + ', ';
inputEl.dispatchEvent(new Event('input'));
this.hint = '';
return;
}
const idx = value.lastIndexOf(',');
const prefix = idx >= 0 ? value.slice(0, idx + 1) : '';
const next = (prefix ? prefix + ' ' : '') + found + ', ';
inputEl.value = next;
inputEl.dispatchEvent(new Event('input'));
this.hint = '';
},
currentToken(value) {
const idx = value.lastIndexOf(',');
const token = idx >= 0 ? value.slice(idx + 1) : value;
return token.trim();
},
};
}
</script>
<x-filament-actions::modals />
</x-filament-panels::page>