914 lines
77 KiB
PHP
914 lines
77 KiB
PHP
<x-filament-panels::page>
|
|
@php
|
|
$tabs = [
|
|
'protocollo-comunicazioni' => 'Protocollo comunicazioni',
|
|
'archivio-digitale' => 'Archivio digitale',
|
|
'scansione-documenti' => 'Scansione documenti',
|
|
'etichette-generiche' => 'Etichette generiche',
|
|
'movimentazione-codici' => 'Movimentazione per codice',
|
|
'template-drive' => 'Template Drive',
|
|
'archivio-fisico' => 'Archivio fisico',
|
|
'permessi' => 'Permessi e cartelle',
|
|
];
|
|
$stats = $this->hubStats;
|
|
$protocolloStats = $this->protocolloComunicazioniStats;
|
|
@endphp
|
|
|
|
<div class="space-y-6">
|
|
<div class="rounded-2xl border border-slate-200 bg-gradient-to-r from-amber-50 via-white to-sky-50 p-5">
|
|
<div class="flex flex-col gap-4 lg:flex-row lg:items-start lg:justify-between">
|
|
<div>
|
|
<div class="text-sm font-semibold uppercase tracking-[0.18em] text-slate-500">Hub Documenti</div>
|
|
<h2 class="mt-2 text-2xl font-semibold text-slate-900">Archivio digitale e fisico nello stesso flusso</h2>
|
|
<p class="mt-2 max-w-3xl text-sm text-slate-600">Questa pagina diventa la base unica per PDF, struttura Drive, contenitori fisici e regole di visibilita. Il modello resta coerente con il passaggio consegne dello stabile e con la futura gestione studio.</p>
|
|
</div>
|
|
<div class="rounded-xl border border-white/70 bg-white/80 px-4 py-3 text-right shadow-sm">
|
|
<div class="text-xs uppercase tracking-wide text-slate-500">Codice mnemonico suggerito</div>
|
|
<div class="mt-1 text-xl font-semibold text-slate-900">{{ $this->mnemonicCodeHint ?? 'STB00000' }}</div>
|
|
<div class="mt-1 text-xs text-slate-500">Formato guida per etichette, QR e archivio fisico.</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-5 grid gap-3 md:grid-cols-2 xl:grid-cols-4">
|
|
<div class="rounded-xl border bg-white p-4">
|
|
<div class="text-xs uppercase tracking-wide text-slate-500">Documenti</div>
|
|
<div class="mt-2 text-2xl font-semibold text-slate-900">{{ $stats['documenti'] ?? 0 }}</div>
|
|
<div class="mt-1 text-sm text-slate-500">Archivio PDF censito.</div>
|
|
</div>
|
|
<div class="rounded-xl border bg-white p-4">
|
|
<div class="text-xs uppercase tracking-wide text-slate-500">Archiviati</div>
|
|
<div class="mt-2 text-2xl font-semibold text-slate-900">{{ $stats['archiviati'] ?? 0 }}</div>
|
|
<div class="mt-1 text-sm text-slate-500">Documenti chiusi e consolidati.</div>
|
|
</div>
|
|
<div class="rounded-xl border bg-white p-4">
|
|
<div class="text-xs uppercase tracking-wide text-slate-500">In scadenza</div>
|
|
<div class="mt-2 text-2xl font-semibold text-slate-900">{{ $stats['in_scadenza'] ?? 0 }}</div>
|
|
<div class="mt-1 text-sm text-slate-500">Finestra dei prossimi 30 giorni.</div>
|
|
</div>
|
|
<div class="rounded-xl border bg-white p-4">
|
|
<div class="text-xs uppercase tracking-wide text-slate-500">Template Drive</div>
|
|
<div class="mt-2 text-2xl font-semibold text-slate-900">{{ $stats['template_drive'] ?? 0 }}</div>
|
|
<div class="mt-1 text-sm text-slate-500">Cartelle base replicabili per stabile.</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex flex-wrap gap-2">
|
|
@foreach($tabs as $tabKey => $tabLabel)
|
|
<button
|
|
type="button"
|
|
wire:click="selectHubTab('{{ $tabKey }}')"
|
|
@class([
|
|
'rounded-full px-4 py-2 text-sm font-medium transition',
|
|
'bg-slate-900 text-white shadow-sm' => $this->hubTab === $tabKey,
|
|
'bg-white text-slate-600 ring-1 ring-inset ring-slate-300 hover:bg-slate-50' => $this->hubTab !== $tabKey,
|
|
])
|
|
>
|
|
{{ $tabLabel }}
|
|
</button>
|
|
@endforeach
|
|
</div>
|
|
|
|
@if($this->hubTab === 'protocollo-comunicazioni')
|
|
<x-filament::section>
|
|
<x-slot name="heading">Protocollo comunicazioni</x-slot>
|
|
<x-slot name="description">Prima tab operativa per l'amministratore: ultimi documenti protocollati, filtri rapidi e vista a box con anteprima PDF o lista compatta.</x-slot>
|
|
|
|
<div class="space-y-4">
|
|
<div class="grid gap-3 md:grid-cols-2 xl:grid-cols-4">
|
|
<div class="rounded-xl border bg-white p-4">
|
|
<div class="text-xs uppercase tracking-wide text-slate-500">Protocollo visibile</div>
|
|
<div class="mt-2 text-2xl font-semibold text-slate-900">{{ $protocolloStats['totale'] ?? 0 }}</div>
|
|
<div class="mt-1 text-sm text-slate-500">Documenti non fattura nello stabile attivo.</div>
|
|
</div>
|
|
<div class="rounded-xl border bg-white p-4">
|
|
<div class="text-xs uppercase tracking-wide text-slate-500">Con PDF</div>
|
|
<div class="mt-2 text-2xl font-semibold text-slate-900">{{ $protocolloStats['con_pdf'] ?? 0 }}</div>
|
|
<div class="mt-1 text-sm text-slate-500">Apribili in anteprima o stampa.</div>
|
|
</div>
|
|
<div class="rounded-xl border bg-white p-4">
|
|
<div class="text-xs uppercase tracking-wide text-slate-500">Questo mese</div>
|
|
<div class="mt-2 text-2xl font-semibold text-slate-900">{{ $protocolloStats['questo_mese'] ?? 0 }}</div>
|
|
<div class="mt-1 text-sm text-slate-500">Nuovi inserimenti recenti.</div>
|
|
</div>
|
|
<div class="rounded-xl border bg-white p-4">
|
|
<div class="text-xs uppercase tracking-wide text-slate-500">Categorie</div>
|
|
<div class="mt-2 text-2xl font-semibold text-slate-900">{{ $protocolloStats['categorie'] ?? 0 }}</div>
|
|
<div class="mt-1 text-sm text-slate-500">Tipologie disponibili nei filtri.</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="rounded-xl border bg-white p-4">
|
|
<div class="flex flex-col gap-3 xl:flex-row xl:items-end xl:justify-between">
|
|
<div class="grid flex-1 gap-3 md:grid-cols-[minmax(0,1.4fr)_minmax(220px,0.6fr)]">
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Ricerca</label>
|
|
<input type="text" wire:model.live.debounce.300ms="protocolloSearch" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" placeholder="Protocollo, titolo, descrizione, ente, riferimento archivio..." />
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Categoria</label>
|
|
<select wire:model.live="protocolloCategory" class="w-full rounded-lg border-slate-200 text-sm shadow-sm">
|
|
<option value="">Tutte le categorie</option>
|
|
@foreach($this->protocolloComunicazioniCategoryOptions as $categoryKey => $categoryLabel)
|
|
<option value="{{ $categoryKey }}">{{ $categoryLabel }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex items-center gap-2">
|
|
<button
|
|
type="button"
|
|
wire:click="setProtocolloView('cards')"
|
|
@class([
|
|
'rounded-full px-3 py-2 text-sm font-medium transition',
|
|
'bg-slate-900 text-white' => $this->protocolloView === 'cards',
|
|
'bg-slate-100 text-slate-600 hover:bg-slate-200' => $this->protocolloView !== 'cards',
|
|
])
|
|
>
|
|
Box
|
|
</button>
|
|
<button
|
|
type="button"
|
|
wire:click="setProtocolloView('list')"
|
|
@class([
|
|
'rounded-full px-3 py-2 text-sm font-medium transition',
|
|
'bg-slate-900 text-white' => $this->protocolloView === 'list',
|
|
'bg-slate-100 text-slate-600 hover:bg-slate-200' => $this->protocolloView !== 'list',
|
|
])
|
|
>
|
|
Lista
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
@if($this->protocolloView === 'cards')
|
|
<div class="grid gap-4 md:grid-cols-2 2xl:grid-cols-3">
|
|
@forelse($this->protocolloComunicazioniRows as $row)
|
|
<article class="overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm">
|
|
<div class="space-y-3 p-4">
|
|
<div class="flex flex-wrap items-center gap-2 text-[11px] font-semibold uppercase tracking-wide text-slate-500">
|
|
<span class="rounded-full bg-slate-100 px-2.5 py-1 text-slate-700">{{ $row['protocollo'] }}</span>
|
|
<span>{{ $row['categoria'] }}</span>
|
|
<span>{{ $row['data'] }}</span>
|
|
</div>
|
|
<div>
|
|
<h3 class="text-base font-semibold text-slate-900">{{ $row['titolo'] }}</h3>
|
|
<p class="mt-1 text-sm text-slate-600">{{ $row['descrizione'] !== '' ? $row['descrizione'] : 'Nessuna descrizione registrata.' }}</p>
|
|
</div>
|
|
<div class="space-y-1 text-xs text-slate-500">
|
|
<div>Cartella: {{ $row['cartella'] }}</div>
|
|
@if($row['fornitore'] !== '')
|
|
<div>Ente / referente: {{ $row['fornitore'] }}</div>
|
|
@endif
|
|
@if($row['archive_reference'] !== '')
|
|
<div>Rif. archivio: {{ $row['archive_reference'] }}</div>
|
|
@endif
|
|
</div>
|
|
<div class="flex flex-wrap gap-2 pt-1">
|
|
<a href="{{ $row['preview_url'] }}" target="_blank" class="inline-flex items-center rounded-lg bg-slate-900 px-3 py-2 text-xs font-medium text-white hover:bg-slate-800">Apri</a>
|
|
<a href="{{ $row['download_url'] }}" 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 hover:bg-slate-50">Scarica</a>
|
|
@if($row['has_pdf'])
|
|
<a href="{{ $row['print_url'] }}" 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 hover:bg-slate-50">Stampa</a>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</article>
|
|
@empty
|
|
<div class="rounded-xl border border-dashed border-slate-300 bg-white px-4 py-10 text-sm text-slate-500 md:col-span-2 2xl:col-span-3">
|
|
Nessun documento protocollato trovato con i filtri correnti.
|
|
</div>
|
|
@endforelse
|
|
</div>
|
|
@else
|
|
<div class="overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm">
|
|
<div class="grid grid-cols-[160px_minmax(0,1.4fr)_180px_130px] gap-3 border-b border-slate-200 bg-slate-50 px-4 py-3 text-xs font-semibold uppercase tracking-wide text-slate-500">
|
|
<div>Protocollo</div>
|
|
<div>Documento</div>
|
|
<div>Categoria / cartella</div>
|
|
<div>Azioni</div>
|
|
</div>
|
|
<div class="divide-y divide-slate-200">
|
|
@forelse($this->protocolloComunicazioniRows as $row)
|
|
<div class="grid grid-cols-[160px_minmax(0,1.4fr)_180px_130px] gap-3 px-4 py-3 text-sm text-slate-700">
|
|
<div>
|
|
<div class="font-semibold text-slate-900">{{ $row['protocollo'] }}</div>
|
|
<div class="mt-1 text-xs text-slate-500">{{ $row['data'] }}</div>
|
|
</div>
|
|
<div class="min-w-0">
|
|
<div class="truncate font-semibold text-slate-900">{{ $row['titolo'] }}</div>
|
|
<div class="mt-1 text-xs text-slate-500">{{ $row['descrizione'] !== '' ? $row['descrizione'] : 'Nessuna descrizione registrata.' }}</div>
|
|
@if($row['archive_reference'] !== '')
|
|
<div class="mt-1 text-xs text-sky-700">{{ $row['archive_reference'] }}</div>
|
|
@endif
|
|
</div>
|
|
<div>
|
|
<div class="font-medium text-slate-900">{{ $row['categoria'] }}</div>
|
|
<div class="mt-1 text-xs text-slate-500">{{ $row['cartella'] }}</div>
|
|
</div>
|
|
<div class="flex flex-wrap gap-2">
|
|
<a href="{{ $row['preview_url'] }}" target="_blank" class="inline-flex items-center rounded-lg border border-slate-300 bg-white px-2.5 py-1.5 text-xs font-medium text-slate-700 hover:bg-slate-50">Apri</a>
|
|
@if($row['has_pdf'])
|
|
<a href="{{ $row['print_url'] }}" target="_blank" class="inline-flex items-center rounded-lg border border-slate-300 bg-white px-2.5 py-1.5 text-xs font-medium text-slate-700 hover:bg-slate-50">Stampa</a>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
@empty
|
|
<div class="px-4 py-8 text-sm text-slate-500">Nessun documento protocollato trovato con i filtri correnti.</div>
|
|
@endforelse
|
|
</div>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</x-filament::section>
|
|
@elseif($this->hubTab === 'archivio-digitale')
|
|
<x-filament::section>
|
|
<x-slot name="heading">Archivio digitale</x-slot>
|
|
<x-slot name="description">Navigazione a cartelle per lo stabile attivo: root stabile, cartella fatture_PDF, anno di arrivo. Le azioni restano operative ma visibili solo come icone.</x-slot>
|
|
|
|
<div class="space-y-4">
|
|
<div class="rounded-xl border border-slate-200 bg-slate-50 p-4">
|
|
<div class="flex flex-col gap-3 xl:flex-row xl:items-center xl:justify-between">
|
|
<div>
|
|
<div class="text-xs font-semibold uppercase tracking-[0.18em] text-slate-500">Percorso corrente</div>
|
|
<div class="mt-2 flex flex-wrap items-center gap-2 text-sm text-slate-700">
|
|
@foreach($this->digitalArchiveBreadcrumbs as $crumb)
|
|
<button
|
|
type="button"
|
|
wire:click="openDigitalFolder({{ $crumb['folder_id'] === null ? 'null' : $crumb['folder_id'] }})"
|
|
class="rounded-full border border-slate-200 bg-white px-3 py-1.5 font-medium text-slate-700 transition hover:border-sky-300 hover:text-sky-700"
|
|
>
|
|
{{ $crumb['label'] }}
|
|
</button>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
<div class="rounded-lg border border-sky-100 bg-white px-4 py-3 text-xs text-slate-600">
|
|
Nomi file fatture ordinati per data e protocollo, cosi la ricerca e l'elenco restano leggibili anche senza aprire il PDF.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid gap-3 md:grid-cols-2 xl:grid-cols-4">
|
|
@forelse($this->digitalArchiveFolders as $folder)
|
|
<button
|
|
type="button"
|
|
wire:click="openDigitalFolder({{ $folder['id'] }})"
|
|
class="rounded-2xl border border-slate-200 bg-white p-4 text-left shadow-sm transition hover:-translate-y-0.5 hover:border-sky-300 hover:shadow"
|
|
>
|
|
<div class="flex items-start justify-between gap-3">
|
|
<div>
|
|
<div class="text-sm font-semibold text-slate-900">{{ $folder['nome'] }}</div>
|
|
<div class="mt-1 text-xs text-slate-500">{{ $folder['descrizione'] }}</div>
|
|
</div>
|
|
<div class="rounded-full bg-sky-50 px-2.5 py-1 text-xs font-semibold text-sky-700">{{ $folder['conteggio'] }}</div>
|
|
</div>
|
|
</button>
|
|
@empty
|
|
<div class="rounded-xl border border-dashed border-slate-300 bg-white px-4 py-8 text-sm text-slate-500 md:col-span-2 xl:col-span-4">
|
|
Nessuna sottocartella disponibile nel livello corrente.
|
|
</div>
|
|
@endforelse
|
|
</div>
|
|
|
|
<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>
|
|
{{ $this->table }}
|
|
</div>
|
|
</div>
|
|
</x-filament::section>
|
|
@elseif($this->hubTab === 'scansione-documenti')
|
|
<x-filament::section>
|
|
<x-slot name="heading">Scansione documenti</x-slot>
|
|
<x-slot name="description">Acquisizione staging-ready per PDF generati da scanner Epson, cartelle di rete o caricamento manuale. Il salvataggio usa lo stesso motore documento, quindi protocollo, archivio e OCR restano allineati.</x-slot>
|
|
|
|
<div class="grid gap-4 xl:grid-cols-[minmax(0,0.95fr)_minmax(0,1.05fr)]">
|
|
<div class="rounded-xl border bg-white p-4">
|
|
<div class="flex items-center justify-between gap-3">
|
|
<div>
|
|
<div class="text-sm font-semibold text-slate-900">Nuova acquisizione scanner</div>
|
|
<div class="mt-1 text-xs text-slate-500">Carica il PDF prodotto dallo scanner, assegna subito stabile, cartella e classe archivio, poi lascia al backend rinomina, protocollo e OCR.</div>
|
|
</div>
|
|
<div class="rounded-full bg-emerald-50 px-3 py-1 text-xs font-semibold text-emerald-700">OCR automatico</div>
|
|
</div>
|
|
|
|
<div class="mt-4 grid gap-3 md:grid-cols-2">
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Stabile</label>
|
|
<select wire:model.live="scannerAcquisitionForm.stabile_id" class="w-full rounded-lg border-slate-200 text-sm shadow-sm">
|
|
<option value="">Seleziona stabile</option>
|
|
@foreach($this->scannerAcquisitionStabiliOptions as $stabileId => $stabileLabel)
|
|
<option value="{{ $stabileId }}">{{ $stabileLabel }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Canale acquisizione</label>
|
|
<select wire:model.defer="scannerAcquisitionForm.canale_acquisizione" class="w-full rounded-lg border-slate-200 text-sm shadow-sm">
|
|
@foreach($this->getScannerAcquisitionChannelOptions() as $channelKey => $channelLabel)
|
|
<option value="{{ $channelKey }}">{{ $channelLabel }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
<div class="md:col-span-2">
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">PDF scansione</label>
|
|
<input type="file" wire:model="scannerPdf" accept="application/pdf" class="block w-full rounded-lg border border-slate-200 bg-white text-sm text-slate-700 shadow-sm file:mr-4 file:rounded-md file:border-0 file:bg-slate-900 file:px-3 file:py-2 file:text-sm file:font-medium file:text-white" />
|
|
<div wire:loading wire:target="scannerPdf" class="mt-2 text-xs text-slate-500">Caricamento PDF in corso...</div>
|
|
</div>
|
|
<div class="md:col-span-2">
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Titolo documento</label>
|
|
<input type="text" wire:model.defer="scannerAcquisitionForm.titolo" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" placeholder="Es. Contratto manutenzione ascensore 2025" />
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Categoria</label>
|
|
<select wire:model.defer="scannerAcquisitionForm.categoria" class="w-full rounded-lg border-slate-200 text-sm shadow-sm">
|
|
@foreach($this->scannerCategoryOptions as $categoryKey => $categoryLabel)
|
|
<option value="{{ $categoryKey }}">{{ $categoryLabel }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Classe archivio</label>
|
|
<select wire:model.defer="scannerAcquisitionForm.archivio_classe" class="w-full rounded-lg border-slate-200 text-sm shadow-sm">
|
|
@foreach($this->getArchivioClasseOptions() as $classKey => $classLabel)
|
|
<option value="{{ $classKey }}">{{ $classLabel }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Cartella archivio</label>
|
|
<select wire:model.defer="scannerAcquisitionForm.cartella_archivio_id" class="w-full rounded-lg border-slate-200 text-sm shadow-sm">
|
|
<option value="">Cartella automatica</option>
|
|
@foreach($this->scannerAcquisitionFolderOptions as $folderId => $folderLabel)
|
|
<option value="{{ $folderId }}">{{ $folderLabel }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Visibilità</label>
|
|
<select wire:model.defer="scannerAcquisitionForm.visibility_scope" class="w-full rounded-lg border-slate-200 text-sm shadow-sm">
|
|
@foreach($this->scannerVisibilityScopeOptions as $scopeKey => $scopeLabel)
|
|
<option value="{{ $scopeKey }}">{{ $scopeLabel }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Fornitore / mittente</label>
|
|
<input type="text" wire:model.defer="scannerAcquisitionForm.fornitore" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" placeholder="Es. OTIS" />
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Profilo scanner</label>
|
|
<input type="text" wire:model.defer="scannerAcquisitionForm.scanner_profile" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" placeholder="Es. Epson ADF fronte-retro 300dpi" />
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Data documento</label>
|
|
<input type="date" wire:model.defer="scannerAcquisitionForm.data_documento" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" />
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Riferimento batch</label>
|
|
<input type="text" wire:model.defer="scannerAcquisitionForm.riferimento_acquisizione" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" placeholder="Es. EPSON-ADF-20250308-01" />
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Faldone / raccoglitore</label>
|
|
<input type="text" wire:model.defer="scannerAcquisitionForm.faldone" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" placeholder="Es. Faldone contratti ascensori" />
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Supporto fisico</label>
|
|
<input type="text" wire:model.defer="scannerAcquisitionForm.supporto_fisico" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" placeholder="Es. Cartella trasparente" />
|
|
</div>
|
|
<div class="md:col-span-2">
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Descrizione operativa</label>
|
|
<textarea wire:model.defer="scannerAcquisitionForm.descrizione" rows="3" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" placeholder="Oggetto del documento, note utili per ricerca e protocollo..."></textarea>
|
|
</div>
|
|
<div class="md:col-span-2">
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Note scanner</label>
|
|
<textarea wire:model.defer="scannerAcquisitionForm.scanner_note" rows="3" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" placeholder="Es. PDF creato da cartella di rete EPSON, 22 pagine, fronte-retro"></textarea>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-4 flex flex-wrap gap-2">
|
|
<button type="button" wire:click="saveScannedDocument" class="inline-flex items-center rounded-lg bg-slate-900 px-4 py-2 text-sm font-medium text-white shadow-sm transition hover:bg-slate-800">Acquisisci documento scanner</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="space-y-4">
|
|
<div class="rounded-xl border bg-white p-4">
|
|
<div class="text-sm font-semibold text-slate-900">Flusso pratico per staging</div>
|
|
<div class="mt-3 grid gap-3 text-sm text-slate-600 md:grid-cols-2">
|
|
<div class="rounded-lg border border-slate-200 bg-slate-50 px-3 py-3">Lo scanner genera un PDF. Qui lo carichi una sola volta e il sistema lo rinomina secondo stabile, anno e protocollo.</div>
|
|
<div class="rounded-lg border border-slate-200 bg-slate-50 px-3 py-3">I metadati di acquisizione restano nel documento per distinguere upload manuale, cartella Epson, email o mobile.</div>
|
|
<div class="rounded-lg border border-slate-200 bg-slate-50 px-3 py-3">La pipeline OCR esistente parte subito dopo il salvataggio, senza creare un ramo tecnico separato.</div>
|
|
<div class="rounded-lg border border-slate-200 bg-slate-50 px-3 py-3">La cartella archivio puo essere fissata subito oppure lasciata automatica in base a categoria e anno.</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="rounded-xl border border-amber-200 bg-amber-50 p-4">
|
|
<div class="text-sm font-semibold text-amber-900">EPSON e browser</div>
|
|
<div class="mt-2 text-sm text-amber-800">Per la prova staging non serve pilotare lo scanner dal browser. Il percorso operativo corretto resta: scansione su PDF dal dispositivo, poi acquisizione da questa tab con OCR e metadati gia coerenti col resto dell'archivio.</div>
|
|
</div>
|
|
|
|
<div class="rounded-xl border bg-white p-4">
|
|
<div class="text-sm font-semibold text-slate-900">Effetti del salvataggio</div>
|
|
<div class="mt-3 space-y-2 text-sm text-slate-600">
|
|
<div class="rounded-lg border border-slate-200 bg-slate-50 px-3 py-3">Numero protocollo generato automaticamente in base a categoria e anno del documento.</div>
|
|
<div class="rounded-lg border border-slate-200 bg-slate-50 px-3 py-3">Rinominazione ordinata del file nello storage locale del progetto.</div>
|
|
<div class="rounded-lg border border-slate-200 bg-slate-50 px-3 py-3">Metadati archivio pronti per QR, collocazione fisica e ricerche OCR successive.</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</x-filament::section>
|
|
@elseif($this->hubTab === 'etichette-generiche')
|
|
<x-filament::section>
|
|
<x-slot name="heading">Etichette generiche per contenitori</x-slot>
|
|
<x-slot name="description">Tab dedicata alla stampa rapida di etichette per faldoni, scatole, raccoglitori e buste. Il QR resta obbligatorio e il codice generato diventa il riferimento stabile per annidamento e scansione.</x-slot>
|
|
|
|
<div class="grid gap-4 xl:grid-cols-[minmax(0,0.95fr)_minmax(0,1.05fr)]">
|
|
<div class="rounded-xl border bg-white p-4">
|
|
<div class="flex items-center justify-between gap-3">
|
|
<div>
|
|
<div class="text-sm font-semibold text-slate-900">Nuova etichetta generica</div>
|
|
<div class="mt-1 text-xs text-slate-500">Crea il contenitore, stampa subito e poi usalo come nodo della catena QR: fattura, cartella, dock, scatola, magazzino, scaffale.</div>
|
|
</div>
|
|
<div class="rounded-full bg-slate-100 px-3 py-1 text-xs font-semibold text-slate-700">QR incluso</div>
|
|
</div>
|
|
|
|
<div class="mt-4 grid gap-3 md:grid-cols-2">
|
|
<div class="md:col-span-2">
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Titolo etichetta</label>
|
|
<input type="text" wire:model.defer="genericLabelForm.titolo" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" placeholder="Es. SCATOLA CONTRATTI ASCENSORI" />
|
|
</div>
|
|
<div class="md:col-span-2">
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Seconda riga</label>
|
|
<input type="text" wire:model.defer="genericLabelForm.linea_secondaria" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" placeholder="Es. Stabile Roma 12 · 2024/2026" />
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Tipologia</label>
|
|
<select wire:model.defer="genericLabelForm.tipo_item" class="w-full rounded-lg border-slate-200 text-sm shadow-sm">
|
|
@foreach($this->physicalArchiveTypeOptions as $typeKey => $typeLabel)
|
|
@if($typeKey !== 'documento')
|
|
<option value="{{ $typeKey }}">{{ $typeLabel }}</option>
|
|
@endif
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Modello etichetta</label>
|
|
<select wire:model.defer="genericLabelForm.modello_etichetta" class="w-full rounded-lg border-slate-200 text-sm shadow-sm">
|
|
@foreach($this->getModelliEtichettaOptions() as $labelKey => $labelText)
|
|
<option value="{{ $labelKey }}">{{ $labelText }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Dentro contenitore</label>
|
|
<select wire:model.defer="genericLabelForm.parent_id" class="w-full rounded-lg border-slate-200 text-sm shadow-sm">
|
|
<option value="">Radice archivio</option>
|
|
@foreach($this->physicalArchiveParentOptions as $parentId => $parentLabel)
|
|
<option value="{{ $parentId }}">{{ $parentLabel }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Supporto</label>
|
|
<input type="text" wire:model.defer="genericLabelForm.supporto_fisico" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" placeholder="Es. Scatola o raccoglitore" />
|
|
</div>
|
|
<div class="md:col-span-2">
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Link acquisto / Amazon</label>
|
|
<input type="text" wire:model.defer="genericLabelForm.amazon_url" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" placeholder="URL completo o ASIN Amazon" />
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Magazzino</label>
|
|
<input type="text" wire:model.defer="genericLabelForm.magazzino" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" placeholder="Es. Deposito EUR" />
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Scaffale</label>
|
|
<input type="text" wire:model.defer="genericLabelForm.scaffale" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" placeholder="Es. B2" />
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Ripiano</label>
|
|
<input type="text" wire:model.defer="genericLabelForm.ripiano" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" placeholder="Es. 4" />
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Dettaglio ubicazione</label>
|
|
<input type="text" wire:model.defer="genericLabelForm.ubicazione_dettaglio" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" placeholder="Es. corridoio destro" />
|
|
</div>
|
|
<div class="md:col-span-2">
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Nota breve</label>
|
|
<textarea wire:model.defer="genericLabelForm.note" rows="3" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" placeholder="Contenuto, periodo, riferimenti operativi..."></textarea>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-4 flex flex-wrap gap-2">
|
|
<button type="button" wire:click="saveGenericArchiveLabel" class="inline-flex items-center rounded-lg bg-slate-900 px-4 py-2 text-sm font-medium text-white shadow-sm transition hover:bg-slate-800">Crea e prepara la stampa</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="space-y-4">
|
|
<div class="rounded-xl border bg-white p-4">
|
|
<div class="text-sm font-semibold text-slate-900">Standard operativo</div>
|
|
<div class="mt-3 grid gap-3 text-sm text-slate-600 md:grid-cols-2">
|
|
<div class="rounded-lg border border-slate-200 bg-slate-50 px-3 py-3">Il codice univoco viene generato automaticamente e diventa il nodo da scansionare.</div>
|
|
<div class="rounded-lg border border-slate-200 bg-slate-50 px-3 py-3">Il QR usa lo stesso schema pubblico gia host-agnostic del resto dell'archivio fisico.</div>
|
|
<div class="rounded-lg border border-slate-200 bg-slate-50 px-3 py-3">Il link Amazon puo essere salvato come URL o come ASIN, con tag affiliato aggiunto dal super-admin.</div>
|
|
<div class="rounded-lg border border-slate-200 bg-slate-50 px-3 py-3">Questa tab rende visibile la nuova etichetta senza doverla cercare dentro il registro generale.</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="rounded-xl border bg-white p-4">
|
|
<div class="flex items-center justify-between gap-3">
|
|
<div class="text-sm font-semibold text-slate-900">Ultime etichette generiche</div>
|
|
<div class="text-xs text-slate-500">Stampa immediata 11354 / 99014</div>
|
|
</div>
|
|
<div class="mt-4 space-y-3">
|
|
@forelse($this->genericLabelRows as $row)
|
|
<div class="rounded-lg border border-slate-200 bg-slate-50 px-3 py-3">
|
|
<div class="flex flex-col gap-3 xl:flex-row xl:items-start xl:justify-between">
|
|
<div>
|
|
<div class="flex flex-wrap items-center gap-2">
|
|
<span class="rounded-full bg-slate-900 px-2.5 py-1 text-[11px] font-semibold text-white">{{ $row['codice_univoco'] }}</span>
|
|
<span class="rounded-full bg-white px-2.5 py-1 text-[11px] font-medium text-slate-700">{{ $row['tipo'] }}</span>
|
|
</div>
|
|
<div class="mt-2 text-sm font-semibold text-slate-900">{{ $row['titolo'] }}</div>
|
|
@if($row['linea_secondaria'] !== '')
|
|
<div class="mt-1 text-xs text-slate-500">{{ $row['linea_secondaria'] }}</div>
|
|
@endif
|
|
<div class="mt-1 text-xs text-slate-500">Percorso: {{ $row['percorso'] }}</div>
|
|
@if($row['ubicazione'] !== '')
|
|
<div class="mt-1 text-xs text-slate-500">Ubicazione: {{ $row['ubicazione'] }}</div>
|
|
@endif
|
|
@if($row['amazon_url'] !== '')
|
|
<div class="mt-2"><a href="{{ $row['amazon_url'] }}" target="_blank" class="text-xs font-medium text-sky-700 underline decoration-sky-300 underline-offset-2">Apri link acquisto</a></div>
|
|
@endif
|
|
</div>
|
|
<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&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=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>
|
|
@empty
|
|
<div class="rounded-lg border border-dashed border-slate-300 bg-white px-4 py-6 text-sm text-slate-500">Nessuna etichetta generica creata ancora per lo stabile attivo.</div>
|
|
@endforelse
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</x-filament::section>
|
|
@elseif($this->hubTab === 'movimentazione-codici')
|
|
<x-filament::section>
|
|
<x-slot name="heading">Movimentazione per codice</x-slot>
|
|
<x-slot name="description">Pagina autonoma per spostare contenitori e documenti tramite codice o scansione. Serve da base alla catena annidata e ai futuri flussi con scanner EPSON.</x-slot>
|
|
|
|
<div class="grid gap-4 xl:grid-cols-[minmax(0,0.9fr)_minmax(0,1.1fr)]">
|
|
<div class="space-y-4">
|
|
<div class="rounded-xl border bg-white p-4">
|
|
<div class="text-sm font-semibold text-slate-900">Sposta per codice</div>
|
|
<div class="mt-1 text-xs text-slate-500">Scansiona o inserisci codice sorgente e codice destinazione. Il contenitore padre viene aggiornato senza dover aprire il registro generale.</div>
|
|
<div class="mt-4 grid gap-3 md:grid-cols-2">
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Codice sorgente</label>
|
|
<input type="text" wire:model.defer="physicalArchiveMoveForm.source_code" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" placeholder="Es. AF-20-0001" />
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Codice destinazione</label>
|
|
<input type="text" wire:model.defer="physicalArchiveMoveForm.target_code" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" placeholder="Es. AF-20-0010" />
|
|
</div>
|
|
</div>
|
|
<div class="mt-4 flex flex-wrap gap-2">
|
|
<button type="button" wire:click="movePhysicalArchiveItem" class="inline-flex items-center rounded-lg border border-slate-300 bg-white px-4 py-2 text-sm font-medium text-slate-700 shadow-sm transition hover:border-slate-400 hover:bg-slate-50">Registra movimentazione</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="rounded-xl border border-amber-200 bg-amber-50 p-4">
|
|
<div class="text-sm font-semibold text-amber-900">Catena QR prevista</div>
|
|
<div class="mt-2 text-sm text-amber-800">QR Fattura -> QR Cartella trasparente -> QR Dock -> QR Scatola -> QR Magazzino -> QR Scaffale. Questa tab separa il movimento dal censimento del contenuto.</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="rounded-xl border bg-white p-4">
|
|
<div class="text-sm font-semibold text-slate-900">Contenitori di primo livello</div>
|
|
<div class="mt-3 grid gap-3 sm:grid-cols-2 xl:grid-cols-3">
|
|
@forelse($this->physicalArchiveContainerTree as $item)
|
|
<div class="rounded-lg border border-slate-200 bg-slate-50 px-3 py-3">
|
|
<div class="text-sm font-semibold text-slate-900">{{ $item['codice_univoco'] }}</div>
|
|
<div class="mt-1 text-sm text-slate-700">{{ $item['titolo'] }}</div>
|
|
<div class="mt-1 text-xs text-slate-500">{{ $item['tipo'] }} · {{ $item['children_count'] }} elementi interni</div>
|
|
</div>
|
|
@empty
|
|
<div class="rounded-lg border border-dashed border-slate-300 bg-white px-4 py-6 text-sm text-slate-500 sm:col-span-2 xl:col-span-3">Nessun contenitore creato ancora per lo stabile attivo.</div>
|
|
@endforelse
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</x-filament::section>
|
|
@elseif($this->hubTab === 'template-drive')
|
|
<x-filament::section>
|
|
<x-slot name="heading">Template cartelle Drive</x-slot>
|
|
<x-slot name="description">Questa struttura resta la base comune per archivio digitale, passaggio consegne e futura sincronizzazione Google. Ora puo essere gestita da database a livello super-admin, con fallback automatico alla config storica.</x-slot>
|
|
|
|
<div class="space-y-4">
|
|
<div class="rounded-xl border bg-white p-4">
|
|
<div class="flex flex-col gap-3 xl:flex-row xl:items-start xl:justify-between">
|
|
<div>
|
|
<div class="text-sm font-semibold text-slate-900">Origine struttura</div>
|
|
<div class="mt-1 text-xs text-slate-500">Fonte attuale: <span class="font-semibold text-slate-900">{{ $this->driveTemplateSource === 'database' ? 'database' : 'config' }}</span></div>
|
|
</div>
|
|
<div class="rounded-full bg-slate-100 px-3 py-1 text-xs font-semibold text-slate-700">{{ count($this->driveTemplateFolders) }} cartelle logiche</div>
|
|
</div>
|
|
|
|
@if($this->canManageDriveTemplates)
|
|
<div class="mt-4 grid gap-3 md:grid-cols-2">
|
|
<div class="md:col-span-2">
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Cartelle base</label>
|
|
<textarea wire:model.defer="driveTemplateForm.folders_text" rows="10" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" placeholder="Una cartella per riga"></textarea>
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Radice annuale</label>
|
|
<input type="text" wire:model.defer="driveTemplateForm.year_root" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" placeholder="Es. archivio_per_anno" />
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Modello anno</label>
|
|
<input type="text" wire:model.defer="driveTemplateForm.year_model" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" placeholder="Es. _anno_modello" />
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Amazon tag affiliato</label>
|
|
<input type="text" wire:model.defer="driveTemplateForm.amazon_affiliate_tag" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" placeholder="Es. netgescon-21" />
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Amazon base URL</label>
|
|
<input type="text" wire:model.defer="driveTemplateForm.amazon_base_url" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" placeholder="Es. https://www.amazon.it/dp" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-4 flex flex-wrap gap-2">
|
|
<button type="button" wire:click="saveDriveTemplateSettings" class="inline-flex items-center rounded-lg bg-slate-900 px-4 py-2 text-sm font-medium text-white shadow-sm transition hover:bg-slate-800">Salva template su database</button>
|
|
</div>
|
|
@else
|
|
<div class="mt-4 rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-800">La modifica del template e dei parametri Amazon e riservata al super-admin. Gli altri ruoli vedono comunque la struttura effettiva.</div>
|
|
@endif
|
|
</div>
|
|
|
|
<div class="grid gap-3 md:grid-cols-2 xl:grid-cols-3">
|
|
@foreach($this->driveTemplateFolders as $folder)
|
|
<div class="rounded-xl border bg-slate-50 px-4 py-3 text-sm text-slate-700">{{ $folder }}</div>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
</x-filament::section>
|
|
@elseif($this->hubTab === 'archivio-fisico')
|
|
<x-filament::section>
|
|
<x-slot name="heading">Archivio fisico</x-slot>
|
|
<x-slot name="description">Registro operativo del cartaceo: inserimento manuale, contenitori annidati e collegamento al digitale. La stampa rapida delle etichette generiche e la movimentazione hanno ora una tab dedicata.</x-slot>
|
|
|
|
<div class="grid gap-4 xl:grid-cols-[minmax(0,1.25fr)_minmax(0,0.75fr)]">
|
|
<div class="space-y-4">
|
|
<div class="rounded-xl border bg-white p-4">
|
|
<div class="flex items-center justify-between gap-3">
|
|
<div>
|
|
<div class="text-sm font-semibold text-slate-900">Elenco archivio condominiale</div>
|
|
<div class="mt-1 text-xs text-slate-500">Inserimento manuale come ticket: data, voce, titolo, nota, contenitore iniziale e posizione finale.</div>
|
|
</div>
|
|
<div class="rounded-full bg-slate-100 px-3 py-1 text-xs font-semibold text-slate-700">DB operativo</div>
|
|
</div>
|
|
|
|
<div class="mt-4 grid gap-3 md:grid-cols-2">
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Data</label>
|
|
<input type="date" wire:model.defer="physicalArchiveForm.data_archiviazione" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" />
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Voce archivio</label>
|
|
<select wire:model.live="physicalArchiveForm.voce_numero" class="w-full rounded-lg border-slate-200 text-sm shadow-sm">
|
|
<option value="">Seleziona voce</option>
|
|
@foreach($this->archivioObbligatorioRows as $row)
|
|
<option value="{{ $row['numero'] }}">Voce {{ $row['numero'] }} · {{ $row['titolo'] }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
<div class="md:col-span-2">
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Titolo da archiviare</label>
|
|
<input type="text" wire:model.defer="physicalArchiveForm.titolo" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" placeholder="Es. Contratto ascensore ORONA 2024" />
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Tipologia elemento</label>
|
|
<select wire:model.live="physicalArchiveForm.tipo_item" class="w-full rounded-lg border-slate-200 text-sm shadow-sm">
|
|
@foreach($this->physicalArchiveTypeOptions as $typeKey => $typeLabel)
|
|
<option value="{{ $typeKey }}">{{ $typeLabel }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Supporto iniziale</label>
|
|
<select wire:model.defer="physicalArchiveForm.supporto_fisico" class="w-full rounded-lg border-slate-200 text-sm shadow-sm">
|
|
<option value="">Nessun supporto</option>
|
|
@foreach($this->archivioFisicoTypes as $type)
|
|
<option value="{{ $type }}">{{ $type }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Documento digitale collegato</label>
|
|
<select wire:model.defer="physicalArchiveForm.documento_id" class="w-full rounded-lg border-slate-200 text-sm shadow-sm">
|
|
<option value="">Nessun documento digitale</option>
|
|
@foreach($this->physicalArchiveDocumentOptions as $documentId => $documentLabel)
|
|
<option value="{{ $documentId }}">{{ $documentLabel }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Contenitore padre</label>
|
|
<select wire:model.defer="physicalArchiveForm.parent_id" class="w-full rounded-lg border-slate-200 text-sm shadow-sm">
|
|
<option value="">Radice archivio</option>
|
|
@foreach($this->physicalArchiveParentOptions as $parentId => $parentLabel)
|
|
<option value="{{ $parentId }}">{{ $parentLabel }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Etichetta</label>
|
|
<select wire:model.defer="physicalArchiveForm.modello_etichetta" class="w-full rounded-lg border-slate-200 text-sm shadow-sm">
|
|
@foreach($this->getModelliEtichettaOptions() as $labelKey => $labelText)
|
|
<option value="{{ $labelKey }}">{{ $labelText }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
<div class="md:col-span-2">
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Nota</label>
|
|
<textarea wire:model.defer="physicalArchiveForm.note" rows="3" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" placeholder="Elenco fogli, riferimento verbale, contenuto della busta..."></textarea>
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Magazzino</label>
|
|
<input type="text" wire:model.defer="physicalArchiveForm.magazzino" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" placeholder="Es. Sede EUR" />
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Scaffale</label>
|
|
<input type="text" wire:model.defer="physicalArchiveForm.scaffale" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" placeholder="Es. A3" />
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Ripiano</label>
|
|
<input type="text" wire:model.defer="physicalArchiveForm.ripiano" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" placeholder="Es. 2" />
|
|
</div>
|
|
<div>
|
|
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Dettaglio ubicazione</label>
|
|
<input type="text" wire:model.defer="physicalArchiveForm.ubicazione_dettaglio" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" placeholder="Es. lato finestra" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-4 flex flex-wrap gap-2">
|
|
<button type="button" wire:click="savePhysicalArchiveEntry" class="inline-flex items-center rounded-lg bg-slate-900 px-4 py-2 text-sm font-medium text-white shadow-sm transition hover:bg-slate-800">Registra voce fisica</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="rounded-xl border bg-white p-4">
|
|
<div class="text-sm font-semibold text-slate-900">Contenitori di primo livello</div>
|
|
<div class="mt-3 grid gap-3 sm:grid-cols-2 xl:grid-cols-3">
|
|
@forelse($this->physicalArchiveContainerTree as $item)
|
|
<div class="rounded-lg border border-slate-200 bg-slate-50 px-3 py-3">
|
|
<div class="flex items-start justify-between gap-3">
|
|
<div>
|
|
<div class="text-sm font-semibold text-slate-900">{{ $item['codice_univoco'] }}</div>
|
|
<div class="mt-1 text-sm text-slate-700">{{ $item['titolo'] }}</div>
|
|
<div class="mt-1 text-xs text-slate-500">{{ $item['tipo'] }} · {{ $item['children_count'] }} elementi interni</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@empty
|
|
<div class="rounded-lg border border-dashed border-slate-300 bg-white px-4 py-6 text-sm text-slate-500 sm:col-span-2 xl:col-span-3">Nessun contenitore creato ancora per lo stabile attivo.</div>
|
|
@endforelse
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="space-y-4">
|
|
<div class="rounded-xl border bg-white p-4">
|
|
<div class="text-sm font-semibold text-slate-900">Standard etichette</div>
|
|
<div class="mt-3 space-y-3 text-sm text-slate-600">
|
|
<div class="rounded-lg border border-slate-200 bg-slate-50 px-3 py-3">Modelli attivi: <span class="font-semibold text-slate-900">11354</span> e <span class="font-semibold text-slate-900">99014</span>.</div>
|
|
<div class="rounded-lg border border-slate-200 bg-slate-50 px-3 py-3">Formato suggerito: <span class="font-semibold text-slate-900">{{ $this->mnemonicCodeHint ?? 'STB00000' }}</span> · contenuto · anno · busta · fascicolo.</div>
|
|
<div class="rounded-lg border border-slate-200 bg-slate-50 px-3 py-3">QR in stampa su etichetta e predisposizione NFC per scatole e fascicoli.</div>
|
|
<div class="rounded-lg border border-slate-200 bg-slate-50 px-3 py-3">Formato data archivio: <span class="font-semibold text-slate-900">AAAA-MM-GG</span> o <span class="font-semibold text-slate-900">YYYYMMDD</span> per aggancio automatico ai file gia codificati.</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="rounded-xl border bg-white p-4">
|
|
<div class="flex items-center justify-between gap-3">
|
|
<div class="text-sm font-semibold text-slate-900">Registro e ricerca</div>
|
|
<div class="text-xs text-slate-500">Titolo manuale, ricerca testuale, contenimento annidato</div>
|
|
</div>
|
|
<div class="mt-3 grid gap-3 md:grid-cols-2">
|
|
<input type="text" wire:model.live.debounce.300ms="physicalArchiveSearch" class="w-full rounded-lg border-slate-200 text-sm shadow-sm" placeholder="Cerca per titolo, codice, nota..." />
|
|
<select wire:model.live="physicalArchiveTypeFilter" class="w-full rounded-lg border-slate-200 text-sm shadow-sm">
|
|
<option value="">Tutte le tipologie</option>
|
|
@foreach($this->physicalArchiveTypeOptions as $typeKey => $typeLabel)
|
|
<option value="{{ $typeKey }}">{{ $typeLabel }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
<div class="mt-4 space-y-3">
|
|
@forelse($this->physicalArchiveRows as $row)
|
|
<div class="rounded-lg border border-slate-200 bg-slate-50 px-3 py-3">
|
|
<div class="flex flex-col gap-3 xl:flex-row xl:items-start xl:justify-between">
|
|
<div class="min-w-0">
|
|
<div class="flex flex-wrap items-center gap-2">
|
|
<span class="rounded-full bg-slate-900 px-2.5 py-1 text-[11px] font-semibold text-white">{{ $row['codice_univoco'] }}</span>
|
|
<span class="rounded-full bg-white px-2.5 py-1 text-[11px] font-medium text-slate-700">{{ $row['tipo'] }}</span>
|
|
<span class="text-xs text-slate-500">{{ $row['data_archiviazione'] }}</span>
|
|
</div>
|
|
<div class="mt-2 text-sm font-semibold text-slate-900">{{ $row['titolo'] }}</div>
|
|
@if($row['voce'] !== '')
|
|
<div class="mt-1 text-xs text-slate-500">{{ $row['voce'] }}</div>
|
|
@endif
|
|
<div class="mt-1 text-xs text-slate-500">Dentro: {{ $row['parent'] }}</div>
|
|
<div class="mt-1 text-xs text-slate-500">Percorso: {{ $row['percorso'] }}</div>
|
|
@if($row['ubicazione'] !== '')
|
|
<div class="mt-1 text-xs text-slate-500">Ubicazione: {{ $row['ubicazione'] }}</div>
|
|
@endif
|
|
@if($row['documento'])
|
|
<div class="mt-1 text-xs text-sky-700">Digitale collegato: {{ $row['documento'] }}</div>
|
|
@endif
|
|
</div>
|
|
<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&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=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>
|
|
@empty
|
|
<div class="rounded-lg border border-dashed border-slate-300 bg-white px-4 py-6 text-sm text-slate-500">Nessuna voce archivio fisico trovata con i filtri correnti.</div>
|
|
@endforelse
|
|
</div>
|
|
</div>
|
|
|
|
<div class="rounded-xl border border-amber-200 bg-amber-50 p-4">
|
|
<div class="text-sm font-semibold text-amber-900">Uso operativo</div>
|
|
<div class="mt-2 text-sm text-amber-800">Puoi registrare direttamente un pezzo di carta senza scansione, poi metterlo in una busta etichettata, spostare la busta in un faldone e il faldone in una scatola usando solo i codici stampati.</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</x-filament::section>
|
|
@else
|
|
<x-filament::section>
|
|
<x-slot name="heading">Permessi e visibilita</x-slot>
|
|
<x-slot name="description">Qui prepariamo la classificazione dei documenti per audience e le cartelle visibili solo a ruoli, gruppi o utenti selezionati.</x-slot>
|
|
|
|
<div class="grid gap-4 xl:grid-cols-[minmax(0,1.3fr)_minmax(280px,0.7fr)]">
|
|
<div>
|
|
<div class="grid gap-3 md:grid-cols-2 xl:grid-cols-3">
|
|
@foreach($this->visibleFolderRows as $folder)
|
|
<div class="rounded-xl border bg-white px-4 py-3">
|
|
<div class="flex items-start justify-between gap-3">
|
|
<div>
|
|
<div class="text-sm font-semibold text-slate-900">{{ $folder['nome'] }}</div>
|
|
<div class="mt-1 text-xs text-slate-500">{{ $folder['stabile'] }}</div>
|
|
</div>
|
|
<div class="rounded-full bg-slate-100 px-2 py-1 text-[11px] font-medium text-slate-700">{{ $folder['scope'] }}</div>
|
|
</div>
|
|
<div class="mt-2 text-xs text-slate-600">{{ $folder['path'] }}</div>
|
|
<div class="mt-3 text-xs text-slate-500">Documenti collegati: {{ $folder['documents_count'] }}</div>
|
|
<div class="mt-2 text-xs text-slate-500">Ruoli: {{ $folder['roles'] !== [] ? implode(', ', $folder['roles']) : 'nessun filtro' }}</div>
|
|
<div class="mt-1 text-xs text-slate-500">Gruppi: {{ $folder['groups'] !== [] ? implode(', ', $folder['groups']) : 'nessun filtro' }}</div>
|
|
<div class="mt-1 text-xs text-slate-500">Utenti: {{ $folder['users'] !== [] ? implode(', ', $folder['users']) : 'nessun utente specifico' }}</div>
|
|
</div>
|
|
@endforeach
|
|
@if(empty($this->visibleFolderRows))
|
|
<div class="rounded-xl border border-dashed bg-white px-4 py-6 text-sm text-slate-500 md:col-span-2 xl:col-span-3">Nessuna cartella archivio configurata per lo stabile attivo. Usa “Nuova cartella archivio” per iniziare a separare visibilita e struttura.</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
<div class="space-y-3">
|
|
@foreach($this->audienceGroups as $group)
|
|
<div class="rounded-xl border bg-white px-4 py-3">
|
|
<div class="text-sm font-semibold text-slate-900">{{ $group }}</div>
|
|
<div class="mt-1 text-xs text-slate-500">Audience candidata per visibilita documentale, export e portali dedicati.</div>
|
|
</div>
|
|
@endforeach
|
|
<div class="rounded-xl border border-amber-200 bg-amber-50 px-4 py-3 text-xs text-amber-800">
|
|
Le cartelle ristrette sono applicate sia nella lista documenti sia nei download diretti, cosi l'ID del file non bypassa i permessi dello stabile o della cartella.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</x-filament::section>
|
|
@endif
|
|
</div>
|
|
</x-filament-panels::page> |