fix: refine archive label printing and public qr

This commit is contained in:
michele 2026-04-22 21:54:56 +00:00
parent 8bc7783ce3
commit 49a49cdb08
10 changed files with 475 additions and 247 deletions

View File

@ -40,7 +40,8 @@ ## Struttura documentazione
## Stato operativo recente ## Stato operativo recente
- Strumenti > Documenti apre ora una dashboard `Protocollo comunicazioni` con ricerca, filtri e anteprima PDF. - Strumenti > Documenti apre ora una dashboard `Protocollo comunicazioni` con ricerca, filtri e anteprima PDF.
- Le etichette archivio fisico generano il QR in locale e supportano i formati DYMO `11354` e `99014`. - Le etichette archivio fisico generano il QR in locale, supportano i formati DYMO `11354` e `99014` e usano un URL pubblico configurabile tramite `APP_PUBLIC_URL`.
- La vista `Archivio digitale` usa ora una tabella a tutta larghezza con riepilogo rapido della cartella corrente.
- La topbar contestuale Filament usa layout a capo, senza scrollbar orizzontale. - La topbar contestuale Filament usa layout a capo, senza scrollbar orizzontale.
## Contribuire ## Contribuire

View File

@ -49,7 +49,7 @@ class DocumentiArchivio extends Page implements HasTable
public string $protocolloView = 'cards'; public string $protocolloView = 'cards';
#[Url(as: 'folder')] #[Url( as : 'folder')]
public ?int $currentFolderId = null; public ?int $currentFolderId = null;
public array $physicalArchiveForm = []; public array $physicalArchiveForm = [];
@ -292,7 +292,7 @@ public function table(Table $table): Table
->hiddenLabel() ->hiddenLabel()
->icon('heroicon-o-archive-box') ->icon('heroicon-o-archive-box')
->tooltip('Collega archivio fisico') ->tooltip('Collega archivio fisico')
->fillForm(fn(Documento $record): array => [ ->fillForm(fn(Documento $record): array=> [
'supporto_fisico' => data_get($record->metadati_archivio ?? [], 'supporto_fisico'), 'supporto_fisico' => data_get($record->metadati_archivio ?? [], 'supporto_fisico'),
'modello_etichetta' => data_get($record->metadati_archivio ?? [], 'modello_etichetta', '11354'), 'modello_etichetta' => data_get($record->metadati_archivio ?? [], 'modello_etichetta', '11354'),
'busta_numero' => data_get($record->metadati_archivio ?? [], 'busta_numero'), 'busta_numero' => data_get($record->metadati_archivio ?? [], 'busta_numero'),
@ -434,6 +434,30 @@ public function getDigitalArchiveCurrentFolderProperty(): ?DocumentoArchivioCart
return DocumentoArchivioCartella::query()->find($this->currentFolderId); return DocumentoArchivioCartella::query()->find($this->currentFolderId);
} }
public function getDigitalArchiveCurrentFolderDocumentCountProperty(): int
{
$stabileId = (int) ($this->resolveActiveStabile()?->id ?? 0);
if ($stabileId <= 0 || ! $this->currentFolderId) {
return 0;
}
return (int) Documento::query()
->where('stabile_id', $stabileId)
->where('tipo_documento', 'fattura')
->where('cartella_archivio_id', $this->currentFolderId)
->count();
}
public function getDigitalArchiveCurrentFolderHintProperty(): string
{
$folder = $this->digitalArchiveCurrentFolder;
if (! $folder instanceof DocumentoArchivioCartella) {
return 'Seleziona la cartella annuale per vedere lelenco dei PDF già ordinati per protocollo e data.';
}
return trim((string) ($folder->percorso_logico ?: $folder->nome));
}
public function getDigitalArchiveFoldersProperty(): array public function getDigitalArchiveFoldersProperty(): array
{ {
$stabile = $this->resolveActiveStabile(); $stabile = $this->resolveActiveStabile();
@ -464,7 +488,7 @@ public function getDigitalArchiveFoldersProperty(): array
->orderBy('sort_order') ->orderBy('sort_order')
->orderBy('nome') ->orderBy('nome')
->get() ->get()
->map(fn(DocumentoArchivioCartella $folder): array => [ ->map(fn(DocumentoArchivioCartella $folder): array=> [
'id' => (int) $folder->id, 'id' => (int) $folder->id,
'nome' => (string) $folder->nome, 'nome' => (string) $folder->nome,
'descrizione' => (string) ($folder->percorso_logico ?: $folder->nome), 'descrizione' => (string) ($folder->percorso_logico ?: $folder->nome),
@ -620,7 +644,7 @@ public function getPhysicalArchiveParentOptionsProperty(): array
->whereIn('tipo_item', ['busta_trasparente', 'cartella_tre_lembi', 'faldone_anelli', 'faldone_lacci', 'scatola']) ->whereIn('tipo_item', ['busta_trasparente', 'cartella_tre_lembi', 'faldone_anelli', 'faldone_lacci', 'scatola'])
->orderBy('codice_univoco') ->orderBy('codice_univoco')
->get(['id', 'codice_univoco', 'titolo']) ->get(['id', 'codice_univoco', 'titolo'])
->mapWithKeys(fn(DocumentoArchivioFisicoItem $item): array => [ ->mapWithKeys(fn(DocumentoArchivioFisicoItem $item): array=> [
(string) $item->id => trim($item->codice_univoco . ' · ' . $item->titolo), (string) $item->id => trim($item->codice_univoco . ' · ' . $item->titolo),
]) ])
->all(); ->all();
@ -690,7 +714,7 @@ public function getPhysicalArchiveRowsProperty(): array
'data_archiviazione' => $item->data_archiviazione?->format('d-m-Y') ?: '—', 'data_archiviazione' => $item->data_archiviazione?->format('d-m-Y') ?: '—',
'parent' => $item->parent ? trim($item->parent->codice_univoco . ' · ' . $item->parent->titolo) : 'Radice', 'parent' => $item->parent ? trim($item->parent->codice_univoco . ' · ' . $item->parent->titolo) : 'Radice',
'percorso' => (string) $item->percorso_fisico, 'percorso' => (string) $item->percorso_fisico,
'documento' => $item->documento ? trim((string) ($item->documento->nome_file ?: $item->documento->nome ?: ('Documento #' . $item->documento->id))) : null, 'documento' => $item->documento ? trim((string) ($item->documento->nome_file ?: $item->documento->nome ?: ('Documento #' . $item->documento->id))): null,
'ubicazione' => collect([$item->magazzino, $item->scaffale, $item->ripiano, $item->ubicazione_dettaglio])->filter()->implode(' · '), 'ubicazione' => collect([$item->magazzino, $item->scaffale, $item->ripiano, $item->ubicazione_dettaglio])->filter()->implode(' · '),
'modello_etichetta' => (string) ($item->modello_etichetta ?: '11354'), 'modello_etichetta' => (string) ($item->modello_etichetta ?: '11354'),
]; ];
@ -935,7 +959,7 @@ public function getProtocolloComunicazioniCategoryOptionsProperty(): array
$labels = $this->getCategorieOptions(); $labels = $this->getCategorieOptions();
return collect($visibleCategories) return collect($visibleCategories)
->mapWithKeys(fn(string $value): array => [$value => $labels[$value] ?? Str::headline($value)]) ->mapWithKeys(fn(string $value): array=> [$value => $labels[$value] ?? Str::headline($value)])
->all(); ->all();
} }

View File

@ -166,7 +166,17 @@ public function etichettaArchivioFisico(Request $request, DocumentoArchivioFisic
$format = $request->query('format', '11354'); $format = $request->query('format', '11354');
$format = in_array($format, ['11354', '99014'], true) ? $format : '11354'; $format = in_array($format, ['11354', '99014'], true) ? $format : '11354';
$defaults = $this->getDymoDefaultsForUser($format); $labelPrinterMode = $request->boolean('autoprint')
|| $request->has('dymo_fix')
|| $request->has('dymo_rot')
|| $request->has('dymo_dx')
|| $request->has('dymo_dy');
$defaults = $labelPrinterMode ? $this->getDymoDefaultsForUser($format) : [
'fix' => false,
'rot' => 0,
'dx' => 0.0,
'dy' => 0.0,
];
$dymoFix = $request->query('dymo_fix'); $dymoFix = $request->query('dymo_fix');
if ($dymoFix === null) { if ($dymoFix === null) {
@ -181,20 +191,34 @@ public function etichettaArchivioFisico(Request $request, DocumentoArchivioFisic
$dymoDx = $request->has('dymo_dx') ? (float) $request->query('dymo_dx') : (float) ($defaults['dx'] ?? 0); $dymoDx = $request->has('dymo_dx') ? (float) $request->query('dymo_dx') : (float) ($defaults['dx'] ?? 0);
$dymoDy = $request->has('dymo_dy') ? (float) $request->query('dymo_dy') : (float) ($defaults['dy'] ?? 0); $dymoDy = $request->has('dymo_dy') ? (float) $request->query('dymo_dy') : (float) ($defaults['dy'] ?? 0);
if ($format === '11354' && $dymoFix) {
if (! in_array($dymoRot, [-90, 90], true)) {
$dymoRot = -90;
}
} else {
if (! in_array($dymoRot, [-180, -90, 0, 90, 180], true)) {
$dymoRot = 0;
}
}
$dymoDx = max(-15.0, min(15.0, $dymoDx));
$dymoDy = max(-15.0, min(15.0, $dymoDy));
return view('filament.print.etichetta-archivio-fisico', [ return view('filament.print.etichetta-archivio-fisico', [
'item' => $item->load(['parent', 'children', 'stabile', 'documento']), 'item' => $item->load(['parent', 'children', 'stabile', 'documento']),
'format' => $format, 'format' => $format,
'labelPrinterMode' => $labelPrinterMode,
'dymoFix' => $dymoFix, 'dymoFix' => $dymoFix,
'dymoRot' => $dymoRot, 'dymoRot' => $dymoRot,
'dymoDx' => $dymoDx, 'dymoDx' => $dymoDx,
'dymoDy' => $dymoDy, 'dymoDy' => $dymoDy,
'publicUrl' => $item->qr_code_data, 'publicUrl' => $item->buildPublicQrUrl(),
]); ]);
} }
public function showPublicArchivioFisicoItem(Request $request, DocumentoArchivioFisicoItem $item) public function showPublicArchivioFisicoItem(Request $request, DocumentoArchivioFisicoItem $item)
{ {
if (! $request->hasValidSignature()) { if (! $request->hasValidSignature(false)) {
abort(403); abort(403);
} }

View File

@ -1,5 +1,4 @@
<?php <?php
namespace App\Models; namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Factories\HasFactory;
@ -107,7 +106,7 @@ public function getPercorsoFisicoAttribute(): string
public function getQrCodeImageAttribute(): string public function getQrCodeImageAttribute(): string
{ {
$data = trim((string) $this->qr_code_data); $data = $this->buildPublicQrUrl();
if ($data !== '' && class_exists(\chillerlan\QRCode\QROptions::class) && class_exists(\chillerlan\QRCode\QRCode::class)) { if ($data !== '' && class_exists(\chillerlan\QRCode\QROptions::class) && class_exists(\chillerlan\QRCode\QRCode::class)) {
$options = new \chillerlan\QRCode\QROptions([ $options = new \chillerlan\QRCode\QROptions([
@ -156,15 +155,26 @@ public static function generateCodice(int $stabileId): string
public function refreshQrCodeData(): void public function refreshQrCodeData(): void
{ {
$url = URL::temporarySignedRoute( $this->forceFill([
'qr_code_data' => $this->buildPublicQrUrl(),
])->saveQuietly();
}
public function buildPublicQrUrl(): string
{
$relativeSignedPath = URL::temporarySignedRoute(
'public.archivio-fisico.show', 'public.archivio-fisico.show',
now()->addYears(5), now()->addYears(5),
['item' => $this->id] ['item' => $this->id],
false,
); );
$this->forceFill([ $baseUrl = rtrim((string) config('app.public_url', config('app.url')), '/');
'qr_code_data' => $url, if ($baseUrl === '') {
])->saveQuietly(); return $relativeSignedPath;
}
return $baseUrl . '/' . ltrim($relativeSignedPath, '/');
} }
protected static function booted(): void protected static function booted(): void

View File

@ -102,8 +102,8 @@ public function panel(Panel $panel): Panel
return $user instanceof User && $user->hasRole('fornitore'); return $user instanceof User && $user->hasRole('fornitore');
}), }),
]) ])
->discoverResources(in: app_path('Filament/Resources'), for : 'App\\Filament\\Resources') ->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources')
->discoverPages(in: app_path('Filament/Pages'), for : 'App\\Filament\\Pages') ->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages')
->pages([ ->pages([
Dashboard::class, Dashboard::class,
SupportoDashboard::class, SupportoDashboard::class,
@ -111,7 +111,7 @@ public function panel(Panel $panel): Panel
PrimaNotaModifica::class, PrimaNotaModifica::class,
ContoMastrino::class, ContoMastrino::class,
]) ])
->discoverWidgets(in: app_path('Filament/Widgets'), for : 'App\\Filament\\Widgets') ->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets')
->widgets([ ->widgets([
AccountWidget::class, AccountWidget::class,
TicketMobileOverview::class, TicketMobileOverview::class,

View File

@ -37,6 +37,8 @@
'url' => env('APP_URL', 'http://localhost'), 'url' => env('APP_URL', 'http://localhost'),
'public_url' => env('APP_PUBLIC_URL', env('APP_URL', 'http://localhost')),
'asset_url' => env('ASSET_URL'), 'asset_url' => env('ASSET_URL'),
/* /*

View File

@ -53,6 +53,12 @@ ## Archivio fisico ed etichette
- stampa etichette nei formati `DYMO 11354` e `DYMO 99014`; - stampa etichette nei formati `DYMO 11354` e `DYMO 99014`;
- pagina pubblica firmata per dettaglio contenitore o documento tramite QR. - pagina pubblica firmata per dettaglio contenitore o documento tramite QR.
Taratura operativa corrente:
- `11354`: layout orizzontale principale con correzione verticale leggera verso l'alto;
- `11354 verticale`: variante dedicata ai driver che ruotano l'etichetta, ora con rotazione corretta lato stampa;
- `99014`: QR spostato in basso a destra per liberare spazio scrivibile nella parte alta.
Per i documenti digitali resta disponibile anche l'assegnazione dei metadati fisici principali: Per i documenti digitali resta disponibile anche l'assegnazione dei metadati fisici principali:
- supporto fisico; - supporto fisico;
@ -75,6 +81,26 @@ ## QR locale sulle etichette
Questo elimina la dipendenza da URL esterni per il QR e rende piu stabile il preview di stampa. Questo elimina la dipendenza da URL esterni per il QR e rende piu stabile il preview di stampa.
## QR pubblico e dominio esterno
Il QR dell'archivio fisico non deve piu dipendere dall'IP interno del server.
Implementazione attuale:
- la firma della route pubblica viene generata in forma relativa;
- il link finale viene costruito usando `APP_PUBLIC_URL`, con fallback su `APP_URL`;
- la validazione avviene lato controller con firma relativa, cosi il QR resta valido anche dietro dominio pubblico o reverse proxy.
Configurazione richiesta in ambiente:
1. impostare `APP_PUBLIC_URL` con il dominio realmente raggiungibile dall'esterno;
2. lasciare `APP_URL` per il contesto applicativo interno se serve;
3. rigenerare o aggiornare i QR gia salvati solo se si vuole riallineare anche il dato persistito nel database.
Esempio:
- `APP_PUBLIC_URL=https://netgescon.example.it`
## Stampa PDF e watermark archivio ## Stampa PDF e watermark archivio
Per i documenti PDF sono ora previste due aperture distinte dalla tab archivio: Per i documenti PDF sono ora previste due aperture distinte dalla tab archivio:
@ -208,6 +234,24 @@ ## Scanner Epson da pagina web
2. upload/ingestione rapida in NetGescon; 2. upload/ingestione rapida in NetGescon;
3. OCR e archiviazione automatica. 3. OCR e archiviazione automatica.
Per il modello Epson WF-C5710 la direzione pratica da testare non e il browser puro, ma una di queste due:
- scansione verso cartella di rete osservata da NetGescon;
- piccolo bridge locale o di rete che espone una chiamata HTTP interna e usa il canale scanner disponibile sul dispositivo.
Questo evita il vincolo dei browser su TWAIN/WIA e mantiene il flusso pilotabile dalla pagina archivio.
## Archivio digitale
La vista `Strumenti > Documenti > Archivio digitale` e stata alleggerita per lavorare meglio dentro la cartella corrente.
Stato attuale:
- breadcrumb e cartelle restano in alto;
- la tabella documenti ora usa tutta la larghezza utile;
- le informazioni laterali sono state compattate in tre card sintetiche sopra la tabella;
- la pagina mostra nome cartella attiva, numero documenti del livello e stato operativo digitale/fisico.
## Dymo LAN 11354 ## Dymo LAN 11354
Domani si puo provare una stampa etichetta su rete con formato `11354` usando: Domani si puo provare una stampa etichetta su rete con formato `11354` usando:

View File

@ -283,25 +283,27 @@ class="rounded-2xl border border-slate-200 bg-white p-4 text-left shadow-sm tran
@endforelse @endforelse
</div> </div>
<div class="grid gap-3 xl:grid-cols-[minmax(0,1fr)_320px]"> <div class="grid gap-3 lg:grid-cols-3">
<div class="rounded-xl border bg-white p-4">
<div class="text-xs uppercase tracking-wide text-slate-500">Cartella attiva</div>
<div class="mt-2 text-sm font-semibold text-slate-900">{{ $this->digitalArchiveCurrentFolder?->nome ?? 'Nessuna cartella selezionata' }}</div>
<div class="mt-1 text-xs text-slate-500">{{ $this->digitalArchiveCurrentFolderHint }}</div>
</div>
<div class="rounded-xl border bg-white p-4">
<div class="text-xs uppercase tracking-wide text-slate-500">Documenti nel livello</div>
<div class="mt-2 text-2xl font-semibold text-slate-900">{{ $this->digitalArchiveCurrentFolderDocumentCount }}</div>
<div class="mt-1 text-xs text-slate-500">Solo fatture PDF collegate alla cartella corrente.</div>
</div>
<div class="rounded-xl border bg-white p-4">
<div class="text-xs uppercase tracking-wide text-slate-500">Operatività</div>
<div class="mt-2 text-sm font-semibold text-slate-900">Archivio digitale e fisico allineati</div>
<div class="mt-1 text-xs text-slate-500">Da qui puoi aprire, stampare, watermarkare e assegnare subito la collocazione cartacea.</div>
</div>
</div>
<div> <div>
{{ $this->table }} {{ $this->table }}
</div> </div>
<div class="space-y-3">
<div class="rounded-xl border bg-white p-4">
<div class="text-sm font-semibold text-slate-900">Flusso digitale e fisico</div>
<div class="mt-2 text-sm text-slate-600">Usa l'icona archivio per assegnare contenitore, busta, fascicolo ed etichetta del documento cartaceo. La posizione verra poi richiamata anche nel watermark PDF.</div>
</div>
<div class="rounded-xl border bg-white p-4">
<div class="text-sm font-semibold text-slate-900">Stampa</div>
<div class="mt-2 text-sm text-slate-600">Icona stampante: PDF senza timbro. Icona duplicato: PDF con watermark archivio digitale e fisico, se la codifica e presente.</div>
</div>
<div class="rounded-xl border bg-white p-4">
<div class="text-sm font-semibold text-slate-900">Scanner</div>
<div class="mt-2 text-sm text-slate-600">La struttura attuale e pronta per il prossimo aggancio scanner: acquisizione PDF, naming ordinato, assegnazione immediata a cartella digitale e posizione fisica.</div>
</div>
</div>
</div>
</div> </div>
</x-filament::section> </x-filament::section>
@elseif($this->hubTab === 'template-drive') @elseif($this->hubTab === 'template-drive')
@ -509,8 +511,9 @@ class="rounded-2xl border border-slate-200 bg-white p-4 text-left shadow-sm tran
@endif @endif
</div> </div>
<div class="flex shrink-0 flex-wrap items-center gap-2"> <div class="flex shrink-0 flex-wrap items-center gap-2">
<a href="{{ route('filament.archivio-fisico.etichetta', ['item' => $row['id']]) }}?format=11354&autoprint=1" target="_blank" class="inline-flex items-center rounded-lg border border-slate-300 bg-white px-3 py-2 text-xs font-medium text-slate-700 shadow-sm hover:bg-slate-50">11354</a> <a href="{{ route('filament.archivio-fisico.etichetta', ['item' => $row['id']]) }}?format=11354&autoprint=1&dymo_fix=0&dymo_rot=0&dymo_dx=0&dymo_dy=-1" target="_blank" class="inline-flex items-center rounded-lg border border-slate-300 bg-white px-3 py-2 text-xs font-medium text-slate-700 shadow-sm hover:bg-slate-50">11354</a>
<a href="{{ route('filament.archivio-fisico.etichetta', ['item' => $row['id']]) }}?format=99014&autoprint=1" target="_blank" class="inline-flex items-center rounded-lg border border-slate-300 bg-white px-3 py-2 text-xs font-medium text-slate-700 shadow-sm hover:bg-slate-50">99014</a> <a href="{{ route('filament.archivio-fisico.etichetta', ['item' => $row['id']]) }}?format=11354&autoprint=1&dymo_fix=1&dymo_rot=90&dymo_dx=0&dymo_dy=0" target="_blank" class="inline-flex items-center rounded-lg border border-slate-300 bg-white px-3 py-2 text-xs font-medium text-slate-700 shadow-sm hover:bg-slate-50">11354 verticale</a>
<a href="{{ route('filament.archivio-fisico.etichetta', ['item' => $row['id']]) }}?format=99014&autoprint=1&dymo_fix=0&dymo_rot=0&dymo_dx=3.5&dymo_dy=3.5" target="_blank" class="inline-flex items-center rounded-lg border border-slate-300 bg-white px-3 py-2 text-xs font-medium text-slate-700 shadow-sm hover:bg-slate-50">99014</a>
</div> </div>
</div> </div>
</div> </div>

View File

@ -10,17 +10,60 @@
@php($dymoDx = isset($dymoDx) ? (float) $dymoDx : 0.0) @php($dymoDx = isset($dymoDx) ? (float) $dymoDx : 0.0)
@php($dymoDy = isset($dymoDy) ? (float) $dymoDy : 0.0) @php($dymoDy = isset($dymoDy) ? (float) $dymoDy : 0.0)
@php($autoPrint = request()->has('autoprint') && filter_var(request()->query('autoprint'), FILTER_VALIDATE_BOOL)) @php($autoPrint = request()->has('autoprint') && filter_var(request()->query('autoprint'), FILTER_VALIDATE_BOOL))
@php($labelPrinterMode = isset($labelPrinterMode) ? (bool) $labelPrinterMode : false)
@php($pageWidth = $fmt === '99014' ? '54mm' : '57mm')
@php($pageHeight = $fmt === '99014' ? '101mm' : '32mm')
<style> <style>
@page { margin: 0; size: {{ $fmt === '99014' ? '54mm 101mm' : '57mm 32mm' }}; } @page {
margin: {{ $labelPrinterMode ? '0' : '10mm' }};
size: {{ $labelPrinterMode ? ($fmt === '99014' ? '54mm 101mm' : '57mm 32mm') : 'A4 portrait' }};
}
html, body { html, body {
margin: 0; margin: 0;
padding: 0; padding: 0;
width: {{ $fmt === '99014' ? '54mm' : '57mm' }}; width: 100%;
height: {{ $fmt === '99014' ? '101mm' : '32mm' }}; min-height: 100%;
font-family: Arial, Helvetica, sans-serif; font-family: Arial, Helvetica, sans-serif;
overflow: hidden; overflow: visible;
background: {{ $labelPrinterMode ? 'white' : '#eef1f5' }};
}
.sheet {
box-sizing: border-box;
width: {{ $labelPrinterMode ? $pageWidth : '210mm' }};
min-height: {{ $labelPrinterMode ? $pageHeight : '297mm' }};
padding: {{ $labelPrinterMode ? '0' : '12mm' }};
display: flex;
align-items: {{ $labelPrinterMode ? 'stretch' : 'flex-start' }};
justify-content: {{ $labelPrinterMode ? 'stretch' : 'flex-start' }};
margin: 0 auto;
background: white;
overflow: hidden;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
break-inside: avoid;
break-after: avoid-page;
page-break-inside: avoid;
page-break-after: avoid;
}
.label {
box-sizing: border-box;
width: {{ $pageWidth }};
height: {{ $pageHeight }};
padding: 0;
flex: 0 0 auto;
position: relative;
}
.calib {
position: absolute;
inset: 0;
transform-origin: 0 0;
}
.label-content {
box-sizing: border-box;
width: 100%;
height: 100%;
padding: {{ $fmt === '99014' ? '3.5mm' : '2mm' }};
} }
.label { box-sizing: border-box; width: 100%; height: 100%; padding: {{ $fmt === '99014' ? '3.5mm' : '2mm' }}; }
.panel { .panel {
width: 100%; width: 100%;
height: 100%; height: 100%;
@ -32,7 +75,16 @@
padding: {{ $fmt === '99014' ? '2.4mm' : '1.5mm' }}; padding: {{ $fmt === '99014' ? '2.4mm' : '1.5mm' }};
align-items: stretch; align-items: stretch;
} }
.panel.panel-99014 {
display: block;
position: relative;
padding: 2.8mm;
}
.content { min-width: 0; } .content { min-width: 0; }
.panel.panel-99014 .content {
padding-right: 0;
padding-bottom: 21mm;
}
.code { font-weight: 800; font-size: {{ $fmt === '99014' ? '14px' : '10px' }}; letter-spacing: 0.4px; } .code { font-weight: 800; font-size: {{ $fmt === '99014' ? '14px' : '10px' }}; letter-spacing: 0.4px; }
.title { font-weight: 700; font-size: {{ $fmt === '99014' ? '13px' : '9px' }}; line-height: 1.15; margin-top: 1mm; } .title { font-weight: 700; font-size: {{ $fmt === '99014' ? '13px' : '9px' }}; line-height: 1.15; margin-top: 1mm; }
.meta { font-size: {{ $fmt === '99014' ? '10px' : '7px' }}; line-height: 1.2; color: #222; margin-top: 1.1mm; } .meta { font-size: {{ $fmt === '99014' ? '10px' : '7px' }}; line-height: 1.2; color: #222; margin-top: 1.1mm; }
@ -46,6 +98,14 @@
padding: 0.4mm; padding: 0.4mm;
box-sizing: border-box; box-sizing: border-box;
} }
.panel.panel-99014 .qr {
position: absolute;
right: 2.8mm;
bottom: 2.8mm;
width: 18mm;
height: 18mm;
padding: 0;
}
.qr svg, .qr svg,
.qr img { .qr img {
display: block; display: block;
@ -58,13 +118,61 @@
aspect-ratio: 1 / 1; aspect-ratio: 1 / 1;
} }
.path { font-size: {{ $fmt === '99014' ? '9px' : '6.6px' }}; line-height: 1.2; color: #444; margin-top: 1mm; word-break: break-word; } .path { font-size: {{ $fmt === '99014' ? '9px' : '6.6px' }}; line-height: 1.2; color: #444; margin-top: 1mm; word-break: break-word; }
@media screen {
body {
padding: 12mm 0;
}
.sheet {
box-shadow: 0 10px 28px rgba(15, 23, 42, 0.18);
}
}
@media print { @media print {
html, body { width: 100%; height: 100%; } html, body {
.label { transform: translate({{ $dymoDx }}mm, {{ $dymoDy }}mm) rotate({{ $dymoRot }}deg); transform-origin: top left; } width: 100%;
min-height: 100%;
background: white;
}
.sheet {
box-shadow: none;
margin: 0;
padding: {{ $labelPrinterMode ? '0' : '12mm' }};
min-height: auto;
}
@if($labelPrinterMode)
html, body {
overflow: hidden;
}
.sheet {
position: fixed;
left: 0;
top: 0;
}
.calib {
transform: translate({{ $dymoDx }}mm, {{ $dymoDy }}mm) rotate({{ $dymoRot }}deg);
}
@if($fmt === '11354' && $dymoFix) @if($fmt === '11354' && $dymoFix)
@page { size: 32mm 57mm; } @page { size: 32mm 57mm; }
html, body { width: 32mm; height: 57mm; } html, body {
.label { width: 57mm; height: 32mm; transform: translate({{ $dymoDx }}mm, calc(57mm + {{ $dymoDy }}mm)) rotate(-90deg); transform-origin: top left; } width: 32mm;
height: 57mm;
}
.sheet {
width: 32mm;
height: 57mm;
padding: 0;
}
.calib {
width: 57mm;
height: 32mm;
@if($dymoRot === 90)
transform: translate({{ $dymoDx }}mm, {{ $dymoDy }}mm) translateX(32mm) rotate(90deg);
@else
transform: translate({{ $dymoDx }}mm, {{ $dymoDy }}mm) translateY(57mm) rotate(-90deg);
@endif
}
@endif
@else
.calib { transform: none !important; }
@endif @endif
} }
</style> </style>
@ -76,16 +184,25 @@
@endif @endif
</head> </head>
<body> <body>
<div class="sheet">
<div class="label"> <div class="label">
<div class="panel"> <div class="calib">
<div class="label-content">
<div @class(['panel', 'panel-99014' => $fmt === '99014'])>
<div class="content"> <div class="content">
<div class="code">{{ $item->codice_univoco }}</div> <div class="code">{{ $item->codice_univoco }}</div>
<div class="title">{{ $item->titolo }}</div> <div class="title">{{ $item->titolo }}</div>
<div class="meta"> <div class="meta">
@if($item->stabile)
<div><strong>Stabile:</strong> {{ $item->stabile->denominazione }}</div>
@endif
<div><strong>Tipo:</strong> {{ $item->tipo_label }}</div> <div><strong>Tipo:</strong> {{ $item->tipo_label }}</div>
@if($item->voce_numero || $item->voce_titolo) @if($item->voce_numero || $item->voce_titolo)
<div><strong>Voce:</strong> {{ $item->voce_numero ? 'Voce ' . $item->voce_numero : '' }} {{ $item->voce_titolo }}</div> <div><strong>Voce:</strong> {{ $item->voce_numero ? 'Voce ' . $item->voce_numero : '' }} {{ $item->voce_titolo }}</div>
@endif @endif
@if($item->data_archiviazione)
<div><strong>Data:</strong> {{ $item->data_archiviazione->format('d-m-Y') }}</div>
@endif
@if($item->parent) @if($item->parent)
<div><strong>Dentro:</strong> {{ $item->parent->codice_univoco }}</div> <div><strong>Dentro:</strong> {{ $item->parent->codice_univoco }}</div>
@endif @endif
@ -100,5 +217,8 @@
</div> </div>
</div> </div>
</div> </div>
</div>
</div>
</div>
</body> </body>
</html> </html>

View File

@ -727,7 +727,7 @@
// --- AUTHENTICATION ROUTES --- // --- AUTHENTICATION ROUTES ---
require __DIR__ . '/auth.php'; require __DIR__ . '/auth.php';
Route::middleware('signed')->get('/archivio-fisico/{item}', [\App\Http\Controllers\Filament\DocumentiPrintController::class, 'showPublicArchivioFisicoItem']) Route::get('/archivio-fisico/{item}', [\App\Http\Controllers\Filament\DocumentiPrintController::class, 'showPublicArchivioFisicoItem'])
->name('public.archivio-fisico.show'); ->name('public.archivio-fisico.show');
Route::middleware(['auth'])->group(function () { Route::middleware(['auth'])->group(function () {