Bootstrap 5
Calendar
Fullcalendar
Evenements
Dynamique
Javascript
Calendrier dynamique Bootstrap 5 avec gestion complète d'événements, vues mensuelle/semaine, création rapide événements et notifications.
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="copyright" content="AngularForAll" />
<meta name="author" content="AngularForAll" />
<meta name="robots" content="noindex, nofollow" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Cache-Control" content="public, max-age=604800" />
<title>Snippets Full Calendar Bootstrap5 2026 05060029 | AngularForAll</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">
<style>
:root {
--primary-color: #4e73df;
--secondary-color: #858796;
--success-color: #1cc88a;
--info-color: #36b9cc;
--warning-color: #f6c23e;
--danger-color: #e74a3b;
--light-color: #f8f9fc;
--dark-color: #5a5c69;
--today-bg: #4e73df;
--selected-bg: #e8f0fe;
--event-color-1: #4e73df;
--event-color-2: #1cc88a;
--event-color-3: #f6c23e;
--event-color-4: #e74a3b;
--event-color-5: #36b9cc;
}
body {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 2rem 0;
}
.calendar-container {
background: white;
border-radius: 20px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
overflow: hidden;
}
.calendar-header {
background: linear-gradient(135deg, #4e73df 0%, #224abe 100%);
color: white;
padding: 2rem;
}
.calendar-nav {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 1.5rem;
}
.month-title {
font-size: 1.8rem;
font-weight: 700;
margin: 0;
text-transform: capitalize;
letter-spacing: 1px;
}
.btn-nav {
background: rgba(255, 255, 255, 0.2);
color: white;
border: none;
width: 45px;
height: 45px;
border-radius: 50%;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.2rem;
}
.btn-nav:hover {
background: rgba(255, 255, 255, 0.4);
transform: scale(1.1);
color: white;
}
.btn-today {
background: rgba(255, 255, 255, 0.25);
color: white;
border: 2px solid rgba(255, 255, 255, 0.5);
padding: 0.5rem 1.5rem;
border-radius: 25px;
font-weight: 600;
transition: all 0.3s ease;
}
.btn-today:hover {
background: white;
color: #4e73df;
border-color: white;
}
.view-selector {
display: flex;
gap: 0.5rem;
background: rgba(255, 255, 255, 0.15);
padding: 0.3rem;
border-radius: 25px;
}
.btn-view {
background: transparent;
color: white;
border: none;
padding: 0.5rem 1.2rem;
border-radius: 20px;
font-size: 0.875rem;
font-weight: 600;
transition: all 0.3s ease;
white-space: nowrap;
}
.btn-view.active {
background: white;
color: #4e73df;
}
.btn-view:hover:not(.active) {
background: rgba(255, 255, 255, 0.2);
color: white;
}
.calendar-body {
padding: 1.5rem;
min-height: 500px;
}
/* Vue Mois */
.weekdays {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 5px;
margin-bottom: 10px;
}
.weekday {
text-align: center;
font-weight: 700;
color: #4e73df;
padding: 0.8rem 0;
font-size: 0.9rem;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.weekday.weekend {
color: #e74a3b;
}
.days-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 5px;
}
.day-cell {
aspect-ratio: 1;
border-radius: 10px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
font-weight: 500;
color: #5a5c69;
font-size: 0.9rem;
min-height: 70px;
}
.day-cell:hover {
background: #f0f2f5;
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
.day-cell.other-month {
color: #d1d3e2;
cursor: default;
}
.day-cell.other-month:hover {
background: transparent;
transform: none;
box-shadow: none;
}
.day-cell.today {
background: var(--today-bg);
color: white;
font-weight: 700;
box-shadow: 0 5px 15px rgba(78, 115, 223, 0.4);
}
.day-cell.today:hover {
background: #224abe;
color: white;
}
.day-cell.selected {
background: var(--selected-bg);
color: #4e73df;
font-weight: 700;
box-shadow: 0 0 0 2px #4e73df;
}
.day-cell.weekend {
color: #e74a3b;
}
.day-cell.today.weekend {
color: white;
}
.day-number {
font-size: 1rem;
margin-bottom: 5px;
}
.event-dots {
display: flex;
gap: 3px;
margin-top: 3px;
}
.event-dot {
width: 5px;
height: 5px;
border-radius: 50%;
}
.event-dot.color-1 {
background: var(--event-color-1);
}
.event-dot.color-2 {
background: var(--event-color-2);
}
.event-dot.color-3 {
background: var(--event-color-3);
}
.event-dot.color-4 {
background: var(--event-color-4);
}
.event-dot.color-5 {
background: var(--event-color-5);
}
/* Vue Semaine */
.week-view {
display: none;
}
.week-view.active {
display: block;
}
.week-header {
display: grid;
grid-template-columns: 60px repeat(7, 1fr);
gap: 2px;
margin-bottom: 2px;
}
.week-header-cell {
text-align: center;
padding: 0.5rem;
font-weight: 600;
font-size: 0.875rem;
color: #4e73df;
}
.week-header-cell.today {
background: rgba(78, 115, 223, 0.1);
border-radius: 8px;
color: #4e73df;
}
.week-body {
display: grid;
grid-template-columns: 60px repeat(7, 1fr);
gap: 2px;
}
.time-label {
text-align: right;
padding: 0.5rem;
font-size: 0.75rem;
color: #858796;
font-weight: 500;
}
.week-cell {
min-height: 40px;
padding: 0.25rem;
font-size: 0.75rem;
border: 1px solid #e3e6f0;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s ease;
}
.week-cell:hover {
background: #f0f2f5;
}
.week-cell.has-event {
background: rgba(78, 115, 223, 0.1);
border-left: 3px solid #4e73df;
padding: 0.5rem;
}
.week-event-title {
font-weight: 600;
color: #4e73df;
font-size: 0.75rem;
margin-bottom: 2px;
}
.week-event-time {
color: #858796;
font-size: 0.7rem;
}
/* Vue Jour */
.day-view {
display: none;
}
.day-view.active {
display: block;
}
.day-view-header {
background: linear-gradient(135deg, #4e73df 0%, #224abe 100%);
color: white;
padding: 1.5rem;
border-radius: 10px;
margin-bottom: 1.5rem;
}
.day-view-date {
font-size: 2rem;
font-weight: 700;
}
.day-view-day {
font-size: 1rem;
opacity: 0.9;
}
.day-timeline {
position: relative;
}
.day-event {
background: white;
border-left: 4px solid #4e73df;
padding: 1rem;
margin-bottom: 0.5rem;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
transition: all 0.3s ease;
cursor: pointer;
}
.day-event:hover {
transform: translateX(5px);
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
}
.day-event.color-1 {
border-left-color: var(--event-color-1);
}
.day-event.color-2 {
border-left-color: var(--event-color-2);
}
.day-event.color-3 {
border-left-color: var(--event-color-3);
}
.day-event.color-4 {
border-left-color: var(--event-color-4);
}
.day-event.color-5 {
border-left-color: var(--event-color-5);
}
.event-time-badge {
background: #f0f2f5;
padding: 0.25rem 0.75rem;
border-radius: 15px;
font-size: 0.8rem;
font-weight: 600;
color: #4e73df;
display: inline-block;
margin-bottom: 0.5rem;
}
.event-title {
font-weight: 700;
color: #5a5c69;
margin-bottom: 0.25rem;
}
.event-description {
font-size: 0.875rem;
color: #858796;
}
/* Modal */
.modal-event-color {
width: 30px;
height: 30px;
border-radius: 50%;
display: inline-block;
cursor: pointer;
transition: all 0.2s ease;
border: 3px solid transparent;
}
.modal-event-color:hover {
transform: scale(1.2);
}
.modal-event-color.selected {
border-color: #5a5c69;
}
/* Responsive */
@media (max-width: 768px) {
.calendar-header {
padding: 1rem;
}
.month-title {
font-size: 1.3rem;
}
.btn-view {
padding: 0.4rem 0.8rem;
font-size: 0.75rem;
}
.day-cell {
min-height: 50px;
font-size: 0.8rem;
}
.week-header,
.week-body {
grid-template-columns: 40px repeat(7, 1fr);
}
.view-selector {
gap: 0.2rem;
}
}
@media (max-width: 480px) {
.calendar-nav {
flex-direction: column;
gap: 1rem;
}
.view-selector {
width: 100%;
justify-content: center;
}
.day-cell {
min-height: 40px;
font-size: 0.75rem;
}
}
</style>
</head>
<body>
<div class="container">
<div class="row justify-content-center">
<div class="col-12">
<div class="calendar-container">
<!-- En-tête du calendrier -->
<div class="calendar-header">
<div class="calendar-nav">
<div class="d-flex align-items-center gap-3">
<button class="btn-nav" id="prevBtn" title="Mois précédent">
<i class="bi bi-chevron-left"></i>
</button>
<h2 class="month-title" id="monthYear">Mai 2026</h2>
<button class="btn-nav" id="nextBtn" title="Mois suivant">
<i class="bi bi-chevron-right"></i>
</button>
</div>
<div class="d-flex align-items-center gap-2">
<button class="btn-today" id="todayBtn">
<i class="bi bi-calendar-check me-2"></i>Aujourd'hui
</button>
<div class="view-selector">
<button class="btn-view active" data-view="month">
<i class="bi bi-calendar-month me-1"></i>Mois
</button>
<button class="btn-view" data-view="week">
<i class="bi bi-calendar-week me-1"></i>Semaine
</button>
<button class="btn-view" data-view="day">
<i class="bi bi-calendar-day me-1"></i>Jour
</button>
</div>
</div>
</div>
<div class="row align-items-center">
<div class="col-md-8">
<div id="selectedDateInfo" class="text-white-50">
<i class="bi bi-hand-index-thumb me-2"></i>
Cliquez sur une date pour voir les détails ou ajouter un événement
</div>
</div>
<div class="col-md-4 text-md-end mt-2 mt-md-0">
<button class="btn btn-light" data-bs-toggle="modal" data-bs-target="#addEventModal">
<i class="bi bi-plus-circle me-2"></i>Nouvel événement
</button>
</div>
</div>
</div>
<!-- Corps du calendrier -->
<div class="calendar-body">
<!-- Vue Mois -->
<div id="monthView" class="view-container">
<div class="weekdays" id="weekdaysContainer"></div>
<div class="days-grid" id="daysGrid"></div>
</div>
<!-- Vue Semaine -->
<div id="weekView" class="week-view">
<div class="week-header" id="weekHeader"></div>
<div class="week-body" id="weekBody"></div>
</div>
<!-- Vue Jour -->
<div id="dayView" class="day-view">
<div class="day-view-header">
<div class="day-view-day" id="dayViewDay"></div>
<div class="day-view-date" id="dayViewDate"></div>
</div>
<div class="day-timeline" id="dayTimeline"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Modal Ajout d'événement -->
<div class="modal fade" id="addEventModal" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-primary text-white">
<h5 class="modal-title">
<i class="bi bi-calendar-plus me-2"></i>Ajouter un événement
</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="eventForm">
<div class="mb-3">
<label class="form-label">Date</label>
<input type="date" class="form-control" id="eventDate" required>
</div>
<div class="mb-3">
<label class="form-label">Titre</label>
<input type="text" class="form-control" id="eventTitle" placeholder="Titre de l'événement" required>
</div>
<div class="mb-3">
<label class="form-label">Heure</label>
<input type="time" class="form-control" id="eventTime">
</div>
<div class="mb-3">
<label class="form-label">Description</label>
<textarea class="form-control" id="eventDescription" rows="3" placeholder="Description (optionnelle)"></textarea>
</div>
<div class="mb-3">
<label class="form-label">Couleur</label>
<div class="d-flex gap-2">
<span class="modal-event-color" style="background: var(--event-color-1);" data-color="1"></span>
<span class="modal-event-color" style="background: var(--event-color-2);" data-color="2"></span>
<span class="modal-event-color" style="background: var(--event-color-3);" data-color="3"></span>
<span class="modal-event-color" style="background: var(--event-color-4);" data-color="4"></span>
<span class="modal-event-color selected" style="background: var(--event-color-5);" data-color="5"></span>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Annuler</button>
<button type="button" class="btn btn-primary" id="saveEventBtn">
<i class="bi bi-check-lg me-2"></i>Enregistrer
</button>
</div>
</div>
</div>
</div>
<!-- Modal Détails événement -->
<div class="modal fade" id="eventDetailsModal" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">
<i class="bi bi-calendar-event me-2"></i>Détails de l'événement
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body" id="eventDetailsBody">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger" id="deleteEventBtn">
<i class="bi bi-trash me-2"></i>Supprimer
</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Fermer</button>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
class DynamicCalendar {
constructor() {
this.currentDate = new Date();
this.selectedDate = new Date();
this.currentView = 'month';
this.events = this.loadEvents();
this.selectedColor = 5;
this.init();
}
init() {
this.render();
this.attachEventListeners();
}
loadEvents() {
const saved = localStorage.getItem('calendarEvents');
return saved ? JSON.parse(saved) : this.getDefaultEvents();
}
saveEvents() {
localStorage.setItem('calendarEvents', JSON.stringify(this.events));
}
getDefaultEvents() {
const today = new Date();
const formatDate = (d) => d.toISOString().split('T')[0];
return [
{
id: 1,
date: formatDate(new Date(today.getFullYear(), today.getMonth(), today.getDate())),
title: 'Réunion d\'équipe',
time: '10:00',
description: 'Réunion hebdomadaire de suivi des projets',
color: 1
},
{
id: 2,
date: formatDate(new Date(today.getFullYear(), today.getMonth(), today.getDate() + 2)),
title: 'Déjeuner client',
time: '12:30',
description: 'Déjeuner avec le client ABC Corp',
color: 2
},
{
id: 3,
date: formatDate(new Date(today.getFullYear(), today.getMonth(), today.getDate() + 5)),
title: 'Formation',
time: '14:00',
description: 'Formation sur les nouvelles technologies',
color: 3
},
{
id: 4,
date: formatDate(new Date(today.getFullYear(), today.getMonth() + 1, 1)),
title: 'Deadline projet',
time: '18:00',
description: 'Date limite de soumission du projet X',
color: 4
},
{
id: 5,
date: formatDate(new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1)),
title: 'Rendez-vous médecin',
time: '09:00',
description: 'Consultation annuelle',
color: 5
}
];
}
getEventsForDate(dateStr) {
return this.events.filter(event => event.date === dateStr);
}
render() {
this.updateHeader();
switch(this.currentView) {
case 'month':
this.renderMonthView();
break;
case 'week':
this.renderWeekView();
break;
case 'day':
this.renderDayView();
break;
}
}
updateHeader() {
const options = { month: 'long', year: 'numeric' };
document.getElementById('monthYear').textContent =
this.currentDate.toLocaleDateString('fr-FR', options);
// Mise à jour info date sélectionnée
const dateStr = this.formatDate(this.selectedDate);
const events = this.getEventsForDate(dateStr);
const infoDiv = document.getElementById('selectedDateInfo');
if (events.length > 0) {
infoDiv.innerHTML = `
<i class="bi bi-calendar-check me-2"></i>
<strong>${this.selectedDate.toLocaleDateString('fr-FR', { weekday: 'long', day: 'numeric', month: 'long' })}</strong>
- ${events.length} événement(s)
`;
} else {
infoDiv.innerHTML = `
<i class="bi bi-calendar me-2"></i>
${this.selectedDate.toLocaleDateString('fr-FR', { weekday: 'long', day: 'numeric', month: 'long' })}
`;
}
}
renderMonthView() {
const year = this.currentDate.getFullYear();
const month = this.currentDate.getMonth();
// Jours de la semaine
const weekdays = ['Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam', 'Dim'];
const weekdaysContainer = document.getElementById('weekdaysContainer');
weekdaysContainer.innerHTML = weekdays.map((day, index) => {
const isWeekend = index >= 5;
return `<div class="weekday ${isWeekend ? 'weekend' : ''}">${day}</div>`;
}).join('');
// Grille des jours
const firstDay = new Date(year, month, 1);
const lastDay = new Date(year, month + 1, 0);
const startPadding = (firstDay.getDay() + 6) % 7;
const daysGrid = document.getElementById('daysGrid');
let html = '';
// Jours du mois précédent
const prevLastDay = new Date(year, month, 0);
for (let i = startPadding - 1; i >= 0; i--) {
const day = prevLastDay.getDate() - i;
const date = new Date(year, month - 1, day);
html += this.createDayCell(date, true);
}
// Jours du mois courant
for (let day = 1; day <= lastDay.getDate(); day++) {
const date = new Date(year, month, day);
html += this.createDayCell(date, false);
}
// Jours du mois suivant
const remainingCells = 42 - (startPadding + lastDay.getDate());
for (let day = 1; day <= remainingCells; day++) {
const date = new Date(year, month + 1, day);
html += this.createDayCell(date, true);
}
daysGrid.innerHTML = html;
}
createDayCell(date, isOtherMonth) {
const today = new Date();
const dateStr = this.formatDate(date);
const events = this.getEventsForDate(dateStr);
const isToday = this.formatDate(date) === this.formatDate(today);
const isSelected = this.formatDate(date) === this.formatDate(this.selectedDate);
const isWeekend = date.getDay() === 0 || date.getDay() === 6;
let classes = ['day-cell'];
if (isOtherMonth) classes.push('other-month');
if (isToday) classes.push('today');
if (isSelected) classes.push('selected');
if (isWeekend && !isToday) classes.push('weekend');
let eventDots = '';
if (events.length > 0) {
const uniqueColors = [...new Set(events.map(e => e.color))];
eventDots = `
<div class="event-dots">
${uniqueColors.slice(0, 3).map(color =>
`<span class="event-dot color-${color}"></span>`
).join('')}
${uniqueColors.length > 3 ? '<span class="event-dot" style="background: #858796;">+</span>' : ''}
</div>
`;
}
return `
<div class="${classes.join(' ')}" data-date="${dateStr}" onclick="calendar.selectDate('${dateStr}')">
<span class="day-number">${date.getDate()}</span>
${eventDots}
</div>
`;
}
renderWeekView() {
const weekStart = this.getWeekStart(this.selectedDate);
const weekHeader = document.getElementById('weekHeader');
const weekBody = document.getElementById('weekBody');
// En-tête de la semaine
const days = ['Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam', 'Dim'];
let headerHtml = '<div></div>'; // Colonne vide pour les heures
for (let i = 0; i < 7; i++) {
const date = new Date(weekStart);
date.setDate(date.getDate() + i);
const isToday = this.formatDate(date) === this.formatDate(new Date());
headerHtml += `
<div class="week-header-cell ${isToday ? 'today' : ''}">
<div>${days[i]}</div>
<strong>${date.getDate()}</strong>
</div>
`;
}
weekHeader.innerHTML = headerHtml;
// Corps de la semaine (8h-20h)
let bodyHtml = '';
for (let hour = 8; hour <= 20; hour++) {
bodyHtml += `<div class="time-label">${hour}:00</div>`;
for (let day = 0; day < 7; day++) {
const date = new Date(weekStart);
date.setDate(date.getDate() + day);
const dateStr = this.formatDate(date);
const events = this.getEventsForDate(dateStr);
// Trouver les événements pour cette heure
const hourEvents = events.filter(event => {
if (!event.time) return false;
const eventHour = parseInt(event.time.split(':')[0]);
return eventHour === hour;
});
if (hourEvents.length > 0) {
const event = hourEvents[0];
bodyHtml += `
<div class="week-cell has-event" onclick="calendar.showEventDetails(${event.id})">
<div class="week-event-title">${event.title}</div>
<div class="week-event-time">${event.time}</div>
</div>
`;
} else {
bodyHtml += `<div class="week-cell"></div>`;
}
}
}
weekBody.innerHTML = bodyHtml;
}
renderDayView() {
const dateStr = this.formatDate(this.selectedDate);
const events = this.getEventsForDate(dateStr);
document.getElementById('dayViewDay').textContent =
this.selectedDate.toLocaleDateString('fr-FR', { weekday: 'long' });
document.getElementById('dayViewDate').textContent =
this.selectedDate.toLocaleDateString('fr-FR', { day: 'numeric', month: 'long', year: 'numeric' });
const timeline = document.getElementById('dayTimeline');
if (events.length === 0) {
timeline.innerHTML = `
<div class="text-center text-muted py-5">
<i class="bi bi-calendar-x display-4"></i>
<p class="mt-3">Aucun événement pour cette journée</p>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addEventModal">
<i class="bi bi-plus-circle me-2"></i>Ajouter un événement
</button>
</div>
`;
} else {
const sortedEvents = events.sort((a, b) => {
if (!a.time) return 1;
if (!b.time) return -1;
return a.time.localeCompare(b.time);
});
timeline.innerHTML = sortedEvents.map(event => `
<div class="day-event color-${event.color}" onclick="calendar.showEventDetails(${event.id})">
<span class="event-time-badge">
<i class="bi bi-clock me-2"></i>${event.time || 'Journée entière'}
</span>
<div class="event-title">${event.title}</div>
${event.description ? `<div class="event-description">${event.description}</div>` : ''}
</div>
`).join('');
}
}
getWeekStart(date) {
const d = new Date(date);
const day = d.getDay();
const diff = d.getDate() - day + (day === 0 ? -6 : 1);
d.setDate(diff);
return d;
}
formatDate(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
selectDate(dateStr) {
this.selectedDate = new Date(dateStr + 'T00:00:00');
if (this.currentView === 'day') {
this.renderDayView();
}
this.render();
// Mettre à jour le champ date du modal
document.getElementById('eventDate').value = dateStr;
}
navigateMonth(direction) {
this.currentDate.setMonth(this.currentDate.getMonth() + direction);
this.render();
}
goToToday() {
this.currentDate = new Date();
this.selectedDate = new Date();
this.render();
document.getElementById('eventDate').value = this.formatDate(new Date());
}
switchView(view) {
this.currentView = view;
// Masquer toutes les vues
document.querySelectorAll('.view-container, .week-view, .day-view').forEach(el => {
el.style.display = 'none';
el.classList.remove('active');
});
// Afficher la vue sélectionnée
switch(view) {
case 'month':
document.getElementById('monthView').style.display = 'block';
break;
case 'week':
document.getElementById('weekView').style.display = 'block';
document.getElementById('weekView').classList.add('active');
break;
case 'day':
document.getElementById('dayView').style.display = 'block';
document.getElementById('dayView').classList.add('active');
break;
}
this.render();
}
addEvent(eventData) {
const newEvent = {
id: Date.now(),
...eventData
};
this.events.push(newEvent);
this.saveEvents();
this.render();
// Notification
this.showToast('Événement ajouté avec succès !', 'success');
}
deleteEvent(eventId) {
this.events = this.events.filter(event => event.id !== eventId);
this.saveEvents();
this.render();
const modal = bootstrap.Modal.getInstance(document.getElementById('eventDetailsModal'));
modal.hide();
this.showToast('Événement supprimé', 'warning');
}
showEventDetails(eventId) {
const event = this.events.find(e => e.id === eventId);
if (!event) return;
const colors = {
1: '#4e73df',
2: '#1cc88a',
3: '#f6c23e',
4: '#e74a3b',
5: '#36b9cc'
};
document.getElementById('eventDetailsBody').innerHTML = `
<div class="text-center mb-3">
<span style="display: inline-block; width: 20px; height: 20px; background: ${colors[event.color]}; border-radius: 50%;"></span>
</div>
<h5 class="text-center">${event.title}</h5>
<div class="text-center text-muted mb-3">
<i class="bi bi-calendar me-2"></i>
${new Date(event.date + 'T00:00:00').toLocaleDateString('fr-FR', { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' })}
${event.time ? `<br><i class="bi bi-clock me-2"></i>${event.time}` : ''}
</div>
${event.description ? `
<div class="alert alert-light">
<i class="bi bi-chat-left-text me-2"></i>${event.description}
</div>
` : ''}
`;
document.getElementById('deleteEventBtn').onclick = () => this.deleteEvent(eventId);
const modal = new bootstrap.Modal(document.getElementById('eventDetailsModal'));
modal.show();
}
showToast(message, type = 'info') {
const toastContainer = document.createElement('div');
toastContainer.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
z-index: 9999;
`;
const bgClass = type === 'success' ? 'bg-success' :
type === 'warning' ? 'bg-warning' : 'bg-info';
toastContainer.innerHTML = `
<div class="toast show ${bgClass} text-white" role="alert">
<div class="toast-body d-flex align-items-center">
<i class="bi bi-${type === 'success' ? 'check-circle' : type === 'warning' ? 'exclamation-triangle' : 'info-circle'} me-2"></i>
${message}
<button type="button" class="btn-close btn-close-white ms-3" data-bs-dismiss="toast"></button>
</div>
</div>
`;
document.body.appendChild(toastContainer);
setTimeout(() => {
toastContainer.remove();
}, 3000);
}
attachEventListeners() {
// Navigation mois
document.getElementById('prevBtn').addEventListener('click', () => {
this.navigateMonth(-1);
});
document.getElementById('nextBtn').addEventListener('click', () => {
this.navigateMonth(1);
});
document.getElementById('todayBtn').addEventListener('click', () => {
this.goToToday();
});
// Sélecteur de vue
document.querySelectorAll('.btn-view').forEach(btn => {
btn.addEventListener('click', (e) => {
document.querySelectorAll('.btn-view').forEach(b => b.classList.remove('active'));
e.target.classList.add('active');
this.switchView(e.target.dataset.view);
});
});
// Sélection de couleur dans le modal
document.querySelectorAll('.modal-event-color').forEach(colorEl => {
colorEl.addEventListener('click', (e) => {
document.querySelectorAll('.modal-event-color').forEach(el => el.classList.remove('selected'));
e.target.classList.add('selected');
this.selectedColor = parseInt(e.target.dataset.color);
});
});
// Sauvegarde d'événement
document.getElementById('saveEventBtn').addEventListener('click', () => {
const date = document.getElementById('eventDate').value;
const title = document.getElementById('eventTitle').value.trim();
const time = document.getElementById('eventTime').value;
const description = document.getElementById('eventDescription').value.trim();
if (!date || !title) {
alert('Veuillez remplir la date et le titre de l\'événement');
return;
}
this.addEvent({
date,
title,
time,
description,
color: this.selectedColor
});
// Réinitialiser le formulaire
document.getElementById('eventForm').reset();
document.getElementById('eventDate').value = this.formatDate(this.selectedDate);
// Fermer le modal
const modal = bootstrap.Modal.getInstance(document.getElementById('addEventModal'));
modal.hide();
});
// Mise à jour de la date dans le modal quand on clique sur une date
document.getElementById('addEventModal').addEventListener('show.bs.modal', () => {
document.getElementById('eventDate').value = this.formatDate(this.selectedDate);
});
}
}
// Initialisation du calendrier
const calendar = new DynamicCalendar();
// Raccourcis clavier
document.addEventListener('keydown', (e) => {
if (e.key === 'ArrowLeft') {
calendar.navigateMonth(-1);
} else if (e.key === 'ArrowRight') {
calendar.navigateMonth(1);
} else if (e.key === 't') {
calendar.goToToday();
}
});
</script>
</body>
</html>
Télécharger le fichier source