netgescon-day0/resources/views/filament/pages/supporto/ticket-acqua.blade.php

219 lines
15 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<x-filament-panels::page>
<div class="space-y-4">
@if ($errors->any())
<div class="rounded-xl border border-rose-300 bg-rose-50 p-3 text-sm text-rose-800">
<div class="font-semibold">Controlla i dati prima di inviare</div>
<ul class="mt-2 list-disc pl-5">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<div class="rounded-2xl border bg-white p-4 shadow-sm">
<div class="flex flex-wrap items-start justify-between gap-3">
<div>
<h2 class="text-lg font-semibold text-slate-900">Ticket Acqua</h2>
<div class="mt-1 text-sm text-slate-600">Pagina mobile dedicata alle autoletture: valore del contatore in primo piano, foto nello stesso flusso operativo e salvataggio diretto su archivio letture acqua.</div>
</div>
<div class="rounded-full bg-sky-50 px-3 py-1 text-xs font-semibold text-sky-800">Protocollo bozza {{ $waterDraftProtocol }}</div>
</div>
</div>
<div class="grid gap-4 xl:grid-cols-[1.1fr_0.9fr]">
<div class="space-y-4">
<div class="rounded-2xl border bg-white p-4 shadow-sm">
<div class="grid gap-4 md:grid-cols-2">
<label class="block text-sm">
<span class="mb-1 block font-medium">Stabile</span>
<select wire:model.live="stabileId" class="w-full rounded-lg border-gray-300">
<option value="">Seleziona stabile</option>
@foreach($stabileOptions as $option)
<option value="{{ $option['id'] }}">{{ $option['label'] }}</option>
@endforeach
</select>
</label>
<label class="block text-sm">
<span class="mb-1 block font-medium">Servizio acqua</span>
<select wire:model.live="servizioId" class="w-full rounded-lg border-gray-300">
<option value="">Seleziona servizio</option>
@foreach($servizioOptions as $option)
<option value="{{ $option['id'] }}">{{ $option['label'] }}</option>
@endforeach
</select>
</label>
<label class="block text-sm md:col-span-2">
<span class="mb-1 block font-medium">Unità immobiliare</span>
<select wire:model.live="unitaId" class="w-full rounded-lg border-gray-300">
<option value="">Seleziona unità</option>
@foreach($unitaOptions as $option)
<option value="{{ $option['id'] }}">{{ $option['label'] }}</option>
@endforeach
</select>
</label>
</div>
</div>
<div class="rounded-2xl border border-sky-200 bg-sky-50 p-4 shadow-sm">
<label class="block text-sm">
<span class="mb-1 block text-sm font-semibold text-slate-900">Lettura attuale del contatore</span>
<input type="number" step="0.001" min="0" wire:model.defer="letturaAttuale" class="w-full rounded-lg border-sky-300 bg-white text-2xl font-semibold text-slate-900" placeholder="Es. 1284.350" />
</label>
<div class="mt-2 text-xs text-slate-600">Inserisci solo il valore letto. Se alleghi foto, il sistema mantiene preview, metadata e riferimenti dello scatto dentro la lettura acqua.</div>
</div>
<div class="rounded-2xl border bg-white p-4 shadow-sm">
<div class="grid gap-4 md:grid-cols-2">
<label class="block text-sm">
<span class="mb-1 block font-medium">Rilevatore</span>
<input type="text" wire:model.defer="rilevatoreNome" class="w-full rounded-lg border-gray-300" placeholder="Nome operatore o condomino" />
</label>
<label class="block text-sm">
<span class="mb-1 block font-medium">Contatto</span>
<input type="text" wire:model.defer="rilevatoreContatto" class="w-full rounded-lg border-gray-300" placeholder="Telefono o email" />
</label>
<label class="block text-sm md:col-span-2">
<span class="mb-1 block font-medium">Data lettura</span>
<input type="date" wire:model.defer="dataLettura" class="w-full rounded-lg border-gray-300" />
</label>
</div>
</div>
</div>
<div class="space-y-4">
<div class="rounded-2xl border bg-white p-4 shadow-sm">
<div class="text-sm font-semibold text-slate-900">Ultima lettura nota</div>
@if($previousReading)
<div class="mt-3 space-y-1 text-sm text-slate-700">
<div><span class="font-medium">Valore:</span> {{ number_format((float) $previousReading['value'], 3, ',', '.') }}</div>
<div><span class="font-medium">Data:</span> {{ $previousReading['date'] ?: 'n/d' }}</div>
<div><span class="font-medium">Riferimento:</span> #{{ $previousReading['id'] }}</div>
</div>
@else
<div class="mt-3 text-sm text-slate-500">Nessuna lettura precedente trovata per questa combinazione servizio/unità.</div>
@endif
</div>
<div class="rounded-2xl border bg-white p-4 shadow-sm">
<label class="block text-sm"
x-data="{
localWaterPreviews: [],
init() {
window.__ticketAcquaPreviewState = window.__ticketAcquaPreviewState || [];
this.localWaterPreviews = [...window.__ticketAcquaPreviewState];
},
persist() {
window.__ticketAcquaPreviewState = [...this.localWaterPreviews];
},
revoke(items) {
items.forEach((item) => {
if (item.url) {
URL.revokeObjectURL(item.url);
}
});
},
addFiles(event) {
const batchId = Date.now();
const mapped = Array.from(event?.target?.files || []).map((file, index) => ({
key: `${batchId}-${index}-${file.name}-${file.size}`,
name: file.name,
size: file.size || 0,
url: (file.type || '').startsWith('image/') ? URL.createObjectURL(file) : null,
}));
const existing = new Map(this.localWaterPreviews.map((item) => [item.key, item]));
mapped.forEach((item) => existing.set(item.key, item));
this.localWaterPreviews = Array.from(existing.values());
this.persist();
if (event?.target) {
event.target.value = null;
}
},
previewFor(name, size = 0) {
const exact = this.localWaterPreviews.find((item) => item.name === name && Number(item.size || 0) === Number(size || 0));
return exact?.url || null;
},
clearAll() {
this.revoke(this.localWaterPreviews);
this.localWaterPreviews = [];
this.persist();
},
}"
x-on:ticket-acqua-draft-reset.window="clearAll()"
>
<span class="mb-2 block font-medium">Foto contatore</span>
<label class="inline-flex cursor-pointer items-center rounded-md bg-emerald-600 px-3 py-2 text-xs font-medium text-white hover:bg-emerald-500">
Scatta o aggiungi foto
<input type="file" wire:model="pendingWaterPhotos" multiple accept="image/*" capture="environment" class="hidden" x-on:change="addFiles($event)" />
</label>
<div class="mt-1 text-xs text-gray-500">Puoi accodare fino a 4 immagini. Ogni foto resta nello stesso riquadro con miniatura e nota.</div>
<div wire:loading wire:target="pendingWaterPhotos" class="mt-2 text-xs font-medium text-amber-700">Caricamento foto in corso: preparo la coda della lettura acqua.</div>
@if(count($this->selectedWaterUploads) > 0)
<div class="mt-4 grid gap-3 sm:grid-cols-2">
@foreach($this->selectedWaterUploads as $upload)
<div class="rounded-xl border bg-slate-50 p-3 text-xs shadow-sm">
<div class="mb-2 aspect-[4/3] overflow-hidden rounded-lg border bg-white" x-show="Boolean(@js($upload['preview_url']) || previewFor(@js($upload['original_name']), @js($upload['size'] ?? 0)))">
<img x-bind:src="@js($upload['preview_url']) || previewFor(@js($upload['original_name']), @js($upload['size'] ?? 0))" alt="{{ $upload['name'] }}" class="h-full w-full object-cover" />
</div>
<div class="rounded-lg border bg-white px-3 py-2 text-[11px] text-slate-700">
<div class="font-medium">{{ $upload['name'] }}</div>
<div class="mt-1 text-emerald-700">Protocollo bozza: {{ $upload['protocol'] }}</div>
<div class="mt-1 text-slate-500">Origine: {{ $upload['original_name'] }}</div>
</div>
@php
$metadata = is_array($upload['metadata'] ?? null) ? $upload['metadata'] : [];
$gpsLat = data_get($metadata, 'gps_decimal.lat');
$gpsLng = data_get($metadata, 'gps_decimal.lng');
$hasGps = is_numeric($gpsLat) && is_numeric($gpsLng);
$exifDate = trim((string) data_get($metadata, 'exif_datetime', ''));
$device = trim((string) data_get($metadata, 'device', ''));
@endphp
@if($hasGps || $exifDate !== '' || $device !== '')
<div class="mt-2 rounded-lg border border-slate-200 bg-white p-2 text-[11px] text-slate-700">
@if($hasGps)
<div><span class="font-medium">Coordinate:</span> {{ number_format((float) $gpsLat, 5, '.', '') }}, {{ number_format((float) $gpsLng, 5, '.', '') }}</div>
@endif
@if($exifDate !== '')
<div><span class="font-medium">Data EXIF:</span> {{ $exifDate }}</div>
@endif
@if($device !== '')
<div><span class="font-medium">Dispositivo:</span> {{ $device }}</div>
@endif
</div>
@endif
<label class="mt-2 block">
<span class="mb-1 block text-[11px] font-medium">Nota sotto la foto</span>
<input type="text" wire:model.defer="waterPhotoDescriptions.{{ $upload['index'] }}" class="w-full rounded-lg border-gray-300" placeholder="Es. contatore cucina, cifra poco leggibile" />
</label>
<button type="button" wire:click="removeWaterPhoto({{ (int) $upload['index'] }})" class="mt-2 inline-flex items-center rounded-md bg-rose-600 px-2 py-1 text-[11px] font-medium text-white hover:bg-rose-500">Rimuovi</button>
</div>
@endforeach
</div>
@else
<div class="mt-3 rounded-xl border border-dashed border-slate-300 bg-slate-50 px-4 py-6 text-center text-sm text-slate-500">Nessuna foto in coda. Aggiungi una o due foto del contatore per avere subito lanteprima prima dellinvio.</div>
@endif
</label>
</div>
<div class="rounded-2xl border bg-white p-4 shadow-sm">
<label class="block text-sm">
<span class="mb-1 block font-medium">Note generali</span>
<textarea rows="3" wire:model.defer="noteGenerali" class="w-full rounded-lg border-gray-300" placeholder="Es. locale contatori chiuso, seconda foto con riflesso, lettura confermata sul posto..."></textarea>
</label>
<div class="mt-3">
<x-filament::button wire:click="registraLetturaAcqua" wire:loading.attr="disabled" wire:target="registraLetturaAcqua,pendingWaterPhotos">
<span wire:loading.remove wire:target="registraLetturaAcqua">Invia ticket acqua</span>
<span wire:loading wire:target="registraLetturaAcqua">Invio lettura...</span>
</x-filament::button>
</div>
</div>
</div>
</div>
</div>
</x-filament-panels::page>