diff --git a/static/app.js b/static/app.js index 7606587..5fa0abd 100644 --- a/static/app.js +++ b/static/app.js @@ -441,38 +441,68 @@ async function loadCalendar() { function calPrev() { calMonth--; if(calMonth<1){calMonth=12;calYear--;} loadCalendar(); } function calNext() { calMonth++; if(calMonth>12){calMonth=1;calYear++;} loadCalendar(); } +// Palette de couleurs par outil (stable par ID) +const CAL_COLORS = [ + '#6366f1','#22c55e','#f59e0b','#38bdf8','#f472b6', + '#a78bfa','#34d399','#fb923c','#60a5fa','#e879f9','#4ade80' +]; +function toolColor(toolId) { return CAL_COLORS[(toolId - 1) % CAL_COLORS.length]; } + function renderCalendar(rentals) { - const title = document.getElementById('cal-title'); - title.textContent = new Date(calYear, calMonth-1, 1).toLocaleDateString('fr-FR', {month:'long', year:'numeric'}); + document.getElementById('cal-title').textContent = + new Date(calYear, calMonth-1, 1).toLocaleDateString('fr-FR', {month:'long', year:'numeric'}); const today = new Date().toISOString().slice(0,10); const firstDay = new Date(calYear, calMonth-1, 1).getDay(); const daysInMonth = new Date(calYear, calMonth, 0).getDate(); - const startOffset = (firstDay + 6) % 7; // Monday start + const startOffset = (firstDay + 6) % 7; - let html = ``; - ['Lun','Mar','Mer','Jeu','Ven','Sam','Dim'].forEach(d => html += ``); - html += ''; + // Légende des outils présents ce mois + const toolsInMonth = [...new Map(rentals.map(r => [r.tool_id, r])).values()]; + const legend = toolsInMonth.map(r => + `${esc(r.tool_name)}` + ).join(''); - for (let i=0; i${legend} +
+
`; - for (let day=1; day<=daysInMonth; day++) { + ['Lun','Mar','Mer','Jeu','Ven','Sam','Dim'].forEach((d, i) => { + html += `
${d}
`; + }); + + for (let i = 0; i < startOffset; i++) html += `
`; + + for (let day = 1; day <= daysInMonth; day++) { const dateStr = `${calYear}-${String(calMonth).padStart(2,'0')}-${String(day).padStart(2,'0')}`; const dayRentals = rentals.filter(r => r.start_date <= dateStr && r.end_date >= dateStr); const isToday = dateStr === today; - const numEl = isToday ? `
${day}
` : `
${day}
`; - const events = dayRentals.map(r => - `
- 🔨 ${esc(r.tool_name)} -
` - ).join(''); - html += `
`; - col++; - if (col % 7 === 0 && day < daysInMonth) html += ''; + const dow = (startOffset + day - 1) % 7; + const isWeekend = dow >= 5; + + const events = dayRentals.map(r => { + const color = toolColor(r.tool_id); + const isStart = r.start_date === dateStr; + const isEnd = r.end_date === dateStr; + const label = isStart ? `🔨 ${esc(r.tool_name)}` : esc(r.tool_name); + return `
${label}
`; + }).join(''); + + html += `
+
${day}
+ ${events} +
`; } - while (col % 7 !== 0) { html += ''; col++; } - html += '
${d}
${numEl}${events}
'; + + // Remplir la dernière ligne + const total = startOffset + daysInMonth; + const remaining = total % 7 === 0 ? 0 : 7 - (total % 7); + for (let i = 0; i < remaining; i++) html += `
`; + + html += ``; document.getElementById('calendar-wrap').innerHTML = html; } diff --git a/static/style.css b/static/style.css index 7db33b1..d79a51c 100644 --- a/static/style.css +++ b/static/style.css @@ -91,14 +91,31 @@ h3 { font-size: 1rem; font-weight: 600; color: var(--muted); } /* Calendar */ .cal-nav { display: flex; align-items: center; gap: 12px; } -.cal-nav span { font-weight: 600; min-width: 140px; text-align: center; } -.cal-table { width: 100%; border-collapse: collapse; } -.cal-table th { padding: 8px; text-align: center; font-size: 0.8rem; color: var(--muted); border-bottom: 1px solid var(--border); } -.cal-table td { padding: 4px; vertical-align: top; min-width: 80px; min-height: 80px; border: 1px solid var(--border); border-radius: 4px; } -.cal-day-num { font-size: 0.8rem; color: var(--muted); padding: 2px 4px; } -.cal-day-num.today { background: var(--primary); color: #fff; border-radius: 50%; width: 22px; height: 22px; display: flex; align-items: center; justify-content: center; } -.cal-event { font-size: 0.7rem; padding: 2px 5px; border-radius: 4px; margin-bottom: 2px; cursor: pointer; background: rgba(99,102,241,0.25); color: var(--primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } -.cal-event:hover { background: rgba(99,102,241,0.45); } +.cal-nav span { font-weight: 600; min-width: 160px; text-align: center; font-size: 1rem; } + +.cal-legend { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 12px; min-height: 20px; } +.cal-legend-item { display: flex; align-items: center; gap: 6px; font-size: .78rem; color: var(--muted); } +.cal-legend-dot { width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; } + +.cal-grid-wrap { overflow-x: auto; } +.cal-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; min-width: 560px; } + +.cal-header-cell { text-align: center; font-size: .75rem; font-weight: 600; color: var(--muted); padding: 6px 4px; text-transform: uppercase; letter-spacing: .05em; } +.cal-header-cell.cal-weekend { color: var(--warning); } + +.cal-cell { background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 6px; min-height: 90px; display: flex; flex-direction: column; gap: 3px; transition: border-color .15s; } +.cal-cell:hover { border-color: var(--primary); } +.cal-cell.cal-empty { background: transparent; border-color: transparent; } +.cal-cell.cal-today { border-color: var(--primary); background: rgba(99,102,241,.07); } +.cal-cell.cal-weekend { background: rgba(255,255,255,.02); } + +.cal-day-num { font-size: .78rem; color: var(--muted); font-weight: 500; margin-bottom: 2px; } +.cal-day-num.today { background: var(--primary); color: #fff; border-radius: 50%; width: 22px; height: 22px; display: flex; align-items: center; justify-content: center; font-weight: 700; } + +.cal-event-block { font-size: .7rem; padding: 2px 6px; border-radius: 4px; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; transition: opacity .15s; font-weight: 500; } +.cal-event-block:hover { opacity: .75; } +.cal-event-start { border-radius: 4px 0 0 4px; } +.cal-event-end { border-radius: 0 4px 4px 0; } /* Platforms */ .platform-row { display: flex; align-items: center; gap: 8px; padding: 8px 0; border-bottom: 1px solid var(--border); }