++ elenco assegnati: righe compatte (classi brevi, copia condivisa)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Francesco Picone
2026-10-19 15:51:57 +02:00
parent 81a8f2ba33
commit a10d06aa6c
4 changed files with 69 additions and 55 deletions

View File

@@ -147,8 +147,10 @@ dell'elenco (`linkSearch`), aprire l'invio del link. Rimedi:
- Il territorio va a Livewire con `wire:model` (non più `.live`): l'anteprima
della miniatura segue la scelta nel browser (evento `scelta` di
`<x-scelta>`, URL della miniatura nelle opzioni).
- Le icone delle righe sono simboli SVG definiti una volta e richiamati con
`<use>`. Il ramo "link scaduto" dell'elenco (solo per non autenticati, cioè
- Righe compatte: icone come simboli SVG definiti una volta e richiamati con
`<use>`, classi brevi `tm-assegnato-*` in `app.css` al posto delle catene
di utility, `<button>` al posto di `<x-btn>` (niente marcatori Livewire),
copia del link con il metodo condiviso `copia()` di `elencoCercabile`. Il ramo "link scaduto" dell'elenco (solo per non autenticati, cioè
mai su questa pagina) è stato tolto.
- `AssegnaPrestazioniTest` tiene fermi questi punti, compreso un tetto al
peso per riga dell'elenco.

View File

