++ elenco assegnati: righe compatte (classi brevi, copia condivisa)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
{{-- 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>
|
||||
<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>
|
||||
</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
|
||||
|
||||
Reference in New Issue
Block a user