196 lines
10 KiB
PHP
196 lines
10 KiB
PHP
<!DOCTYPE html>
|
|
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<meta name="csrf-token" content="{{ csrf_token() }}">
|
|
<title>Nuovo Ticket Mobile - NetGescon</title>
|
|
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
|
</head>
|
|
<body class="min-h-screen bg-slate-100 text-slate-900">
|
|
<div class="max-w-3xl mx-auto p-4 md:p-8">
|
|
<div class="mb-4">
|
|
<h1 class="text-xl md:text-2xl font-semibold">Apri ticket da cellulare</h1>
|
|
<p class="text-sm text-slate-600">Puoi allegare foto dalla fotocamera e file dal telefono o dal PC.</p>
|
|
</div>
|
|
|
|
@if ($errors->any())
|
|
<div class="mb-4 rounded-lg border border-red-300 bg-red-50 p-3 text-red-700 text-sm">
|
|
<ul class="list-disc pl-5">
|
|
@foreach ($errors->all() as $error)
|
|
<li>{{ $error }}</li>
|
|
@endforeach
|
|
</ul>
|
|
</div>
|
|
@endif
|
|
|
|
<form method="POST" action="{{ route('condomino.tickets.store') }}" enctype="multipart/form-data" class="space-y-4 rounded-lg bg-white p-4 md:p-6 shadow">
|
|
@csrf
|
|
|
|
<div>
|
|
<label for="unita_immobiliare_id" class="block text-sm font-medium">Unità immobiliare</label>
|
|
<select id="unita_immobiliare_id" name="unita_immobiliare_id" class="mt-1 block w-full rounded-md border-slate-300" required>
|
|
<option value="">Seleziona unità</option>
|
|
@foreach ($unitaImmobiliari as $unita)
|
|
@php $uid = (int) ($unita->id ?? $unita->id_unita ?? 0); @endphp
|
|
<option value="{{ $uid }}" {{ (int) old('unita_immobiliare_id') === $uid ? 'selected' : '' }}>
|
|
{{ $unita->stabile->denominazione ?? 'Stabile' }} - {{ $unita->denominazione ?? ('Unità '.$unita->id_unita) }}
|
|
</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label for="categoria_ticket_id" class="block text-sm font-medium">Categoria</label>
|
|
<select id="categoria_ticket_id" name="categoria_ticket_id" class="mt-1 block w-full rounded-md border-slate-300">
|
|
<option value="">Seleziona categoria</option>
|
|
@foreach ($categorieTicket as $categoria)
|
|
<option value="{{ $categoria->id }}" {{ old('categoria_ticket_id') == $categoria->id ? 'selected' : '' }}>{{ $categoria->nome }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label for="titolo" class="block text-sm font-medium">Titolo</label>
|
|
<input id="titolo" name="titolo" type="text" value="{{ old('titolo') }}" class="mt-1 block w-full rounded-md border-slate-300" required>
|
|
</div>
|
|
|
|
<div>
|
|
<label for="descrizione" class="block text-sm font-medium">Descrizione</label>
|
|
<textarea id="descrizione" name="descrizione" rows="4" class="mt-1 block w-full rounded-md border-slate-300" required>{{ old('descrizione') }}</textarea>
|
|
</div>
|
|
|
|
<div>
|
|
<label for="luogo_intervento" class="block text-sm font-medium">Luogo intervento</label>
|
|
<input id="luogo_intervento" name="luogo_intervento" type="text" value="{{ old('luogo_intervento') }}" class="mt-1 block w-full rounded-md border-slate-300">
|
|
</div>
|
|
|
|
<div>
|
|
<label for="priorita" class="block text-sm font-medium">Priorità</label>
|
|
<select id="priorita" name="priorita" class="mt-1 block w-full rounded-md border-slate-300" required>
|
|
@foreach (['Bassa', 'Media', 'Alta', 'Urgente'] as $p)
|
|
<option value="{{ $p }}" {{ old('priorita', 'Media') === $p ? 'selected' : '' }}>{{ $p }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
|
|
<div class="rounded-xl border border-slate-200 bg-slate-50 p-4">
|
|
<div class="flex flex-col gap-1 md:flex-row md:items-center md:justify-between">
|
|
<div>
|
|
<div class="text-sm font-medium">Allegati con anteprima e descrizione</div>
|
|
<p class="text-xs text-slate-500">Ogni file compare qui sotto in un box dedicato, così puoi descriverlo subito prima dell'invio.</p>
|
|
</div>
|
|
<div class="text-xs text-slate-500">Max 20MB per file</div>
|
|
</div>
|
|
|
|
<div class="mt-4 grid gap-4 md:grid-cols-2">
|
|
<div>
|
|
<label for="allegati_camera" class="block text-sm font-medium">Foto da fotocamera</label>
|
|
<input id="allegati_camera" name="allegati[]" type="file" multiple accept="image/*" capture="environment" class="mt-1 block w-full rounded-md border-slate-300">
|
|
<p class="mt-1 text-xs text-slate-500">Scatta foto direttamente da smartphone.</p>
|
|
</div>
|
|
|
|
<div>
|
|
<label for="allegati_file" class="block text-sm font-medium">File da telefono / PC</label>
|
|
<input id="allegati_file" name="allegati[]" type="file" multiple accept="image/*,application/pdf,.doc,.docx,.xls,.xlsx,.txt,.zip" class="mt-1 block w-full rounded-md border-slate-300">
|
|
<p class="mt-1 text-xs text-slate-500">Immagini, PDF e documenti. I metadati foto vengono letti in fase di salvataggio.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="ticket-attachment-preview-empty" class="mt-4 rounded-lg border border-dashed border-slate-300 bg-white px-4 py-5 text-center text-sm text-slate-500">
|
|
Nessun allegato selezionato.
|
|
</div>
|
|
<div id="ticket-attachment-preview-list" class="mt-4 grid gap-3 sm:grid-cols-2"></div>
|
|
</div>
|
|
|
|
<div class="flex gap-3 pt-2">
|
|
<button type="submit" class="inline-flex items-center rounded-lg bg-blue-600 px-4 py-2 text-white text-sm font-medium">Invia ticket</button>
|
|
<a href="{{ route('condomino.tickets.index') }}" class="inline-flex items-center rounded-lg border border-slate-300 px-4 py-2 text-sm">Annulla</a>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<script>
|
|
(() => {
|
|
const cameraInput = document.getElementById('allegati_camera');
|
|
const fileInput = document.getElementById('allegati_file');
|
|
const previewList = document.getElementById('ticket-attachment-preview-list');
|
|
const emptyState = document.getElementById('ticket-attachment-preview-empty');
|
|
|
|
if (!cameraInput || !fileInput || !previewList || !emptyState) {
|
|
return;
|
|
}
|
|
|
|
const formatBytes = (value) => {
|
|
const bytes = Number(value || 0);
|
|
if (!Number.isFinite(bytes) || bytes <= 0) {
|
|
return '0 KB';
|
|
}
|
|
|
|
if (bytes < 1024 * 1024) {
|
|
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
}
|
|
|
|
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
};
|
|
|
|
const currentFiles = () => ([
|
|
...Array.from(cameraInput.files || []),
|
|
...Array.from(fileInput.files || []),
|
|
]);
|
|
|
|
const renderPreviews = () => {
|
|
const files = currentFiles();
|
|
previewList.innerHTML = '';
|
|
emptyState.classList.toggle('hidden', files.length > 0);
|
|
|
|
files.forEach((file, index) => {
|
|
const card = document.createElement('div');
|
|
card.className = 'overflow-hidden rounded-xl border border-slate-200 bg-white shadow-sm';
|
|
|
|
const media = document.createElement(file.type.startsWith('image/') ? 'img' : 'div');
|
|
if (file.type.startsWith('image/')) {
|
|
media.className = 'h-40 w-full bg-slate-100 object-cover';
|
|
media.alt = file.name;
|
|
media.src = URL.createObjectURL(file);
|
|
} else {
|
|
media.className = 'flex h-40 w-full items-center justify-center bg-slate-100 text-sm font-medium text-slate-500';
|
|
media.textContent = file.name.split('.').pop()?.toUpperCase() || 'FILE';
|
|
}
|
|
|
|
const body = document.createElement('div');
|
|
body.className = 'space-y-3 p-3';
|
|
|
|
const title = document.createElement('div');
|
|
title.className = 'text-sm font-medium text-slate-900';
|
|
title.textContent = file.name;
|
|
|
|
const meta = document.createElement('div');
|
|
meta.className = 'text-xs text-slate-500';
|
|
meta.textContent = `${file.type || 'file generico'} · ${formatBytes(file.size)}`;
|
|
|
|
const label = document.createElement('label');
|
|
label.className = 'block text-xs font-medium text-slate-700';
|
|
label.textContent = 'Descrizione allegato';
|
|
|
|
const textarea = document.createElement('textarea');
|
|
textarea.name = 'attachment_descriptions[]';
|
|
textarea.rows = 3;
|
|
textarea.className = 'mt-1 block w-full rounded-md border-slate-300 text-sm';
|
|
textarea.placeholder = 'Es. perdita vicino al contatore, crepa sul soffitto, dettaglio del guasto';
|
|
|
|
label.appendChild(textarea);
|
|
body.append(title, meta, label);
|
|
card.append(media, body);
|
|
previewList.appendChild(card);
|
|
});
|
|
};
|
|
|
|
cameraInput.addEventListener('change', renderPreviews);
|
|
fileInput.addEventListener('change', renderPreviews);
|
|
renderPreviews();
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|