Reorganize supplier scheda and harden legacy support flows

This commit is contained in:
michele 2026-04-11 10:09:42 +00:00
parent ab074eb321
commit 7c74942d0f
11 changed files with 314 additions and 53 deletions

View File

@ -5,6 +5,7 @@ # Changelog
## [Unreleased]
- Initial open-source release prep.
- Reorganized the supplier scheda into operational tabs, linked supplier rubrica-clienti back to anagrafica unica, fixed the supplier ticket-intervento Blade parse error, and hardened legacy supplier matching plus SMDR/Post-it labeling for Git-first rollout.
- Reworked Supporto > Aggiornamento Nodo to show local/remote commit dates, explicit pending commit counts, and a clear note that Git updates do not move storage archives or private uploaded data.
- Reorganized the Importazione Archivi operational tab so the action buttons come first, followed by the explanatory blocks and command fields.
- Removed active-stabile filtering from topbar avvisi/urgenze counters and from the Google ticket-operativi box so cross-stabile operational tickets stay visible.

View File

@ -250,20 +250,6 @@ private function resolveLocalFornitore(object $legacy): ?Fornitore
$cf = trim((string) ($legacy->codice_fiscale ?? ''));
$ragione = trim((string) ($legacy->ragione_sociale ?? ''));
if ($legacyId > 0) {
$found = Fornitore::query()->where('old_id', $legacyId)->first();
if ($found instanceof Fornitore) {
return $found;
}
}
if ($legacyCod !== '' && ctype_digit($legacyCod)) {
$found = Fornitore::query()->where('old_id', (int) $legacyCod)->first();
if ($found instanceof Fornitore) {
return $found;
}
}
if ($piva !== '') {
$found = Fornitore::query()->where('partita_iva', $piva)->first();
if ($found instanceof Fornitore) {
@ -284,11 +270,54 @@ private function resolveLocalFornitore(object $legacy): ?Fornitore
$normalizedRagione = $this->normalizeComparableString($ragione);
if ($normalizedRagione !== '') {
return Fornitore::query()
$found = Fornitore::query()
->get(['id', 'ragione_sociale'])
->first(function (Fornitore $fornitore) use ($normalizedRagione): bool {
return $this->normalizeComparableString((string) ($fornitore->ragione_sociale ?? '')) === $normalizedRagione;
});
if ($found instanceof Fornitore) {
return $found;
}
}
return $this->resolveLocalFornitoreByLegacyIds($legacyId, $legacyCod, $piva, $cf, $normalizedRagione);
}
private function resolveLocalFornitoreByLegacyIds(int $legacyId, string $legacyCod, string $piva, string $cf, string $normalizedRagione): ?Fornitore
{
$candidateIds = collect([
$legacyId > 0 ? $legacyId : null,
($legacyCod !== '' && ctype_digit($legacyCod)) ? (int) $legacyCod : null,
])
->filter(fn($value): bool => is_int($value) && $value > 0)
->unique()
->values();
foreach ($candidateIds as $candidateId) {
$matches = Fornitore::query()
->where('old_id', (int) $candidateId)
->get(['id', 'partita_iva', 'codice_fiscale', 'ragione_sociale']);
if ($matches->count() === 1) {
return $matches->first();
}
$filtered = $matches->filter(function (Fornitore $fornitore) use ($piva, $cf, $normalizedRagione): bool {
if ($piva !== '' && trim((string) ($fornitore->partita_iva ?? '')) === $piva) {
return true;
}
if ($cf !== '' && trim((string) ($fornitore->codice_fiscale ?? '')) === $cf) {
return true;
}
return $normalizedRagione !== ''
&& $this->normalizeComparableString((string) ($fornitore->ragione_sociale ?? '')) === $normalizedRagione;
})->values();
if ($filtered->count() === 1) {
return $filtered->first();
}
}
return null;

View File

@ -3,6 +3,7 @@
use App\Models\ChiamataPostIt;
use App\Models\CommunicationMessage;
use App\Services\Anagrafiche\RubricaPhoneLookupService;
use App\Services\Cti\PbxRoutingService;
use Carbon\Carbon;
use Illuminate\Console\Command;
@ -243,15 +244,17 @@ public function handle(): int
if ($writePostIt && Schema::hasTable('chiamate_post_it') && $this->shouldCreateOperationalPostIt($parsed)) {
try {
$lineSuffix = ! empty($parsed['co']) ? ' - linea ' . (string) $parsed['co'] : '';
$lineLabel = $this->resolveDisplayLineLabel($parsed, $routing);
$lineSuffix = $lineLabel !== '' ? ' - linea ' . $lineLabel : '';
$isMissedResponseGroup = $this->isMissedResponseGroupCall($parsed, $routing);
$caller = $this->resolveCallerDisplay($phone, $amministratoreId);
ChiamataPostIt::query()->create([
'telefono' => $phone,
'stabile_id' => $stabileId,
'stabile_id' => $caller['has_rubrica'] ? $stabileId : null,
'creato_da_user_id' => $assignedUserId,
'nome_chiamante' => 'SMDR INBOUND',
'nome_chiamante' => $caller['label'],
'oggetto' => 'Chiamata in entrata da SMDR' . $lineSuffix,
'nota' => $line,
'direzione' => 'in_arrivo',
@ -292,6 +295,42 @@ public function handle(): int
} while (true);
}
/**
* @return array{label:string,has_rubrica:bool}
*/
private function resolveCallerDisplay(?string $phone, ?int $amministratoreId): array
{
$digits = preg_replace('/\D+/', '', (string) $phone) ?: '';
if ($digits === '') {
return ['label' => 'Contatto non in Rubrica', 'has_rubrica' => false];
}
$rubrica = app(RubricaPhoneLookupService::class)->findByPhone($digits, (int) ($amministratoreId ?? 0));
if ($rubrica) {
$label = trim((string) ($rubrica->ragione_sociale ?: (($rubrica->nome ?? '') . ' ' . ($rubrica->cognome ?? ''))));
if ($label !== '') {
return ['label' => $label, 'has_rubrica' => true];
}
}
return ['label' => 'Contatto non in Rubrica', 'has_rubrica' => false];
}
private function resolveDisplayLineLabel(array $parsed, array $routing): string
{
$lineLabel = trim((string) ($routing['line_label'] ?? ''));
if ($lineLabel !== '') {
return $lineLabel;
}
$rawLine = preg_replace('/\D+/', '', (string) ($parsed['co'] ?? '')) ?: '';
if ($rawLine === '') {
return '';
}
return self::CO_PUBLIC_LINES[$rawLine] ?? (string) ($parsed['co'] ?? '');
}
private function parseSmdrLine(string $line): ?array
{
$normalized = trim((string) preg_replace('/^\[\d+\]\s*/', '', $line));

View File

@ -2,6 +2,7 @@
namespace App\Filament\Pages\Fornitore;
use App\Filament\Pages\Fornitore\Concerns\ResolvesOperatoreContext;
use App\Filament\Pages\Gescon\RubricaUniversaleScheda;
use App\Models\Fornitore;
use App\Models\TicketIntervento;
use App\Models\User;
@ -118,6 +119,8 @@ public function refreshRows(): void
'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') ?: '-',
@ -164,6 +167,8 @@ protected function resolveCallerDataForRubrica(TicketIntervento $intervento): ar
$telefono = $parsed['telefono'];
$email = '';
$origine = 'Descrizione ticket';
$rubricaUrl = null;
$rubricaLabel = '';
if ($ticket?->soggettoRichiedente) {
$soggetto = $ticket->soggettoRichiedente;
@ -171,6 +176,8 @@ protected function resolveCallerDataForRubrica(TicketIntervento $intervento): ar
$telefono = trim((string) ($soggetto->telefono ?? '')) ?: $telefono;
$email = trim((string) ($soggetto->email ?? ''));
$origine = 'Richiedente ticket';
$rubricaUrl = RubricaUniversaleScheda::getUrl(['record' => (int) $soggetto->id], panel: 'admin-filament');
$rubricaLabel = trim((string) ($soggetto->categoria ?? ''));
} elseif ($ticket?->apertoDaUser) {
$contatto = trim((string) ($ticket->apertoDaUser->name ?? '')) ?: $contatto;
$email = trim((string) ($ticket->apertoDaUser->email ?? ''));
@ -192,6 +199,8 @@ protected function resolveCallerDataForRubrica(TicketIntervento $intervento): ar
'email' => $email,
'origine' => $origine,
'stabile' => (string) ($ticket?->stabile?->denominazione ?? '-'),
'rubrica_url' => $rubricaUrl,
'rubrica_label'=> $rubricaLabel,
'identity_key' => $identityKey,
];
}

View File

@ -100,6 +100,8 @@ class FornitoreScheda extends Page
public ?int $dipendenteRubricaId = null;
public string $sectionTab = 'profilo';
/** @var array<int, array<string, mixed>> */
public array $dipendenteRubricaMatches = [];
@ -194,6 +196,23 @@ public function mount(int | string $record): void
$this->loadTagSuggestions();
$this->refreshDipendentiRows();
$this->refreshCatalogRows();
$requestedTab = (string) request()->query('tab', 'profilo');
if ($this->isValidSectionTab($requestedTab)) {
$this->sectionTab = $requestedTab;
}
}
public function setSectionTab(string $tab): void
{
if ($this->isValidSectionTab($tab)) {
$this->sectionTab = $tab;
}
}
private function isValidSectionTab(string $tab): bool
{
return in_array($tab, ['profilo', 'accessi', 'assistenza', 'catalogo', 'contabilita'], true);
}
public function creaDipendenteFornitore(): void

View File

@ -9,6 +9,7 @@
use App\Models\Ticket;
use App\Models\User;
use App\Services\Anagrafiche\RubricaPhoneLookupService;
use App\Services\Cti\PbxRoutingService;
use BackedEnum;
use Filament\Notifications\Notification;
use Filament\Pages\Page;
@ -20,6 +21,16 @@
class PostItGestione extends Page
{
/** @var array<string,string> */
private const SMDR_PUBLIC_LINE_FALLBACKS = [
'1' => '0639731100',
'01' => '0639731100',
'0001' => '0639731100',
'3' => '0688812703',
'03' => '0688812703',
'0003' => '0688812703',
];
protected static ?string $title = 'Post-it Gestione';
protected static ?string $navigationLabel = 'Post-it Gestione';
@ -508,6 +519,7 @@ public function creaPostItDaMessaggio(int $messageId): void
$phone = (string) ($message->phone_number ?: ($smdr['dial_number'] ?? ''));
$nominativo = $this->getTecnicoNominativo($message);
$lineLabel = $this->getTecnicoLineaLabel($message);
$callerLabel = $this->resolveTecnicoCallerLabel($message, $phone, $nominativo);
$direction = match (strtolower(trim((string) $message->direction))) {
'inbound' => 'Chiamata ricevuta',
'outbound' => 'Chiamata effettuata',
@ -534,10 +546,10 @@ public function creaPostItDaMessaggio(int $messageId): void
}
$postIt = ChiamataPostIt::query()->create([
'stabile_id' => $message->stabile_id,
'stabile_id' => $this->resolveOperationalPostItStabileId($message, $phone),
'creato_da_user_id' => Auth::id(),
'telefono' => $message->phone_number,
'nome_chiamante' => $nominativo ?: ($phone !== '' ? $phone : ($sourceLabel . ' ' . strtoupper((string) $message->direction))),
'telefono' => $phone !== '' ? $phone : $message->phone_number,
'nome_chiamante' => $callerLabel !== '' ? $callerLabel : ($sourceLabel . ' ' . strtoupper((string) $message->direction)),
'direzione' => $this->normalizePostItDirection((string) $message->direction),
'origine' => $sourceKey . '_table',
'origine_id' => (string) $message->id,
@ -634,8 +646,7 @@ public function getTecnicoProviderLabel(CommunicationMessage $message): string
public function getTecnicoLineaLabel(CommunicationMessage $message): string
{
if ((string) $message->channel === 'smdr') {
$line = trim((string) data_get($message->metadata, 'smdr.co', ''));
return $line !== '' ? $line : '-';
return $this->resolveSmdrLineLabel($message);
}
$eventType = trim((string) data_get($message->metadata, 'event_type', ''));
@ -645,6 +656,24 @@ public function getTecnicoLineaLabel(CommunicationMessage $message): string
return $parts !== [] ? implode(' / ', $parts) : '-';
}
public function shouldShowPostItStabile(ChiamataPostIt $postIt): bool
{
if (! $postIt->stabile) {
return false;
}
if ($this->resolveGroupedRubricaId($postIt)) {
return true;
}
$message = $this->resolveMessageFromPostIt($postIt);
if ($message instanceof CommunicationMessage && ! $this->isInternalSmdrMessage($message)) {
return false;
}
return ! $this->isGenericCallerLabel((string) ($postIt->nome_chiamante ?? ''), (string) ($postIt->telefono ?? ''));
}
private function matchesTecnicoLineFilter(CommunicationMessage $message, string $filter): bool
{
$needle = mb_strtolower(trim($filter));
@ -817,12 +846,12 @@ private function resolveGroupedCallerLabel(array $items): string
}
$label = trim((string) ($item->nome_chiamante ?? ''));
if ($label !== '') {
if ($label !== '' && ! $this->isGenericCallerLabel($label, (string) ($item->telefono ?? ''))) {
return $label;
}
}
return 'Chiamante non identificato';
return 'Contatto non in Rubrica';
}
private function resolveGroupedPhoneLabel(array $items): string
@ -895,6 +924,79 @@ private function buildGroupedCollection($items)
->values();
}
private function resolveOperationalPostItStabileId(CommunicationMessage $message, string $phone): ?int
{
if ($this->isInternalSmdrMessage($message)) {
return (int) ($message->stabile_id ?? 0) ?: null;
}
if ($this->resolveRubricaIdByPhone($phone)) {
return (int) ($message->stabile_id ?? 0) ?: null;
}
return null;
}
private function resolveTecnicoCallerLabel(CommunicationMessage $message, string $phone, ?string $nominativo): string
{
$label = trim((string) $nominativo);
if ($label !== '') {
return $label;
}
if ($this->isInternalSmdrMessage($message)) {
$extension = $this->extractExtensionFromMessage($message);
return $extension ? 'Interno ' . $extension : 'Interno PBX';
}
return 'Contatto non in Rubrica';
}
private function resolveSmdrLineLabel(CommunicationMessage $message): string
{
$stored = trim((string) data_get($message->metadata, 'smdr.line_label', ''));
if ($stored !== '') {
return $stored;
}
$rawLine = trim((string) data_get($message->metadata, 'smdr.co', ''));
if ($rawLine === '') {
return '-';
}
$routing = app(PbxRoutingService::class)->resolveByIncomingLine($rawLine);
$lineLabel = trim((string) ($routing['line_label'] ?? ''));
if ($lineLabel !== '') {
return $lineLabel;
}
$normalized = app(PbxRoutingService::class)->normalizeLineNumber($rawLine);
return self::SMDR_PUBLIC_LINE_FALLBACKS[$normalized] ?? $rawLine;
}
private function isGenericCallerLabel(string $label, string $phone = ''): bool
{
$normalizedLabel = mb_strtolower(trim($label));
if ($normalizedLabel === '') {
return true;
}
if (in_array($normalizedLabel, ['chiamante non identificato', 'contatto non in rubrica'], true)) {
return true;
}
if (str_starts_with($normalizedLabel, 'smdr ') || str_starts_with($normalizedLabel, 'panasonic cti ')) {
return true;
}
$labelDigits = preg_replace('/\D+/', '', $label) ?: '';
$phoneDigits = preg_replace('/\D+/', '', $phone) ?: '';
return $labelDigits !== '' && $phoneDigits !== '' && $labelDigits === $phoneDigits;
}
public function richiediClickToCallDaMessaggio(int $messageId): void
{
$user = Auth::user();
@ -1044,8 +1146,8 @@ private function resolveRubricaIdByPhone(?string $phone): ?int
return $this->rubricaPhoneCache[$digits];
}
$adminId = (int) (Auth::user()?->amministratore?->id ?? 0);
$result = app(RubricaPhoneLookupService::class)->findByPhone($digits, $adminId)?->id;
$adminId = (int) (Auth::user()?->amministratore?->id ?? 0);
$result = app(RubricaPhoneLookupService::class)->findByPhone($digits, $adminId)?->id;
$result = is_numeric($result) ? (int) $result : null;
$this->rubricaPhoneCache[$digits] = $result;

View File

@ -6,7 +6,7 @@
<div class="text-lg font-semibold">Rubrica clienti fornitore</div>
<div class="text-sm text-gray-600">
@if($this->fornitoreLabel)
Contatti emersi dai ticket e dalle lavorazioni di {{ $this->fornitoreLabel }}.
Contatti emersi dai ticket e dalle lavorazioni di {{ $this->fornitoreLabel }}, agganciati quando possibile all'anagrafica unica.
@else
Seleziona un fornitore per aprire la rubrica clienti.
@endif
@ -19,6 +19,10 @@
</div>
</div>
<div class="rounded-xl border border-slate-200 bg-slate-50 px-4 py-3 text-sm text-slate-700">
Questa vista resta stretta sui contatti del singolo fornitore, ma il punto di verità anagrafico rimane la scheda dell'anagrafica unica. Da qui puoi filtrare i contatti operativi e saltare direttamente sulla scheda condivisa quando il legame esiste gia.
</div>
@if($this->missingAdminContext)
<div class="rounded-xl border border-amber-300 bg-amber-50 p-4 text-sm text-amber-800">
Questa vista richiede un fornitore selezionato.
@ -38,10 +42,16 @@
<div>
<div class="font-semibold">{{ $row['contatto'] }}</div>
<div class="mt-1 text-xs text-gray-500">Origine: {{ $row['origine'] }} · Interventi: {{ $row['totale_interventi'] }}</div>
@if($row['rubrica_url'])
<div class="mt-1 text-xs text-indigo-700">Anagrafica unica collegata{{ $row['rubrica_label'] !== '' ? ' · ' . $row['rubrica_label'] : '' }}</div>
@endif
<div class="mt-2 text-xs text-gray-500">Stabile: {{ $row['stabile'] }}</div>
<div class="mt-1 text-xs text-gray-500">Ultimo aggiornamento: {{ $row['updated_at'] }}</div>
</div>
<div class="flex flex-wrap gap-2">
@if($row['rubrica_url'])
<a href="{{ $row['rubrica_url'] }}" class="inline-flex items-center rounded-md bg-white px-3 py-1.5 text-xs font-medium text-indigo-700 ring-1 ring-inset ring-indigo-300 hover:bg-indigo-50">Scheda anagrafica</a>
@endif
@if($row['telefono'] !== '')
<a href="tel:{{ preg_replace('/\s+/', '', (string) $row['telefono']) }}" class="inline-flex items-center rounded-md bg-emerald-600 px-3 py-1.5 text-xs font-medium text-white hover:bg-emerald-500">Chiama</a>
@endif

View File

@ -151,6 +151,7 @@
@endif
</div>
</div>
@endif
</div>
</div>
<div class="mt-4 whitespace-pre-wrap rounded-lg bg-gray-50 p-3 text-sm text-gray-700">{{ $ticket->descrizione }}</div>

View File

@ -1,7 +1,57 @@
<x-filament-panels::page>
@php
$tabs = [
'profilo' => 'Dati condivisi',
'accessi' => 'Dipendenti e accessi',
'assistenza' => 'Assistenza interna',
'catalogo' => 'Catalogo e prodotti',
'contabilita' => 'Contabilita',
];
@endphp
<div class="space-y-6" 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-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 grid-cols-1 gap-6 lg:grid-cols-3">
<div class="space-y-6 lg:col-span-2">
<div class="rounded-lg border p-4">
<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>
@ -15,7 +65,7 @@
</div>
</div>
<div class="rounded-lg border p-4">
<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">
@ -43,7 +93,7 @@
</div>
</div>
<div class="rounded-lg border p-4">
<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>
@ -168,7 +218,7 @@
</div>
</div>
<div class="rounded-lg border p-4">
<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>
@ -220,7 +270,7 @@
</div>
</div>
<div class="rounded-lg border p-4">
<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>
@ -352,7 +402,7 @@
</div>
</div>
<div class="rounded-lg border p-4">
<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>
@ -361,7 +411,7 @@
</div>
</div>
<div class="rounded-lg border p-4">
<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">
@ -375,7 +425,7 @@
@endif
</div>
<div class="rounded-lg border p-4" x-data="fornitoreTagsBox(@js($tagSuggestions))">
<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>
@ -415,7 +465,7 @@ class="w-full rounded-lg border-gray-300 text-sm"
$acquaScan = (bool) ($acqua['scan_enabled'] ?? false);
@endphp
<div class="rounded-lg border p-4">
<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>
@ -468,7 +518,7 @@ class="w-full rounded-lg border-gray-300 text-sm"
</div>
</div>
<div class="rounded-lg border p-4">
<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>
@ -500,7 +550,7 @@ class="w-full rounded-lg border-gray-300 text-sm"
</div>
</div>
<div class="rounded-lg border p-4">
<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>
@ -562,7 +612,7 @@ class="text-sm font-medium text-primary-600 hover:underline"
</div>
</div>
<div class="rounded-lg border p-4">
<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">
@ -581,7 +631,7 @@ class="text-sm font-medium text-primary-600 hover:underline"
@endif
</div>
<div class="rounded-lg border p-4">
<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">
@ -605,7 +655,7 @@ class="block rounded-md px-2 py-1 hover:bg-gray-50"
@endif
</div>
<div class="rounded-lg border p-4">
<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">
@ -629,7 +679,7 @@ class="block rounded-md px-2 py-1 hover:bg-gray-50"
@endif
</div>
<div class="rounded-lg border p-4">
<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">
@ -656,7 +706,7 @@ class="block rounded-md px-2 py-1 hover:bg-gray-50"
@endif
</div>
<div class="rounded-lg border p-4">
<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">
@ -678,6 +728,7 @@ class="block rounded-md px-2 py-1 hover:bg-gray-50"
</div>
</div>
</div>
</div>
<script>
function fornitoreTagsBox(suggestions) {

View File

@ -8,17 +8,17 @@
<input type="text" wire:model.live.debounce.250ms="collaboratoreRubricaSearch" 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>
@if($collaboratoreRubricaId)
@if($this->collaboratoreRubricaId)
<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">{{ $collaboratoreRubricaSearch }}</span>
Nominativo selezionato: <span class="font-semibold">{{ $this->collaboratoreRubricaSearch }}</span>
<button type="button" wire:click="resetCollaboratoreRubricaSelection" 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($collaboratoreRubricaMatches) > 0)
@if(count($this->collaboratoreRubricaMatches) > 0)
<div class="mt-3 space-y-2">
@foreach($collaboratoreRubricaMatches as $match)
<button type="button" wire:click="selezionaCollaboratoreRubrica({{ (int) $match['id'] }})" class="w-full rounded-lg border p-3 text-left {{ (int) $collaboratoreRubricaId === (int) $match['id'] ? 'border-emerald-400 bg-emerald-50 ring-1 ring-emerald-200' : 'bg-white hover:bg-gray-50' }}">
@foreach($this->collaboratoreRubricaMatches as $match)
<button type="button" wire:click="selezionaCollaboratoreRubrica({{ (int) $match['id'] }})" class="w-full rounded-lg border p-3 text-left {{ (int) $this->collaboratoreRubricaId === (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' }}
@ -38,7 +38,7 @@
</button>
@endforeach
</div>
@elseif(filled($collaboratoreRubricaSearch) && ! $collaboratoreRubricaId)
@elseif(filled($this->collaboratoreRubricaSearch) && ! $this->collaboratoreRubricaId)
<div class="mt-3 text-xs text-gray-500">Nessun nominativo trovato con questa ricerca.</div>
@endif

View File

@ -64,7 +64,7 @@
- <a href="{{ $rubricaUrl }}" class="text-indigo-700 hover:underline">Apri scheda rubrica</a>
@endif
@endif
@if($riga->stabile)
@if($this->shouldShowPostItStabile($riga))
- {{ $riga->stabile->denominazione ?: ('Stabile #' . $riga->stabile->id) }}
@endif
</div>
@ -162,7 +162,7 @@
<span class="rounded-md px-2 py-1 text-[11px] font-semibold {{ $callState['class'] }}">{{ $callState['label'] }}</span>
</div>
@if($item->stabile)
@if($this->shouldShowPostItStabile($item))
<div class="mt-1 text-[11px] text-amber-900">{{ $item->stabile->denominazione ?: ('Stabile #' . $item->stabile->id) }}</div>
@endif
</div>
@ -435,7 +435,7 @@
<span class="rounded-md px-2 py-1 text-[11px] font-semibold {{ $callState['class'] }}">{{ $callState['label'] }}</span>
</div>
@if($item->stabile)
@if($this->shouldShowPostItStabile($item))
<div class="mt-1 text-xs text-slate-700">{{ $item->stabile->denominazione ?: ('Stabile #' . $item->stabile->id) }}</div>
@endif