@@ -1808,3 +1808,43 @@ html { scroll-behavior: smooth; }
color: var(--tm-testo-tenue);
font-size: 14px;
}
/* ─── Elenco dei territori assegnati (pagina Assegna) ───────────────────────
Classi brevi al posto delle catene di utility: le righe sono ~80 e ogni
byte si moltiplica (ElencoAssegnati, AssegnaPrestazioniTest). */
.tm-assegnato { padding: 16px 20px; }
.tm-assegnato svg { width: 16px; height: 16px; fill: none; stroke: currentColor; flex: none; }
.tm-assegnato-testa {
display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between;
gap: 8px 24px;
}
.tm-assegnato-numero { color: #4f46e5; font-size: 14px; font-weight: 700; }
.tm-assegnato-numero:hover { text-decoration: underline; }
.tm-assegnato-zona {
margin-left: 6px; padding: 2px 6px; border-radius: 4px;
background: #f3f4f6; color: #9ca3af; font-size: 12px;
}
.tm-assegnato-info { color: #6b7280; font-size: 12px; }
.tm-assegnato-info b { color: #1f2937; font-size: 14px; font-weight: 500; }
.tm-assegnato-giorni {
padding: 2px 8px; border-radius: 999px;
background: #f3f4f6; color: #6b7280; font-weight: 600;
}
.tm-assegnato-giorni.tm-g-presto { background: #fffbeb; color: #d97706; }
.tm-assegnato-giorni.tm-g-tardi { background: #fef2f2; color: #dc2626; }
.tm-assegnato-sospeso {
display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
margin-top: 12px; padding: 6px 12px; border: 1px solid #fde68a; border-radius: 6px;
background: #fffbeb; color: #92400e; font-size: 12px; font-weight: 600;
}
.tm-assegnato-link { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 12px; }
.tm-assegnato-link > * { flex: none; }
.tm-assegnato-link code {
flex: 1 1 0; min-width: 0; padding: 8px 12px;
border: 1px solid #e5e7eb; border-radius: 6px; background: #f9fafb;
color: #4b5563; font-size: 12px; word-break: break-all; user-select: all; cursor: text;
}
.tm-assegnato-senza { margin-top: 8px; color: #d1d5db; font-size: 12px; }
/* Pulsante copia: la spunta al posto dell'icona per due secondi. */
.tm-copia svg + svg, .tm-copia.tm-copiato svg:first-child { display: none; }
.tm-copia.tm-copiato svg + svg { display: block; color: #16a34a; }

View File

@@ -21,6 +21,16 @@ document.addEventListener('alpine:init', () => {
if (normalizza(this.q) === '') return false;
return ![...this.$root.querySelectorAll('[data-cerca]')].some((riga) => this.corrisponde(riga.dataset.cerca));
},
// Copia il link della riga (il <code> accanto al pulsante); il pulsante
// mostra la spunta per due secondi (classe tm-copiato). Un metodo solo
// invece dello stesso codice ripetuto in ogni riga.
copia(pulsante) {
const testo = pulsante.closest('[data-link]')?.querySelector('code')?.textContent.trim() ?? '';
navigator.clipboard?.writeText(testo).then(() => {
pulsante.classList.add('tm-copiato');
setTimeout(() => pulsante.classList.remove('tm-copiato'), 2000);
}).catch(() => {});
},
}));
// vuoto: il valore di "nessuna scelta" per la proprietà Livewire collegata

View File

@@ -41,65 +41,27 @@
: null;
$nome = $a->proclamatore?->nome_completo ?? 'N/A';
@endphp
<div wire:key="assegnato-{{ $a->id }}" data-cerca="{{ $cerca[$a->id] }}" x-show="corrisponde($el.dataset.cerca)" class="px-5 py-4">
<div class="flex flex-wrap items-start justify-between gap-x-6 gap-y-2">
<div class="flex items-center gap-2">
<a data-ritorno href="{{ route('territori.show', $a->territorio_id) }}" class="text-indigo-600 hover:underline font-bold text-sm">N° {{ $a->territorio?->numero }}</a>
@if($a->territorio?->zona?->nome)
<span class="text-xs text-gray-400 bg-gray-100 rounded px-1.5 py-0.5">{{ $a->territorio->zona->nome }}</span>
@endif
</div>
<div class="flex items-center gap-3 text-sm flex-wrap">
<span class="font-medium text-gray-800">{{ $nome }}</span>
<span class="text-gray-300">·</span>
<span class="text-gray-500 text-xs">{{ $a->assigned_at->format('d/m/Y') }}</span>
<span class="text-gray-300">·</span>
<span class="text-xs font-semibold px-2 py-0.5 rounded-full {{ $a->giorni > 120 ? 'bg-red-50 text-red-600' : ($a->giorni > 90 ? 'bg-amber-50 text-amber-600' : 'bg-gray-100 text-gray-500') }}">{{ $a->giorni }}g</span>
</div>
{{-- Riga compatta: classi brevi (tm-assegnato-*, in app.css), pulsanti
scritti come <button> e non <x-btn>, copia con il metodo condiviso
copia(): sono ~80 righe, ogni byte si moltiplica. --}}
<div wire:key="assegnato-{{ $a->id }}" data-cerca="{{ $cerca[$a->id] }}" x-show="corrisponde($el.dataset.cerca)" class="tm-assegnato">
<div class="tm-assegnato-testa">
<span><a data-ritorno href="{{ route('territori.show', $a->territorio_id) }}" class="tm-assegnato-numero">N° {{ $a->territorio?->numero }}</a> @if($a->territorio?->zona?->nome)<span class="tm-assegnato-zona">{{ $a->territorio->zona->nome }}</span>@endif</span>
<span class="tm-assegnato-info"><b>{{ $nome }}</b> · {{ $a->assigned_at->format('d/m/Y') }} · <span class="tm-assegnato-giorni {{ $a->giorni > 120 ? 'tm-g-tardi' : ($a->giorni > 90 ? 'tm-g-presto' : '') }}">{{ $a->giorni }}g</span></span>
</div>
{{-- Procedura di invio interrotta, o risposto "non ancora" --}}
@if($a->link_in_sospeso)
<div class="mt-3 flex flex-wrap items-center gap-2 rounded-md px-3 py-1.5 text-xs font-semibold bg-amber-50 border border-amber-200 text-amber-800" role="status">
<svg class="h-3.5 w-3.5" fill="none" stroke="currentColor" aria-hidden="true"><use href="#tm-i-avviso"/></svg>
Link ancora da inviare
{{-- L'invio si apre nel componente della pagina (Assegna::apriInvio). --}}
<x-btn variante="secondary" tono="tenue" dimensione="sm" wire:click="$dispatch('apri-invio', { assegnazioneId: {{ $a->id }} })">Invia link</x-btn>
</div>
<div class="tm-assegnato-sospeso" role="status"><svg aria-hidden="true"><use href="#tm-i-avviso"/></svg> Link ancora da inviare <button type="button" class="tm-btn tm-btn-secondary tm-btn-tenue tm-btn-sm" wire:click="$dispatch('apri-invio', { assegnazioneId: {{ $a->id }} })">Invia link</button></div>
@endif
@if($pdfUrl)
<div x-data="{ copied: false }" class="mt-3 flex flex-wrap items-center gap-2">
<code class="flex-1 min-w-0 text-xs bg-gray-50 border border-gray-200 rounded-md px-3 py-2 text-gray-600 break-all select-all cursor-text" x-on:click="window.getSelection().selectAllChildren($el)">{{ $pdfUrl }}</code>
<x-btn variante="secondary" tono="tenue" dimensione="sm" icona class="flex-none"
x-on:click="navigator.clipboard.writeText($el.closest('div').querySelector('code').textContent).then(() => { copied = true; setTimeout(() => copied = false, 2000) })"
title="Copia link" aria-label="Copia il link del PDF">
<svg x-show="!copied" fill="none" stroke="currentColor" aria-hidden="true"><use href="#tm-i-copia"/></svg>
<svg x-show="copied" x-cloak class="text-green-600" fill="none" stroke="currentColor" aria-hidden="true"><use href="#tm-i-spunta"/></svg>
</x-btn>
@if($whatsapp)
<a href="{{ $whatsapp }}" target="_blank" rel="noopener" class="tm-whatsapp tm-whatsapp-compatto flex-none"
title="Scrivi su WhatsApp a {{ $nome }}" aria-label="Scrivi su WhatsApp a {{ $nome }}">
<svg fill="none" stroke="currentColor" aria-hidden="true"><use href="#tm-i-whatsapp"/></svg>
</a>
@endif
{{-- Interruttore di stato, non un comando: lo stato "inviato"
si legge dalla variante (primary quando è acceso) e
aria-pressed lo dice anche a chi non lo vede. --}}
<x-btn :variante="$a->link_sent ? 'primary' : 'secondary'" tono="tenue" dimensione="sm" class="flex-none"
wire:click="toggleLinkSent({{ $a->id }})"
aria-pressed="{{ $a->link_sent ? 'true' : 'false' }}"
title="{{ $a->link_sent ? 'Link inviato — clicca per annullare' : 'Segna come inviato' }}">
<svg fill="none" stroke="currentColor" aria-hidden="true"><use href="{{ $a->link_sent ? '#tm-i-spunta' : '#tm-i-invio' }}"/></svg>
{{ $a->link_sent ? 'Link inviato' : 'Non inviato' }}
</x-btn>
<span x-show="copied" x-cloak class="flex-none text-xs text-green-600 font-medium">Copiato!</span>
<div class="tm-assegnato-link" data-link>
<code x-on:click="window.getSelection().selectAllChildren($el)">{{ $pdfUrl }}</code>
<button type="button" class="tm-btn tm-btn-secondary tm-btn-tenue tm-btn-sm tm-btn-icona tm-copia" x-on:click="copia($el)" title="Copia link" aria-label="Copia il link del PDF"><svg aria-hidden="true"><use href="#tm-i-copia"/></svg><svg aria-hidden="true"><use href="#tm-i-spunta"/></svg></button>
@if($whatsapp)<a href="{{ $whatsapp }}" target="_blank" rel="noopener" class="tm-whatsapp tm-whatsapp-compatto" title="Scrivi su WhatsApp a {{ $nome }}" aria-label="Scrivi su WhatsApp a {{ $nome }}"><svg aria-hidden="true"><use href="#tm-i-whatsapp"/></svg></a>@endif
{{-- Interruttore di stato: la variante (primary se acceso) e aria-pressed dicono se il link è inviato. --}}
<button type="button" class="tm-btn {{ $a->link_sent ? 'tm-btn-primary' : 'tm-btn-secondary' }} tm-btn-tenue tm-btn-sm" wire:click="toggleLinkSent({{ $a->id }})" aria-pressed="{{ $a->link_sent ? 'true' : 'false' }}" title="{{ $a->link_sent ? 'Link inviato — clicca per annullare' : 'Segna come inviato' }}"><svg aria-hidden="true"><use href="{{ $a->link_sent ? '#tm-i-spunta' : '#tm-i-invio' }}"/></svg> {{ $a->link_sent ? 'Link inviato' : 'Non inviato' }}</button>
</div>
@else
<p class="mt-2 text-xs text-gray-300">Nessun PDF disponibile</p>
<p class="tm-assegnato-senza">Nessun PDF disponibile</p>
@endif
</div>
@endforeach