563 lines
32 KiB
PHP
563 lines
32 KiB
PHP
<!DOCTYPE html>
|
|
<html lang="it">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>NetGescon Convocazione & Votazione</title>
|
|
<meta name="csrf-token" content="{{ csrf_token() }}">
|
|
<!-- Tailwind CSS -->
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
<!-- Google Fonts -->
|
|
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet">
|
|
<!-- FontAwesome -->
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
|
|
<style>
|
|
body {
|
|
font-family: 'Outfit', sans-serif;
|
|
background: radial-gradient(circle at top, #1e293b 0%, #0f172a 100%);
|
|
}
|
|
.glass-card {
|
|
background: rgba(30, 41, 59, 0.7);
|
|
backdrop-filter: blur(16px);
|
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
|
}
|
|
</style>
|
|
</head>
|
|
<body class="min-h-screen text-slate-100 flex flex-col justify-between">
|
|
|
|
<!-- Header -->
|
|
<header class="glass-card sticky top-0 z-50 border-b border-slate-800 px-4 py-3 flex items-center justify-between">
|
|
<div class="flex items-center space-x-2">
|
|
<div class="w-8 h-8 bg-blue-600 rounded-lg flex items-center justify-center shadow-lg shadow-blue-500/20">
|
|
<i class="fas fa-building text-white text-sm"></i>
|
|
</div>
|
|
<div>
|
|
<h1 class="text-xs font-semibold tracking-wider text-blue-400 uppercase">NetGescon Mobile</h1>
|
|
<p class="text-sm font-bold truncate max-w-[200px]">{{ $assemblea->stabile->denominazione }}</p>
|
|
</div>
|
|
</div>
|
|
<div class="text-xxs px-2 py-1 rounded-full bg-slate-800 text-slate-400 font-semibold border border-slate-700">
|
|
Unit: {{ $convocazione->unitaImmobiliare->interno ?: $convocazione->unitaImmobiliare->codice_unita }}
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Main Content -->
|
|
<main class="flex-grow p-4 space-y-6">
|
|
|
|
<!-- Welcome Card -->
|
|
<div class="glass-card rounded-2xl p-5 shadow-xl space-y-4">
|
|
<div class="flex justify-between items-start">
|
|
<div>
|
|
<h2 class="text-lg font-bold text-white">Gentile {{ $convocazione->soggetto->nome_completo ?? 'Condomino' }}</h2>
|
|
<p class="text-xs text-slate-400">Sei collegato alla gestione digitale dell'assemblea.</p>
|
|
</div>
|
|
<span class="px-2 py-1 text-[10px] font-bold uppercase rounded bg-indigo-500/20 text-indigo-300 border border-indigo-500/30">
|
|
{{ $convocazione->ruolo === 'I' ? 'Inquilino' : 'Proprietario' }}
|
|
</span>
|
|
</div>
|
|
|
|
<!-- Receipt Confirmation Box -->
|
|
<div id="receipt-box" class="p-3.5 rounded-xl bg-slate-900/60 border border-slate-800 flex items-center justify-between gap-3">
|
|
<div class="flex items-center space-x-3">
|
|
<div id="receipt-icon" class="w-9 h-9 rounded-lg flex items-center justify-center {{ $convocazione->ricezione_confermata_at ? 'bg-emerald-500/20 text-emerald-400 border border-emerald-500/30' : 'bg-amber-500/20 text-amber-400 border border-amber-500/30' }}">
|
|
<i class="fas {{ $convocazione->ricezione_confermata_at ? 'fa-check-double' : 'fa-envelope-open' }}"></i>
|
|
</div>
|
|
<div>
|
|
<p class="text-xs font-semibold text-slate-300">Ricezione convocazione</p>
|
|
<p id="receipt-status" class="text-[10px] text-slate-400">
|
|
{{ $convocazione->ricezione_confermata_at ? 'Firma apposta il ' . $convocazione->ricezione_confermata_at->format('d/m/Y H:i') : 'Firma elettronica richiesta' }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
@if(!$convocazione->ricezione_confermata_at)
|
|
<button onclick="openSignatureModal()" id="receipt-btn" class="px-3 py-1.5 bg-blue-600 hover:bg-blue-500 text-white rounded-lg text-xs font-semibold shadow-lg shadow-blue-500/10 active:scale-95 transition-all">
|
|
Firma
|
|
</button>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ZIP Download Card -->
|
|
<div class="glass-card rounded-2xl p-5 shadow-xl space-y-3">
|
|
<h3 class="text-sm font-bold text-slate-300 uppercase tracking-wider flex items-center">
|
|
<i class="fas fa-file-archive mr-2 text-indigo-500"></i> Documenti Assembleari
|
|
</h3>
|
|
<p class="text-xs text-slate-400">Scarica la documentazione completa dell'assemblea (preventivi, bilanci, relazioni) in un unico file compresso.</p>
|
|
<a href="{{ route('public.assemblea.zip', $convocazione->token_accesso) }}" class="w-full flex items-center justify-center space-x-2 py-3 bg-indigo-600 hover:bg-indigo-500 text-white rounded-xl text-xs font-bold transition-all active:scale-95 shadow-lg shadow-indigo-500/10">
|
|
<i class="fas fa-download mr-1 text-sm"></i> Scarica Tutto (.ZIP)
|
|
</a>
|
|
</div>
|
|
|
|
<!-- Assembly Info Panel -->
|
|
<div class="glass-card rounded-2xl p-5 shadow-xl space-y-4">
|
|
<h3 class="text-sm font-bold text-slate-300 uppercase tracking-wider flex items-center"><i class="fas fa-calendar-day mr-2 text-blue-500"></i>Dettagli convocazione</h3>
|
|
|
|
<div class="grid grid-cols-2 gap-4 text-xs">
|
|
<div class="bg-slate-900/40 p-3 rounded-xl border border-slate-800/80">
|
|
<p class="text-slate-400 mb-1">1ª Convocazione</p>
|
|
<p class="font-bold text-white">{{ $assemblea->data_prima_convocazione ? $assemblea->data_prima_convocazione->format('d/m/Y H:i') : 'n/d' }}</p>
|
|
</div>
|
|
<div class="bg-slate-900/40 p-3 rounded-xl border border-slate-800/80">
|
|
<p class="text-slate-400 mb-1">2ª Convocazione</p>
|
|
<p class="font-bold text-white">{{ $assemblea->data_seconda_convocazione ? $assemblea->data_seconda_convocazione->format('d/m/Y H:i') : 'n/d' }}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bg-slate-900/40 p-3 rounded-xl border border-slate-800/80 text-xs">
|
|
<p class="text-slate-400 mb-1">Sede e Luogo</p>
|
|
<p class="font-semibold text-white flex items-center"><i class="fas fa-map-marker-alt mr-1.5 text-red-400"></i>{{ $assemblea->luogo ?: 'Presso i locali condominiali / Telematica' }}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Real-Time Voting Box -->
|
|
<div id="live-voting-panel" class="p-5 rounded-2xl bg-gradient-to-br from-indigo-900/50 to-purple-900/30 border border-indigo-500/20 shadow-2xl relative overflow-hidden hidden">
|
|
<!-- Pulsante decorativo pulsante -->
|
|
<div class="absolute -top-12 -right-12 w-28 h-28 bg-indigo-500/10 rounded-full blur-xl animate-pulse"></div>
|
|
|
|
<div class="flex items-center justify-between mb-4">
|
|
<div class="flex items-center space-x-2">
|
|
<span class="flex h-2.5 w-2.5 relative">
|
|
<span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-red-400 opacity-75"></span>
|
|
<span class="relative inline-flex rounded-full h-2.5 w-2.5 bg-red-500"></span>
|
|
</span>
|
|
<h3 class="text-sm font-bold tracking-wider text-indigo-300 uppercase">Votazione in corso</h3>
|
|
</div>
|
|
<span class="px-2 py-0.5 rounded-full bg-indigo-500/20 text-indigo-300 text-[10px] font-semibold border border-indigo-400/20">LIVE</span>
|
|
</div>
|
|
|
|
<div id="live-odg-details" class="space-y-4">
|
|
<div>
|
|
<h4 id="live-odg-title" class="text-md font-bold text-white leading-snug"></h4>
|
|
<p id="live-odg-desc" class="text-xs text-slate-300 mt-1.5 leading-relaxed"></p>
|
|
</div>
|
|
|
|
<!-- Bottoni di voto -->
|
|
<div class="space-y-3 pt-2">
|
|
<!-- Votazione propria -->
|
|
<div id="own-vote-section" class="space-y-2">
|
|
<p class="text-[11px] font-semibold text-slate-400 uppercase tracking-wider">Il tuo voto (Int. {{ $convocazione->unitaImmobiliare->interno }})</p>
|
|
<div class="grid grid-cols-3 gap-2">
|
|
<button onclick="submitVote('favorevole')" class="py-2.5 rounded-xl border border-emerald-500/20 bg-emerald-500/10 hover:bg-emerald-500/20 text-emerald-400 text-xs font-bold active:scale-95 transition-all flex flex-col items-center justify-center gap-1">
|
|
<i class="fas fa-thumbs-up"></i> Favorevole
|
|
</button>
|
|
<button onclick="submitVote('contrario')" class="py-2.5 rounded-xl border border-rose-500/20 bg-rose-500/10 hover:bg-rose-500/20 text-rose-400 text-xs font-bold active:scale-95 transition-all flex flex-col items-center justify-center gap-1">
|
|
<i class="fas fa-thumbs-down"></i> Contrario
|
|
</button>
|
|
<button onclick="submitVote('astenuto')" class="py-2.5 rounded-xl border border-slate-500/20 bg-slate-500/10 hover:bg-slate-500/20 text-slate-400 text-xs font-bold active:scale-95 transition-all flex flex-col items-center justify-center gap-1">
|
|
<i class="fas fa-ban"></i> Astenuto
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sezione deleghe -->
|
|
@if($deleghe->isNotEmpty())
|
|
<div id="deleghe-vote-section" class="mt-4 pt-3 border-t border-slate-800 space-y-3">
|
|
<p class="text-[11px] font-semibold text-slate-400 uppercase tracking-wider">Voti delegati in delega</p>
|
|
@foreach($deleghe as $delega)
|
|
<div class="p-2.5 rounded-xl bg-slate-900/60 border border-slate-800 space-y-2">
|
|
<div class="flex justify-between items-center text-[10px] text-slate-300">
|
|
<span>Int. {{ $delega->unitaImmobiliare->interno }} - {{ $delega->soggetto->nome_completo }}</span>
|
|
<span class="text-indigo-400 font-semibold">Delega</span>
|
|
</div>
|
|
<div class="grid grid-cols-3 gap-2">
|
|
<button onclick="submitVote('favorevole', {{ $delega->unita_immobiliare_id }}, {{ $delega->soggetto_id }}, this)" class="py-1.5 rounded-lg border border-emerald-500/20 bg-emerald-500/10 text-emerald-400 text-[11px] font-semibold active:scale-95 transition-all">
|
|
Favorevole
|
|
</button>
|
|
<button onclick="submitVote('contrario', {{ $delega->unita_immobiliare_id }}, {{ $delega->soggetto_id }}, this)" class="py-1.5 rounded-lg border border-rose-500/20 bg-rose-500/10 text-rose-400 text-[11px] font-semibold active:scale-95 transition-all">
|
|
Contrario
|
|
</button>
|
|
<button onclick="submitVote('astenuto', {{ $delega->unita_immobiliare_id }}, {{ $delega->soggetto_id }}, this)" class="py-1.5 rounded-lg border border-slate-500/20 bg-slate-500/10 text-slate-400 text-[11px] font-semibold active:scale-95 transition-all">
|
|
Astenuto
|
|
</button>
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Ordine del giorno & Allegati -->
|
|
<div class="glass-card rounded-2xl p-5 shadow-xl space-y-4">
|
|
<h3 class="text-sm font-bold text-slate-300 uppercase tracking-wider flex items-center"><i class="fas fa-list-ol mr-2 text-blue-500"></i>Ordine del Giorno (OdG)</h3>
|
|
|
|
<div class="space-y-3">
|
|
@foreach($odg as $punto)
|
|
<div class="p-3.5 rounded-xl bg-slate-900/40 border border-slate-800 space-y-2">
|
|
<div class="flex items-start justify-between gap-3">
|
|
<div class="flex items-start space-x-2">
|
|
<span class="w-5 h-5 rounded-full bg-slate-800 flex items-center justify-center text-xxs font-bold text-slate-300 mt-0.5">
|
|
{{ $punto->numero_punto }}
|
|
</span>
|
|
<div>
|
|
<h4 class="text-xs font-bold text-white leading-snug">{{ $punto->titolo }}</h4>
|
|
<p class="text-xxs text-slate-400 mt-1 leading-relaxed">{{ $punto->descrizione }}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Allegati del Punto -->
|
|
@if($punto->allegati && count($punto->allegati) > 0)
|
|
<div class="mt-2 pt-2 border-t border-slate-800/50 space-y-1.5 text-xxs">
|
|
<p class="font-bold text-slate-400">Allegati:</p>
|
|
<div class="flex flex-col gap-1.5">
|
|
@foreach($punto->allegati as $file)
|
|
<a href="{{ $file['url'] }}" target="_blank" class="flex items-center space-x-1.5 text-blue-400 hover:underline">
|
|
<i class="fas fa-file-pdf text-red-500"></i>
|
|
<span>{{ $file['nome'] }}</span>
|
|
</a>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
<div class="flex flex-wrap items-center justify-between gap-2 pt-2 border-t border-slate-800/80 text-[10px]">
|
|
@if($punto->articolo_legge)
|
|
<span class="text-blue-400"><i class="fas fa-gavel mr-1"></i>Rif: {{ $punto->articolo_legge }}</span>
|
|
@endif
|
|
@if($punto->maggioranza_richiesta)
|
|
<span class="text-amber-400"><i class="fas fa-balance-scale mr-1"></i>Maggioranza: {{ $punto->maggioranza_richiesta }}</span>
|
|
@endif
|
|
@if($punto->tabellaMillesimale)
|
|
<span class="text-slate-400"><i class="fas fa-calculator mr-1"></i>Tab: {{ $punto->tabellaMillesimale->denominazione }}</span>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Profile Update Card -->
|
|
<div class="glass-card rounded-2xl p-5 shadow-xl space-y-4">
|
|
<h3 class="text-sm font-bold text-slate-300 uppercase tracking-wider flex items-center">
|
|
<i class="fas fa-user-edit mr-2 text-blue-500"></i> Variazione Recapiti / Catasto
|
|
</h3>
|
|
<p class="text-xs text-slate-400">Verifica i tuoi recapiti di contatto. Invia eventuali rettifiche per l'aggiornamento dell'anagrafica condominiale.</p>
|
|
|
|
<form id="anagrafica-form" onsubmit="submitAnagrafica(event)" class="space-y-3">
|
|
<div>
|
|
<label class="block text-[10px] text-slate-400 uppercase font-bold mb-1">Email di contatto</label>
|
|
<input type="email" id="profile-email" value="{{ $convocazione->soggetto->email }}" class="w-full bg-slate-900/60 border border-slate-700 rounded-lg p-2.5 text-xs text-slate-100 focus:outline-none focus:border-blue-500">
|
|
</div>
|
|
<div>
|
|
<label class="block text-[10px] text-slate-400 uppercase font-bold mb-1">P.E.C. (Posta Elettronica Certificata)</label>
|
|
<input type="email" id="profile-pec" value="{{ $convocazione->soggetto->pec }}" class="w-full bg-slate-900/60 border border-slate-700 rounded-lg p-2.5 text-xs text-slate-100 focus:outline-none focus:border-blue-500">
|
|
</div>
|
|
<div>
|
|
<label class="block text-[10px] text-slate-400 uppercase font-bold mb-1">Telefono / Cellulare</label>
|
|
<input type="text" id="profile-telefono" value="{{ $convocazione->soggetto->telefono }}" class="w-full bg-slate-900/60 border border-slate-700 rounded-lg p-2.5 text-xs text-slate-100 focus:outline-none focus:border-blue-500">
|
|
</div>
|
|
<div>
|
|
<label class="block text-[10px] text-slate-400 uppercase font-bold mb-1">Indirizzo di Residenza</label>
|
|
<input type="text" id="profile-indirizzo" value="{{ $convocazione->soggetto->indirizzo_residenza ?? $convocazione->soggetto->indirizzo }}" class="w-full bg-slate-900/60 border border-slate-700 rounded-lg p-2.5 text-xs text-slate-100 focus:outline-none focus:border-blue-500">
|
|
</div>
|
|
|
|
<!-- Cadastral data -->
|
|
<div class="p-3 bg-slate-900/40 rounded-xl border border-slate-800 text-[10px] text-slate-400 space-y-1">
|
|
<p class="font-bold text-slate-300">Dati Catastali Unità:</p>
|
|
<p>Foglio: {{ $convocazione->unitaImmobiliare->foglio ?: '-' }} | Particella: {{ $convocazione->unitaImmobiliare->particella ?: '-' }} | Sub: {{ $convocazione->unitaImmobiliare->subalterno ?: '-' }}</p>
|
|
<p class="text-[9px] text-indigo-400 italic">Presto potrai aggiornare automaticamente l'anagrafica acquistando una visura in tempo reale.</p>
|
|
</div>
|
|
|
|
<button type="submit" class="w-full py-2.5 bg-blue-600 hover:bg-blue-500 text-white rounded-xl text-xs font-bold transition-all active:scale-95 shadow-md">
|
|
Invia Richiesta Variazione
|
|
</button>
|
|
</form>
|
|
</div>
|
|
|
|
</main>
|
|
|
|
<!-- Toast Notification -->
|
|
<div id="toast" class="fixed bottom-4 left-4 right-4 p-4 rounded-xl bg-slate-900 border border-slate-800 shadow-2xl flex items-center justify-between text-xs transition-all transform translate-y-24 opacity-0 z-50">
|
|
<span id="toast-message" class="text-slate-300"></span>
|
|
<button onclick="hideToast()" class="text-slate-400 hover:text-white"><i class="fas fa-times"></i></button>
|
|
</div>
|
|
|
|
<!-- Footer -->
|
|
<footer class="text-center py-4 text-xxs text-slate-500 border-t border-slate-800 glass-card">
|
|
NetGescon © 2026 - Piattaforma Gestione Assemblee Condominiali
|
|
</footer>
|
|
|
|
<!-- Modal Firma Convocazione -->
|
|
<div id="modal-signature" class="fixed inset-0 z-50 overflow-y-auto hidden bg-slate-950/80 backdrop-blur-sm flex items-center justify-center p-4">
|
|
<div class="bg-slate-900 border border-slate-800 rounded-2xl max-w-md w-full p-6 shadow-2xl space-y-4">
|
|
<div class="flex justify-between items-center border-b border-slate-800 pb-2">
|
|
<h3 class="text-sm font-bold text-white uppercase tracking-wider">Firma Convocazione</h3>
|
|
<button onclick="closeSignatureModal()" class="text-slate-400 hover:text-white"><i class="fas fa-times"></i></button>
|
|
</div>
|
|
|
|
<p class="text-xs text-slate-400 leading-relaxed">
|
|
Disegna la tua firma all'interno del box sottostante per confermare la ricezione della convocazione. I dati verranno registrati con IP e marca temporale.
|
|
</p>
|
|
|
|
<div class="relative w-full h-36 bg-slate-950 rounded-xl border border-slate-800 overflow-hidden">
|
|
<canvas id="signature-canvas" class="w-full h-full cursor-crosshair"></canvas>
|
|
</div>
|
|
|
|
<div class="flex space-x-2">
|
|
<button onclick="clearCanvas()" class="flex-1 py-2 border border-slate-800 hover:bg-slate-800 text-slate-400 rounded-xl text-xs font-semibold">
|
|
Pulisci
|
|
</button>
|
|
<button onclick="saveSignature()" class="flex-1 py-2 bg-blue-600 hover:bg-blue-500 text-white rounded-xl text-xs font-bold shadow-md shadow-blue-500/10">
|
|
Conferma Firma
|
|
</button>
|
|
</div>
|
|
|
|
<!-- CIE Mock Validation Box -->
|
|
<div class="pt-3 border-t border-slate-800 space-y-2">
|
|
<p class="text-[10px] text-slate-400 text-center font-semibold">Certificazione con Identità Digitale (Opzionale)</p>
|
|
<button onclick="validateCie()" id="cie-btn" class="w-full flex items-center justify-center gap-2 py-2 bg-indigo-600 hover:bg-indigo-500 text-white rounded-xl text-xs font-bold transition-all shadow-md shadow-indigo-500/10">
|
|
<i class="fas fa-id-card"></i> Valida con CIE / SPID
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Polling & Event Javascript -->
|
|
<script>
|
|
const token = "{{ $convocazione->token_accesso }}";
|
|
let activeOdgId = null;
|
|
|
|
function showToast(message, isSuccess = true) {
|
|
const toast = document.getElementById('toast');
|
|
const msgEl = document.getElementById('toast-message');
|
|
msgEl.textContent = message;
|
|
if (isSuccess) {
|
|
toast.classList.remove('border-red-500/20');
|
|
toast.classList.add('border-emerald-500/20');
|
|
} else {
|
|
toast.classList.remove('border-emerald-500/20');
|
|
toast.classList.add('border-red-500/20');
|
|
}
|
|
toast.classList.remove('translate-y-24', 'opacity-0');
|
|
setTimeout(hideToast, 4000);
|
|
}
|
|
|
|
function hideToast() {
|
|
document.getElementById('toast').classList.add('translate-y-24', 'opacity-0');
|
|
}
|
|
|
|
// Canvas signature logic
|
|
const canvas = document.getElementById('signature-canvas');
|
|
const ctx = canvas.getContext('2d');
|
|
let drawing = false;
|
|
|
|
function startDrawing(e) {
|
|
drawing = true;
|
|
draw(e);
|
|
}
|
|
function stopDrawing() {
|
|
drawing = false;
|
|
ctx.beginPath();
|
|
}
|
|
function draw(e) {
|
|
if (!drawing) return;
|
|
ctx.lineWidth = 2;
|
|
ctx.lineCap = 'round';
|
|
ctx.strokeStyle = '#60a5fa'; // Blue-400
|
|
|
|
let clientX = e.clientX || (e.touches && e.touches[0].clientX);
|
|
let clientY = e.clientY || (e.touches && e.touches[0].clientY);
|
|
|
|
const rect = canvas.getBoundingClientRect();
|
|
const x = clientX - rect.left;
|
|
const y = clientY - rect.top;
|
|
|
|
ctx.lineTo(x, y);
|
|
ctx.stroke();
|
|
ctx.beginPath();
|
|
ctx.moveTo(x, y);
|
|
}
|
|
|
|
canvas.addEventListener('mousedown', startDrawing);
|
|
canvas.addEventListener('mouseup', stopDrawing);
|
|
canvas.addEventListener('mousemove', draw);
|
|
canvas.addEventListener('touchstart', startDrawing);
|
|
canvas.addEventListener('touchend', stopDrawing);
|
|
canvas.addEventListener('touchmove', draw);
|
|
|
|
function clearCanvas() {
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
}
|
|
|
|
function openSignatureModal() {
|
|
document.getElementById('modal-signature').classList.remove('hidden');
|
|
// Resize canvas to match container size
|
|
canvas.width = canvas.parentElement.clientWidth;
|
|
canvas.height = canvas.parentElement.clientHeight;
|
|
}
|
|
|
|
function closeSignatureModal() {
|
|
document.getElementById('modal-signature').classList.add('hidden');
|
|
}
|
|
|
|
function validateCie() {
|
|
const cieBtn = document.getElementById('cie-btn');
|
|
cieBtn.disabled = true;
|
|
cieBtn.innerHTML = '<i class="fas fa-spinner animate-spin"></i> Validazione CIE in corso...';
|
|
|
|
setTimeout(() => {
|
|
cieBtn.className = 'w-full flex items-center justify-center gap-2 py-2 bg-emerald-600 text-white rounded-xl text-xs font-bold';
|
|
cieBtn.innerHTML = '<i class="fas fa-check-circle"></i> CIE Validata con Successo!';
|
|
showToast('Identità digitale certificata tramite CIE!');
|
|
// Auto-fill canvas signature as simulated confirmation
|
|
ctx.fillStyle = '#60a5fa';
|
|
ctx.font = '20px Outfit';
|
|
ctx.fillText('VALIDATO TRAMITE CIE', 20, 50);
|
|
}, 2000);
|
|
}
|
|
|
|
function saveSignature() {
|
|
const dataUrl = canvas.toDataURL();
|
|
|
|
fetch(`/public/assemblea/${token}/ricezione`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
|
|
},
|
|
body: JSON.stringify({
|
|
firma_dati: dataUrl
|
|
})
|
|
})
|
|
.then(res => res.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
showToast('Firma registrata con successo!');
|
|
closeSignatureModal();
|
|
const btn = document.getElementById('receipt-btn');
|
|
if (btn) btn.remove();
|
|
document.getElementById('receipt-status').textContent = 'Firma apposta il ' + data.ricezione_confermata_at;
|
|
document.getElementById('receipt-icon').className = 'w-9 h-9 rounded-lg flex items-center justify-center bg-emerald-500/20 text-emerald-400 border border-emerald-500/30';
|
|
document.getElementById('receipt-icon').innerHTML = '<i class="fas fa-check-double"></i>';
|
|
} else {
|
|
showToast(data.message || 'Errore durante la firma.', false);
|
|
}
|
|
})
|
|
.catch(err => {
|
|
console.error(err);
|
|
showToast('Errore di rete. Riprova.', false);
|
|
});
|
|
}
|
|
|
|
function submitAnagrafica(e) {
|
|
e.preventDefault();
|
|
const email = document.getElementById('profile-email').value;
|
|
const pec = document.getElementById('profile-pec').value;
|
|
const telefono = document.getElementById('profile-telefono').value;
|
|
const indirizzo = document.getElementById('profile-indirizzo').value;
|
|
|
|
fetch(`/public/assemblea/${token}/anagrafica`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
|
|
},
|
|
body: JSON.stringify({ email, pec, telefono, indirizzo })
|
|
})
|
|
.then(res => res.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
showToast(data.message);
|
|
} else {
|
|
showToast('Errore durante l\'invio recapiti.', false);
|
|
}
|
|
})
|
|
.catch(err => {
|
|
console.error(err);
|
|
showToast('Errore di rete.', false);
|
|
});
|
|
}
|
|
|
|
function submitVote(voteStr, unitaId = null, soggettoId = null, btnEl = null) {
|
|
if (btnEl) {
|
|
// Disable sister buttons for the same delegation
|
|
const container = btnEl.closest('div');
|
|
container.querySelectorAll('button').forEach(b => {
|
|
b.classList.remove('bg-emerald-500/30', 'bg-rose-500/30', 'bg-slate-500/30', 'border-emerald-500/40', 'border-rose-500/40', 'border-slate-500/40');
|
|
});
|
|
}
|
|
|
|
fetch(`/public/assemblea/${token}/vota`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
|
|
},
|
|
body: JSON.stringify({
|
|
ordine_giorno_id: activeOdgId,
|
|
voto: voteStr,
|
|
unita_immobiliare_id: unitaId,
|
|
soggetto_id: soggettoId
|
|
})
|
|
})
|
|
.then(res => res.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
showToast('Voto inviato con successo!');
|
|
if (btnEl) {
|
|
// Highlight selected button
|
|
let colorClass = voteStr === 'favorevole' ? 'bg-emerald-500/30 border-emerald-500/40' : (voteStr === 'contrario' ? 'bg-rose-500/30 border-rose-500/40' : 'bg-slate-500/30 border-slate-500/40');
|
|
btnEl.className.split(' ').forEach(c => {
|
|
if (c.startsWith('bg-') || c.startsWith('border-')) {
|
|
btnEl.classList.remove(c);
|
|
}
|
|
});
|
|
colorClass.split(' ').forEach(c => btnEl.classList.add(c));
|
|
} else {
|
|
// Highlight own vote section
|
|
const container = document.getElementById('own-vote-section');
|
|
container.querySelectorAll('button').forEach(b => {
|
|
b.classList.remove('bg-emerald-500/30', 'bg-rose-500/30', 'bg-slate-500/30');
|
|
if (b.onclick.toString().includes(voteStr)) {
|
|
let bg = voteStr === 'favorevole' ? 'bg-emerald-500/30' : (voteStr === 'contrario' ? 'bg-rose-500/30' : 'bg-slate-500/30');
|
|
b.classList.add(bg);
|
|
}
|
|
});
|
|
}
|
|
} else {
|
|
showToast(data.message || 'Errore nell\'invio del voto.', false);
|
|
}
|
|
})
|
|
.catch(err => {
|
|
console.error(err);
|
|
showToast('Errore di rete. Riprova.', false);
|
|
});
|
|
}
|
|
|
|
function pollActiveVoting() {
|
|
fetch(`/public/assemblea/${token}/poll`)
|
|
.then(res => res.json())
|
|
.then(data => {
|
|
const livePanel = document.getElementById('live-voting-panel');
|
|
if (data.attivo_odg_id) {
|
|
if (activeOdgId !== data.attivo_odg_id) {
|
|
activeOdgId = data.attivo_odg_id;
|
|
document.getElementById('live-odg-title').textContent = `${data.punto.numero}. ${data.punto.titolo}`;
|
|
document.getElementById('live-odg-desc').textContent = data.punto.descrizione;
|
|
|
|
// Reset selections in buttons
|
|
document.querySelectorAll('#live-voting-panel button').forEach(b => {
|
|
b.classList.remove('bg-emerald-500/30', 'bg-rose-500/30', 'bg-slate-500/30', 'border-emerald-500/40', 'border-rose-500/40', 'border-slate-500/40');
|
|
});
|
|
|
|
livePanel.classList.remove('hidden');
|
|
showToast('Nuova votazione aperta!');
|
|
}
|
|
} else {
|
|
activeOdgId = null;
|
|
livePanel.classList.add('hidden');
|
|
}
|
|
})
|
|
.catch(err => console.error('Polling error:', err));
|
|
}
|
|
|
|
// Avvia polling ogni 3 secondi
|
|
setInterval(pollActiveVoting, 3000);
|
|
pollActiveVoting();
|
|
</script>
|
|
</body>
|
|
</html>
|