Calendario Corsi di Formazione Online
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
border-radius: 20px;
box-shadow: 0 20px 40px rgba(0,0,0,0.1);
overflow: hidden;
}
.header {
background: linear-gradient(135deg, #2c3e50 0%, #3498db 100%);
color: white;
padding: 30px;
text-align: center;
}
.header h1 {
font-size: 2.5em;
margin-bottom: 10px;
text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
}
.header p {
font-size: 1.2em;
opacity: 0.9;
}
.controls {
padding: 20px;
background: #f8f9fa;
border-bottom: 1px solid #e9ecef;
}
.control-group {
display: flex;
gap: 15px;
flex-wrap: wrap;
align-items: center;
}
.btn {
padding: 12px 24px;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
font-weight: 600;
transition: all 0.3s ease;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.btn-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.btn-success {
background: linear-gradient(135deg, #56ab2f 0%, #a8e6cf 100%);
color: white;
}
.btn-danger {
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a24 100%);
color: white;
}
.btn-warning {
background: linear-gradient(135deg, #feca57 0%, #ff9ff3 100%);
color: white;
}
.btn:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
.status {
padding: 15px;
margin: 20px;
border-radius: 8px;
font-weight: 500;
}
.status.success {
background: #d4edda;
border: 1px solid #c3e6cb;
color: #155724;
}
.status.error {
background: #f8d7da;
border: 1px solid #f5c6cb;
color: #721c24;
}
.status.warning {
background: #fff3cd;
border: 1px solid #ffeaa7;
color: #856404;
}
.course-selector {
display: flex;
gap: 10px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.course-btn {
padding: 10px 20px;
border: 2px solid #667eea;
background: white;
color: #667eea;
border-radius: 25px;
cursor: pointer;
transition: all 0.3s ease;
font-weight: 600;
}
.course-btn.active {
background: #667eea;
color: white;
}
.course-btn:hover {
background: #667eea;
color: white;
transform: scale(1.05);
}
.calendar-container {
padding: 20px;
max-height: 600px;
overflow-y: auto;
}
.course-section {
margin-bottom: 30px;
border: 1px solid #e9ecef;
border-radius: 12px;
overflow: hidden;
}
.course-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 15px 20px;
font-weight: 600;
font-size: 1.1em;
}
.lessons-table {
width: 100%;
border-collapse: collapse;
}
.lessons-table th {
background: #f8f9fa;
padding: 12px;
text-align: left;
font-weight: 600;
border-bottom: 2px solid #e9ecef;
}
.lessons-table td {
padding: 12px;
border-bottom: 1px solid #e9ecef;
vertical-align: middle;
}
.lessons-table tr:hover {
background: #f8f9fa;
}
.lesson-row {
transition: all 0.3s ease;
}
.lesson-row.selected {
background: #e3f2fd !important;
border-left: 4px solid #2196f3;
}
.lesson-actions {
display: flex;
gap: 5px;
}
.btn-small {
padding: 5px 10px;
font-size: 12px;
border-radius: 4px;
}
.modal {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
backdrop-filter: blur(5px);
}
.modal-content {
background: white;
margin: 15% auto;
padding: 30px;
border-radius: 15px;
width: 90%;
max-width: 500px;
box-shadow: 0 20px 40px rgba(0,0,0,0.2);
}
.modal-header {
font-size: 1.5em;
margin-bottom: 20px;
color: #2c3e50;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: 600;
color: #555;
}
.form-group input, .form-group select {
width: 100%;
padding: 10px;
border: 2px solid #e9ecef;
border-radius: 8px;
font-size: 14px;
transition: border-color 0.3s ease;
}
.form-group input:focus, .form-group select:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102,126,234,0.1);
}
.modal-actions {
display: flex;
gap: 10px;
justify-content: flex-end;
}
.stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 20px;
}
.stat-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 20px;
border-radius: 12px;
text-align: center;
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}
.stat-number {
font-size: 2em;
font-weight: bold;
margin-bottom: 5px;
}
.stat-label {
font-size: 0.9em;
opacity: 0.9;
}
.loading {
text-align: center;
padding: 40px;
color: #666;
}
.spinner {
border: 4px solid #f3f3f3;
border-top: 4px solid #667eea;
border-radius: 50%;
width: 40px;
height: 40px;
animation: spin 1s linear infinite;
margin: 0 auto 20px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@media (max-width: 768px) {
.control-group {
flex-direction: column;
align-items: stretch;
}
.course-selector {
flex-direction: column;
}
.lessons-table {
font-size: 14px;
}
.btn {
font-size: 12px;
padding: 8px 16px;
}
}
🔄 Genera Calendario
📥 Esporta Calendario
🔄 Reset Calendario
🗑️ Cancella Tutto
Caricamento calendario...
Nuova Data:
Nuovo Orario:
09:00-09:30
09:45-10:15
10:30-11:00
✅ Conferma
❌ Annulla
// Stato dell'applicazione
let courses = [];
let selectedCourse = 'all';
let selectedLesson = null;
let excludedDates = ['2015-09-22', '2015-09-23', '2015-09-24'];
// Inizializzazione
document.addEventListener('DOMContentLoaded', function() {
generateSchedule();
});
// Genera il calendario iniziale
function generateSchedule() {
showStatus('Generazione calendario in corso...', 'warning');
// Resetta i corsi
courses = [];
// Genera 5 corsi
for (let i = 1; i <= 5; i++) {
courses.push({
id: i,
name: `Corso ${i}`,
lessons: generateLessonsForCourse(i)
});
}
updateUI();
showStatus('Calendario generato con successo!', 'success');
}
// Genera lezioni per un corso specifico
function generateLessonsForCourse(courseId) {
const lessons = [];
const startDate = new Date('2015-09-15');
const endDate = new Date('2015-12-03');
// Calcola la data di inizio per questo corso
const courseStartDate = new Date(startDate);
courseStartDate.setDate(startDate.getDate() + (courseId - 1) * 14); // 2 settimane tra corsi
let currentDate = new Date(courseStartDate);
let lessonCount = 0;
let dayCount = 0;
while (lessonCount < 10 && currentDate = 2) {
// Vai alla settimana successiva
currentDate.setDate(currentDate.getDate() + (7 - currentDate.getDay() + 1));
dayCount = 0;
continue;
}
// Aggiungi lezioni per questo giorno
const lessonsPerDay = Math.min(3, 10 - lessonCount);
const times = ['09:00', '09:45', '10:30'];
for (let i = 0; i < lessonsPerDay; i++) {
lessons.push({
id: lessonCount + 1,
courseId: courseId,
date: new Date(currentDate),
time: times[i],
title: `Lezione ${lessonCount + 1}`,
status: 'scheduled'
});
lessonCount++;
}
dayCount++;
currentDate.setDate(currentDate.getDate() + 2); // 2 giorni dopo
}
return lessons;
}
// Aggiorna l'interfaccia utente
function updateUI() {
updateCourseSelector();
updateCalendar();
updateStats();
}
// Aggiorna il selettore dei corsi
function updateCourseSelector() {
const selector = document.getElementById('courseSelector');
selector.innerHTML = `
📚 Tutti i Corsi
`;
courses.forEach(course => {
selector.innerHTML += `
${course.name}
`;
});
}
// Aggiorna il calendario
function updateCalendar() {
const container = document.getElementById('calendarContainer');
container.innerHTML = '';
const coursesToShow = selectedCourse === 'all' ? courses : courses.filter(c => c.id === selectedCourse);
coursesToShow.forEach(course => {
const section = document.createElement('div');
section.className = 'course-section';
section.innerHTML = `
Data
Giorno
Orario
Lezione
Status
Azioni
${course.lessons.map(lesson => `
${formatDate(lesson.date)}
${getDayName(lesson.date)}
${lesson.time}-${getEndTime(lesson.time)}
${lesson.title}
${getStatusText(lesson.status)}
📅 Sposta
🗑️ Cancella
✅ Completata
`).join('')}
`;
container.appendChild(section);
});
}
// Seleziona un corso
function selectCourse(courseId) {
selectedCourse = courseId;
updateUI();
}
// Sposta una lezione
function moveLesson(courseId, lessonId) {
selectedLesson = { courseId, lessonId };
document.getElementById('moveModal').style.display = 'block';
}
// Conferma lo spostamento
function confirmMove() {
const newDate = document.getElementById('newDate').value;
const newTime = document.getElementById('newTime').value;
if (!newDate || !newTime) {
showStatus('Seleziona data e orario per lo spostamento', 'error');
return;
}
const course = courses.find(c => c.id === selectedLesson.courseId);
const lesson = course.lessons.find(l => l.id === selectedLesson.lessonId);
if (lesson) {
lesson.date = new Date(newDate);
lesson.time = newTime;
updateUI();
closeModal();
showStatus('Lezione spostata con successo!', 'success');
}
}
// Cancella una lezione
function deleteLesson(courseId, lessonId) {
if (confirm('Sei sicuro di voler cancellare questa lezione?')) {
const course = courses.find(c => c.id === courseId);
const lessonIndex = course.lessons.findIndex(l => l.id === lessonId);
if (lessonIndex !== -1) {
course.lessons.splice(lessonIndex, 1);
// Genera una nuova lezione per completare il corso
const newLesson = generateReplacementLesson(courseId);
if (newLesson) {
course.lessons.push(newLesson);
}
updateUI();
showStatus('Lezione cancellata e sostituita con successo!', 'success');
}
}
}
// Genera una lezione sostitutiva
function generateReplacementLesson(courseId) {
const course = courses.find(c => c.id === courseId);
if (!course) return null;
// Trova la prima data disponibile dopo l'ultima lezione
let lastDate = new Date(Math.max(...course.lessons.map(l => l.date.getTime())));
lastDate.setDate(lastDate.getDate() + 1);
const endDate = new Date('2015-12-03');
while (lastDate l.id)) + 1,
courseId: courseId,
date: new Date(lastDate),
time: '09:00',
title: `Lezione ${course.lessons.length + 1}`,
status: 'scheduled'
};
}
}
lastDate.setDate(lastDate.getDate() + 1);
}
return null;
}
// Marca lezione come completata
function markCompleted(courseId, lessonId) {
const course = courses.find(c => c.id === courseId);
const lesson = course.lessons.find(l => l.id === lessonId);
if (lesson) {
lesson.status = lesson.status === 'completed' ? 'scheduled' : 'completed';
updateUI();
showStatus('Status lezione aggiornato!', 'success');
}
}
// Aggiorna le statistiche
function updateStats() {
const totalCourses = courses.length;
const totalLessons = courses.reduce((sum, course) => sum + course.lessons.length, 0);
const completedLessons = courses.reduce((sum, course) =>
sum + course.lessons.filter(l => l.status === 'completed').length, 0);
// Trova prossima lezione
const now = new Date();
const upcomingLessons = courses.flatMap(course =>
course.lessons.filter(l => l.date > now && l.status === 'scheduled')
).sort((a, b) => a.date - b.date);
const nextLesson = upcomingLessons.length > 0 ?
formatDate(upcomingLessons[0].date) : 'Nessuna';
document.getElementById('totalCourses').textContent = totalCourses;
document.getElementById('totalLessons').textContent = totalLessons;
document.getElementById('completedLessons').textContent = completedLessons;
document.getElementById('nextLesson').textContent = nextLesson;
}
// Esporta calendario
function exportCalendar() {
const data = {
courses: courses,
generatedAt: new Date().toISOString(),
excludedDates: excludedDates
};
const dataStr = JSON.stringify(data, null, 2);
const dataUri = 'data:application/json;charset=utf-8,'+ encodeURIComponent(dataStr);
const exportFileDefaultName = 'calendario_corsi.json';
const linkElement = document.createElement('a');
linkElement.setAttribute('href', dataUri);
linkElement.setAttribute('download', exportFileDefaultName);
linkElement.click();
showStatus('Calendario esportato con successo!', 'success');
}
// Reset calendario
function resetCalendar() {
if (confirm('Sei sicuro di voler resettare il calendario?')) {
generateSchedule();
}
}
// Cancella tutto
function clearAll() {
if (confirm('Sei sicuro di voler cancellare tutto il calendario?')) {
courses = [];
updateUI();
showStatus('Calendario cancellato', 'warning');
}
}
// Chiudi modal
function closeModal() {
document.getElementById('moveModal').style.display = 'none';
selectedLesson = null;
}
// Mostra status
function showStatus(message, type) {
const status = document.getElementById('status');
status.textContent = message;
status.className = `status ${type}`;
status.style.display = 'block';
setTimeout(() => {
status.style.display = 'none';
}, 3000);
}
// Utility functions
function formatDate(date) {
return date.toLocaleDateString('it-IT');
}
function getDayName(date) {
const days = ['Domenica', 'Lunedì', 'Martedì', 'Mercoledì', 'Giovedì', 'Venerdì', 'Sabato'];
return days[date.getDay()];
}
function getEndTime(startTime) {
const [hours, minutes] = startTime.split(':').map(Number);
const endMinutes = minutes + 30;
const endHours = hours + Math.floor(endMinutes / 60);
return `${endHours.toString().padStart(2, '0')}:${(endMinutes % 60).toString().padStart(2, '0')}`;
}
function getStatusText(status) {
switch (status) {
case 'scheduled': return '📅 Programmata';
case 'completed': return '✅ Completata';
case 'cancelled': return '❌ Cancellata';
default: return '❓ Sconosciuto';
}
}
// Chiudi modal cliccando fuori
window.onclick = function(event) {
const modal = document.getElementById('moveModal');
if (event.target === modal) {
closeModal();
}
}