Aeroklubben-IT/arbetsgrupp-live/public/index.html

397 lines
No EOL
12 KiB
HTML

<!DOCTYPE html>
<html lang="sv">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Arbetsgrupp - Deltagarformulär</title>
<script src="/socket.io/socket.io.js"></script>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.container {
background: rgba(255, 255, 255, 0.95);
border-radius: 20px;
padding: 40px;
max-width: 600px;
width: 100%;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.2);
backdrop-filter: blur(10px);
}
.logo {
display: block;
margin: 0 auto 20px;
height: auto;
max-width: 100%;
width: 7em;
}
h1 {
color: #2c3e50;
text-align: center;
margin-bottom: 30px;
font-size: 2rem;
font-weight: 600;
}
.question {
background: #f8f9fa;
padding: 20px;
border-radius: 12px;
margin-bottom: 30px;
border-left: 4px solid #667eea;
}
.question h2 {
color: #2c3e50;
font-size: 1.3rem;
margin-bottom: 10px;
}
.options {
display: grid;
gap: 15px;
margin-bottom: 25px;
}
.option {
display: flex;
align-items: center;
padding: 15px;
background: white;
border: 2px solid #e9ecef;
border-radius: 10px;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
}
.option:hover {
border-color: #667eea;
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.2);
}
.option.selected {
border-color: #667eea;
background: linear-gradient(45deg, #667eea, #764ba2);
color: white;
}
.option input[type="checkbox"] {
margin-right: 12px;
transform: scale(1.2);
}
.option label {
flex: 1;
cursor: pointer;
font-weight: 500;
}
.custom-input-container {
margin-top: 20px;
}
.custom-input {
width: 100%;
padding: 15px;
border: 2px solid #e9ecef;
border-radius: 10px;
font-size: 16px;
transition: border-color 0.3s ease;
}
.custom-input:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
.add-custom {
margin-top: 10px;
background: #28a745;
color: white;
border: none;
padding: 10px 20px;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.3s ease;
}
.add-custom:hover {
background: #218838;
}
.submit-btn {
width: 100%;
background: linear-gradient(45deg, #667eea, #764ba2);
color: white;
border: none;
padding: 18px;
border-radius: 12px;
font-size: 18px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
margin-top: 20px;
}
.submit-btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3);
}
.submit-btn:disabled {
background: #6c757d;
cursor: not-allowed;
transform: none;
box-shadow: none;
}
.success-message {
background: #d4edda;
color: #155724;
padding: 15px;
border-radius: 10px;
margin-bottom: 20px;
text-align: center;
font-weight: 500;
}
.error-message {
background: #f8d7da;
color: #721c24;
padding: 15px;
border-radius: 10px;
margin-bottom: 20px;
text-align: center;
font-weight: 500;
}
@media (max-width: 768px) {
.container {
padding: 25px;
margin: 10px;
}
h1 {
font-size: 1.5rem;
}
}
</style>
</head>
<body>
<div class="container">
<img src="/aeroklubben.png" alt="Aeroklubben i Göteborg" class="logo" />
<h1>Vi behöver DIG!</h1>
<div id="message"></div>
<form id="responseForm">
<div class="question">
<h2>Vilken arbetsgrupp skulle du kunna tänka dig delta i?</h2>
<p style="color: #6c757d; margin-top: 5px;">Du kan välja flera alternativ</p>
</div>
<div style="margin-bottom: 25px;">
<input
type="text"
id="participantName"
class="custom-input"
placeholder="Ditt namn *"
required
style="margin-bottom: 20px;"
>
</div>
<div class="options" id="options">
<!-- Options will be populated dynamically -->
</div>
<div class="custom-input-container">
<input
type="text"
id="customInput"
class="custom-input"
placeholder="Eller skriv ditt eget alternativ..."
>
<button type="button" id="addCustom" class="add-custom">Lägg till</button>
</div>
<button type="submit" id="submitBtn" class="submit-btn">Skicka svar</button>
</form>
</div>
<script>
const socket = io();
const form = document.getElementById('responseForm');
const optionsContainer = document.getElementById('options');
const customInput = document.getElementById('customInput');
const addCustomBtn = document.getElementById('addCustom');
const submitBtn = document.getElementById('submitBtn');
const messageDiv = document.getElementById('message');
const participantNameInput = document.getElementById('participantName');
let availableOptions = [];
function showMessage(text, type = 'success') {
messageDiv.innerHTML = `<div class="${type}-message">${text}</div>`;
scrollTo(0, 0);
setTimeout(() => {
messageDiv.innerHTML = '';
}, 5000);
}
function renderOptions() {
// Store currently selected options before re-rendering
const currentlySelected = Array.from(document.querySelectorAll('input[type="checkbox"]:checked'))
.map(checkbox => checkbox.value);
optionsContainer.innerHTML = '';
availableOptions.forEach((option, index) => {
const optionDiv = document.createElement('div');
optionDiv.className = 'option';
optionDiv.innerHTML = `
<input type="checkbox" id="option${index}" value="${option}">
<label for="option${index}">${option}</label>
`;
const checkbox = optionDiv.querySelector('input');
const label = optionDiv.querySelector('label');
label.addEventListener('click', (e) => {
e.stopPropagation(); // Prevent triggering the div click event
});
// Restore selection state if this option was previously selected
if (currentlySelected.includes(option)) {
checkbox.checked = true;
optionDiv.classList.add('selected');
}
checkbox.addEventListener('change', () => {
if (checkbox.checked) {
optionDiv.classList.add('selected');
} else {
optionDiv.classList.remove('selected');
}
});
optionDiv.addEventListener('click', (e) => {
if (e.target !== checkbox) {
checkbox.checked = !checkbox.checked;
checkbox.dispatchEvent(new Event('change'));
}
});
optionsContainer.appendChild(optionDiv);
});
}
function addCustomOption() {
const customValue = customInput.value.trim();
if (customValue && !availableOptions.includes(customValue)) {
availableOptions.push(customValue);
renderOptions();
customInput.value = '';
// Auto-select the newly added option
const newCheckbox = document.querySelector(`input[value="${customValue}"]`);
if (newCheckbox) {
newCheckbox.checked = true;
newCheckbox.dispatchEvent(new Event('change'));
}
}
}
addCustomBtn.addEventListener('click', addCustomOption);
customInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
e.preventDefault();
addCustomOption();
}
});
form.addEventListener('submit', async (e) => {
e.preventDefault();
const participantName = participantNameInput.value.trim();
const selectedOptions = Array.from(document.querySelectorAll('input[type="checkbox"]:checked'))
.map(checkbox => checkbox.value);
if (!participantName) {
showMessage('Ange ditt namn', 'error');
return;
}
if (selectedOptions.length === 0) {
showMessage('Välj minst ett alternativ', 'error');
return;
}
submitBtn.disabled = true;
submitBtn.textContent = 'Skickar...';
try {
const response = await fetch('/api/submit', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
responses: selectedOptions,
participantName: participantName
}),
});
if (response.ok) {
showMessage('Tack för ditt svar!');
form.reset();
document.querySelectorAll('.option').forEach(opt => opt.classList.remove('selected'));
} else {
throw new Error('Server error');
}
} catch (error) {
showMessage('Ett fel uppstod. Försök igen.', 'error');
} finally {
submitBtn.disabled = false;
submitBtn.textContent = 'Skicka svar';
}
});
// Socket.IO event listeners
socket.on('optionsUpdated', (options) => {
availableOptions = options;
renderOptions();
});
// Initial load
fetch('/api/options')
.then(response => response.json())
.then(options => {
availableOptions = options;
renderOptions();
})
.catch(error => {
console.error('Error loading options:', error);
});
</script>
</body>
</html>