|
- <!DOCTYPE html>
- <html lang="it">
- <head>
- <meta charset="UTF-8" />
- <title>Consiglio Comunale</title>
- <style>
- body { font-family: Arial, sans-serif; background: #f0f2f5; padding: 20px; }
- .container { background: #fff; padding: 30px; border-radius: 12px; box-shadow: 0 4px 8px rgba(0,0,0,0.1); max-width: 1000px; margin: auto; }
- h1 { text-align: center; color: #333; }
- .input-group { display: flex; justify-content: center; align-items: center; margin-bottom: 20px; flex-wrap: wrap; }
- label { font-size: 18px; margin-right: 10px; }
- input[type="number"], input[type="file"], input[type="text"] { width: 250px; padding: 12px; font-size: 18px; border: 1px solid #ccc; border-radius: 8px; margin: 5px; }
- button { padding: 12px 24px; font-size: 16px; border: none; border-radius: 8px; cursor: pointer; margin: 5px; }
- #confirm { background-color: #28a745; color: white; }
- #start { background-color: #007bff; color: white; }
- #configure { background-color: #17a2b8; color: white; }
- #mapConfig { background-color: #fd7e14; color: white; }
- #stop { background-color: #dc3545; color: white; }
- #exportLog, #clearLog, #exportXML, #importXML { background-color: #6c757d; color: white; }
- #buttons, #config-buttons { margin-top: 20px; text-align: center; }
- #buttons button { background-color: #6f42c1; color: white; margin: 5px; position: relative; }
- #currentSpeaker { margin-top: 20px; font-size: 24px; font-weight: bold; border: 2px solid #007bff; padding: 20px; height: 100px; background: #e9ecef; display: flex; align-items: center; justify-content: center; border-radius: 8px; }
- #mapContainer { position: relative; width: 800px; height: 600px; background-image: url('mappa.jpg'); background-size: cover; background-position: center; border: 2px solid #ccc; margin: 20px auto; }
- .map-button { position: absolute; padding: 10px 15px; background-color: #007bff; color: white; border: none; border-radius: 8px; user-select: none; }
- .hidden { display: none; }
- #saveMapBtn { display: block; margin: 20px auto; }
- button.clicked-feedback { animation: buttonFlash 0.3s ease-in-out; }
- @keyframes buttonFlash {
- 0% { transform: scale(1); filter: brightness(1); }
- 50% { transform: scale(1.1); filter: brightness(1.5); }
- 100% { transform: scale(1); filter: brightness(1); }
- }
- </style>
- </head>
- <body>
- <div class="container">
- <h1>Consiglio Comunale</h1>
-
- <div class="input-group">
- <label for="numPartecipanti">Numero partecipanti:</label>
- <input type="number" id="numPartecipanti" min="1" />
- <button id="confirm">Conferma</button>
- <button id="importXML">Importa Partecipanti (XML)</button>
- <input type="file" id="fileInput" accept=".xml" style="display:none">
- </div>
-
- <div class="input-group hidden" id="sessionControls">
- <button id="start">Avvia</button>
- <button id="configure">Assegna nomi</button>
- <button id="mapConfig">Configura mappa</button>
- <button id="stop" class="hidden">Termina</button>
- <button id="exportLog">Esporta Log</button>
- <button id="clearLog">Pulisci Log</button>
- <button id="exportXML" class="hidden">Esporta Elenco Partecipanti (XML)</button>
- </div>
-
- <div id="buttons" class="hidden"></div>
- <div id="config-buttons" class="hidden"></div>
- <div id="mapContainer" class="hidden"></div>
- <button id="saveMapBtn" class="hidden">Salva Mappa</button>
-
- <h2 style="text-align: center;">Chi sta parlando:</h2>
- <div id="currentSpeaker">Nessuno</div>
-
- <script>
- let buttonLabels = [];
- let buttonCoords = [];
- let sessionLog = '';
- let sessionStart = null;
-
- let sessionState = {
- active: false,
- startTime: null,
- currentSpeaker: 'Nessuno',
- mapVisible: false
- };
-
- const exportXMLBtn = document.getElementById('exportXML');
- const saveBtn = document.getElementById('saveMapBtn');
- const map = document.getElementById('mapContainer');
-
- function giveButtonFeedback(btn) {
- btn.classList.add('clicked-feedback');
- setTimeout(() => btn.classList.remove('clicked-feedback'), 300);
- }
-
- function applyButtonFeedback() {
- document.querySelectorAll('button').forEach(btn => {
- btn.addEventListener('click', () => giveButtonFeedback(btn));
- });
- }
-
- function saveSessionLog() {
- localStorage.setItem('sessionLog', sessionLog);
- }
-
- function saveSessionData() {
- localStorage.setItem('buttonLabels', JSON.stringify(buttonLabels));
- localStorage.setItem('buttonCoords', JSON.stringify(buttonCoords));
- localStorage.setItem('sessionState', JSON.stringify(sessionState));
- }
-
- function logSpeech(index) {
- const now = new Date();
- const time = sessionStart ? new Date(now - sessionStart).toISOString().substr(11, 8) : now.toLocaleTimeString();
- const entry = `[${time}] Parla: ${buttonLabels[index]}\n`;
- sessionLog += entry;
- sessionState.currentSpeaker = buttonLabels[index];
- saveSessionLog();
- saveSessionData();
- document.getElementById('currentSpeaker').textContent = buttonLabels[index];
- }
-
- function createButtons() {
- const div = document.getElementById('buttons');
- div.innerHTML = '<hr style="margin: 20px 0;">';
- buttonLabels.forEach((name, i) => {
- const btn = document.createElement('button');
- btn.textContent = name;
- btn.addEventListener('click', () => logSpeech(i));
- div.appendChild(btn);
- });
- div.classList.remove('hidden');
- applyButtonFeedback();
- saveSessionData();
- }
-
- function renderMapConfig() {
- if (buttonLabels.length === 0) return;
-
- map.innerHTML = '';
- map.classList.remove('hidden');
- saveBtn.classList.remove('hidden');
- saveBtn.style.display = 'block';
-
- const cols = Math.ceil(Math.sqrt(buttonLabels.length));
- const spacingX = map.clientWidth / (cols + 1);
- const spacingY = map.clientHeight / (cols + 1);
-
- buttonLabels.forEach((name, i) => {
- const x = buttonCoords[i].x;
- const y = buttonCoords[i].y;
- const b = document.createElement('button');
- b.textContent = name;
- b.className = 'map-button';
- b.style.left = `${x}px`;
- b.style.top = `${y}px`;
- b.setAttribute('data-index', i);
- b.onmousedown = startDrag;
- map.appendChild(b);
- });
- }
-
- function renderMapView() {
- if (buttonLabels.length === 0) return;
-
- map.innerHTML = '';
- map.classList.remove('hidden');
- saveBtn.classList.add('hidden');
-
- buttonLabels.forEach((name, i) => {
- const b = document.createElement('button');
- b.textContent = name;
- b.className = 'map-button';
- b.style.left = `${buttonCoords[i].x}px`;
- b.style.top = `${buttonCoords[i].y}px`;
- b.style.cursor = 'pointer';
- b.onclick = () => {
- giveButtonFeedback(b);
- logSpeech(i);
- };
- map.appendChild(b);
- });
- }
- document.getElementById('confirm').addEventListener('click', () => {
- const n = parseInt(document.getElementById('numPartecipanti').value);
- if (!n || n < 1) return alert('Inserisci un numero valido');
- const gruppi = ['Centro', 'Sinistra', 'Destra'];
- buttonLabels = Array.from({ length: n }, (_, i) => {
- const gruppo = gruppi[i % gruppi.length];
- const numero = Math.floor(i / gruppi.length) + 1;
- return `${gruppo} ${numero}`;
- });
- buttonCoords = Array.from({ length: n }, () => ({ x: 0, y: 0 }));
- createButtons();
- document.getElementById('sessionControls').classList.remove('hidden');
- });
-
- document.getElementById('start').addEventListener('click', () => {
- if (!sessionStart) {
- sessionStart = new Date();
- sessionState.active = true;
- sessionState.startTime = sessionStart.getTime();
- sessionLog += `[00:00:00] Sessione avviata (${sessionStart.toLocaleString('it-IT')})\n`;
- saveSessionLog();
- saveSessionData();
- document.getElementById('stop').classList.remove('hidden');
- document.getElementById('start').classList.add('hidden');
- document.getElementById('configure').classList.add('hidden');
- }
- });
-
- document.getElementById('stop').addEventListener('click', () => {
- if (sessionStart) {
- const end = new Date();
- const duration = new Date(end - sessionStart).toISOString().substr(11, 8);
- sessionLog += `[${duration}] Sessione terminata (${end.toLocaleString('it-IT')})\n`;
- sessionState.active = false;
- sessionState.startTime = null;
- saveSessionLog();
- saveSessionData();
- document.getElementById('stop').classList.add('hidden');
- document.getElementById('start').classList.remove('hidden');
- document.getElementById('configure').classList.remove('hidden');
- }
- });
- document.getElementById('configure').addEventListener('click', () => {
- const configDiv = document.getElementById('config-buttons');
- configDiv.innerHTML = '';
- saveBtn.classList.add('hidden');
-
- buttonLabels.forEach((name, i) => {
- const input = document.createElement('input');
- input.type = 'text';
- input.value = name;
- input.dataset.index = i;
- input.style.margin = '5px';
- input.style.padding = '8px';
- configDiv.appendChild(input);
- });
-
- const saveBtnNames = document.createElement('button');
- saveBtnNames.textContent = 'Salva Nomi';
- saveBtnNames.style.backgroundColor = '#28a745';
- saveBtnNames.style.color = 'white';
- saveBtnNames.style.marginTop = '20px';
- saveBtnNames.style.display = 'block';
- saveBtnNames.onclick = () => {
- configDiv.querySelectorAll('input').forEach(input => {
- const idx = parseInt(input.dataset.index);
- buttonLabels[idx] = input.value;
- });
- saveSessionData();
- configDiv.classList.add('hidden');
- createButtons();
- };
-
- configDiv.appendChild(saveBtnNames);
- configDiv.classList.remove('hidden');
- });
-
-
- document.getElementById('clearLog').addEventListener('click', () => {
- if (confirm('Sei sicuro di voler cancellare il log e i dati?')) {
- localStorage.clear();
- location.reload();
- }
- });
-
- document.getElementById('exportLog').addEventListener('click', () => {
- const blob = new Blob([sessionLog], { type: 'text/plain' });
- const link = document.createElement('a');
- link.href = URL.createObjectURL(blob);
- link.download = `log_consiglio_${new Date().toLocaleDateString('it-IT').replace(/\//g, '-')}.txt`;
- link.click();
- });
-
- exportXMLBtn.addEventListener('click', () => {
- let xml = '<?xml version="1.0" encoding="UTF-8"?><participants>';
- buttonLabels.forEach((name, i) => {
- const { x, y } = buttonCoords[i] || { x: 0, y: 0 };
- xml += `<participant><name>${name}</name><x>${x}</x><y>${y}</y></participant>`;
- });
- xml += '</participants>';
- const blob = new Blob([xml], { type: 'application/xml' });
- const link = document.createElement('a');
- link.href = URL.createObjectURL(blob);
- link.download = `partecipanti_consiglio_${new Date().toLocaleDateString('it-IT').replace(/\//g, '-')}.xml`;
- link.click();
- });
-
- document.getElementById('importXML').addEventListener('click', () => document.getElementById('fileInput').click());
-
- document.getElementById('fileInput').addEventListener('change', e => {
- const file = e.target.files[0];
- if (!file) return;
- const reader = new FileReader();
- reader.onload = evt => {
- const xml = new DOMParser().parseFromString(evt.target.result, 'application/xml');
- const participants = xml.getElementsByTagName('participant');
- buttonLabels = [];
- buttonCoords = [];
- for (const p of participants) {
- buttonLabels.push(p.getElementsByTagName('name')[0].textContent);
- buttonCoords.push({
- x: parseInt(p.getElementsByTagName('x')[0]?.textContent || 0),
- y: parseInt(p.getElementsByTagName('y')[0]?.textContent || 0)
- });
- }
- createButtons();
- document.getElementById('sessionControls').classList.remove('hidden');
- };
- reader.readAsText(file);
- });
-
- let dragEl = null, offsetX = 0, offsetY = 0;
- function startDrag(e) {
- dragEl = e.target;
- offsetX = e.offsetX;
- offsetY = e.offsetY;
- document.onmousemove = drag;
- document.onmouseup = stopDrag;
- }
-
- function drag(e) {
- if (!dragEl) return;
- const rect = map.getBoundingClientRect();
- const x = e.clientX - rect.left - offsetX;
- const y = e.clientY - rect.top - offsetY;
- dragEl.style.left = `${x}px`;
- dragEl.style.top = `${y}px`;
- }
-
- function stopDrag() {
- if (dragEl) {
- const i = dragEl.dataset.index;
- buttonCoords[i] = {
- x: parseInt(dragEl.style.left),
- y: parseInt(dragEl.style.top)
- };
- dragEl = null;
- document.onmousemove = null;
- document.onmouseup = null;
- saveSessionData();
- }
- }
-
- document.getElementById('mapConfig').addEventListener('click', () => {
- sessionState.mapVisible = true;
- saveSessionData();
- renderMapConfig();
- });
-
- saveBtn.addEventListener('click', (event) => {
- if (map.classList.contains('hidden')) return;
- giveButtonFeedback(event.currentTarget);
-
- document.querySelectorAll('.map-button').forEach(b => {
- b.onmousedown = null;
- b.style.cursor = 'pointer';
- b.onclick = () => {
- giveButtonFeedback(b);
- logSpeech(b.dataset.index);
- };
- });
-
- sessionState.mapVisible = true;
- saveSessionData();
-
- saveBtn.style.display = 'none';
- saveBtn.classList.add('hidden');
- exportXMLBtn.classList.remove('hidden');
- });
-
- window.addEventListener('load', () => {
- map.classList.add('hidden');
- saveBtn.classList.add('hidden');
- saveBtn.style.display = 'none';
-
- const savedLog = localStorage.getItem('sessionLog');
- if (savedLog) sessionLog = savedLog;
-
- const savedLabels = localStorage.getItem('buttonLabels');
- const savedCoords = localStorage.getItem('buttonCoords');
- const savedState = localStorage.getItem('sessionState');
-
- if (savedLabels && savedCoords) {
- buttonLabels = JSON.parse(savedLabels);
- buttonCoords = JSON.parse(savedCoords);
- createButtons();
- document.getElementById('sessionControls').classList.remove('hidden');
- }
-
- if (savedState) {
- sessionState = JSON.parse(savedState);
- if (sessionState.active && sessionState.startTime) {
- sessionStart = new Date(sessionState.startTime);
- document.getElementById('start').classList.add('hidden');
- document.getElementById('stop').classList.remove('hidden');
- document.getElementById('configure').classList.add('hidden');
- }
- document.getElementById('currentSpeaker').textContent = sessionState.currentSpeaker || 'Nessuno';
-
- if (sessionState.mapVisible) {
- renderMapView();
- }
- }
-
- applyButtonFeedback();
- document.getElementById('numPartecipanti').value = '';
- });
- </script>
- </div>
- </body>
- </html>
|