taskflow / calendrier.html
leroyfal's picture
Create calendrier.html
50fe2fe verified
Raw History Blame Contribute Delete
53.8 kB
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>TaskFlow - Calendrier</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.sidebar {
transition: all 0.3s ease;
}
.event-card:hover {
transform: translateY(-2px);
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
}
.calendar-day:hover {
background-color: #f5f3ff;
}
.calendar-day.today {
background-color: #eef2ff;
border: 2px solid #6366f1;
}
.calendar-day.selected {
background-color: #6366f1;
color: white;
}
.calendar-day.has-events::after {
content: '';
position: absolute;
bottom: 8px;
left: 50%;
transform: translateX(-50%);
width: 6px;
height: 6px;
border-radius: 50%;
background-color: #6366f1;
}
.calendar-day.selected.has-events::after {
background-color: white;
}
.dropdown-menu {
animation: fadeIn 0.2s ease-out;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
</style>
</head>
<body class="bg-gray-50 font-sans">
<!-- App Container -->
<div class="flex h-screen overflow-hidden">
<!-- Sidebar Navigation -->
<div class="sidebar bg-indigo-700 text-white w-64 flex-shrink-0 hidden md:block">
<div class="p-4 flex items-center space-x-2 border-b border-indigo-600">
<i class="fas fa-tasks text-2xl"></i>
<h1 class="text-xl font-bold">TaskFlow</h1>
</div>
<nav class="p-4">
<div class="mb-8">
<h2 class="text-xs uppercase tracking-wider text-indigo-300 mb-4">Menu Principal</h2>
<ul class="space-y-2">
<li>
<a href="#" class="flex items-center space-x-2 p-2 rounded hover:bg-indigo-800">
<i class="fas fa-home w-5"></i>
<span>Tableau de bord</span>
</a>
</li>
<li>
<a href="#" class="flex items-center space-x-2 p-2 rounded hover:bg-indigo-800">
<i class="fas fa-project-diagram w-5"></i>
<span>Mes projets</span>
</a>
</li>
<li>
<a href="#" class="flex items-center space-x-2 p-2 rounded hover:bg-indigo-800">
<i class="fas fa-tasks w-5"></i>
<span>Mes tâches</span>
</a>
</li>
<li>
<a href="#" class="flex items-center space-x-2 p-2 rounded bg-indigo-800">
<i class="fas fa-calendar-alt w-5"></i>
<span>Calendrier</span>
</a>
</li>
</ul>
</div>
<div>
<h2 class="text-xs uppercase tracking-wider text-indigo-300 mb-4">Équipes</h2>
<ul class="space-y-2">
<li>
<a href="#" class="flex items-center space-x-2 p-2 rounded hover:bg-indigo-800">
<div class="w-5 h-5 rounded-full bg-green-500 flex items-center justify-center text-xs">D</div>
<span>Développement</span>
</a>
</li>
<li>
<a href="#" class="flex items-center space-x-2 p-2 rounded hover:bg-indigo-800">
<div class="w-5 h-5 rounded-full bg-blue-500 flex items-center justify-center text-xs">M</div>
<span>Marketing</span>
</a>
</li>
<li>
<a href="#" class="flex items-center space-x-2 p-2 rounded hover:bg-indigo-800">
<div class="w-5 h-5 rounded-full bg-purple-500 flex items-center justify-center text-xs">V</div>
<span>Ventes</span>
</a>
</li>
</ul>
</div>
</nav>
</div>
<!-- Main Content Area -->
<div class="flex-1 flex flex-col overflow-hidden">
<!-- Top Navigation Bar -->
<header class="bg-white border-b border-gray-200 flex items-center justify-between p-4">
<div class="flex items-center space-x-4">
<button class="md:hidden text-gray-600">
<i class="fas fa-bars text-xl"></i>
</button>
<h2 class="text-xl font-semibold text-gray-800">Calendrier</h2>
</div>
<div class="flex items-center space-x-4">
<div class="relative">
<button class="p-2 rounded-full hover:bg-gray-100">
<i class="fas fa-bell text-gray-600"></i>
<span class="absolute top-0 right-0 w-2 h-2 bg-red-500 rounded-full"></span>
</button>
</div>
<div class="relative">
<button class="flex items-center space-x-2">
<div class="w-8 h-8 rounded-full bg-indigo-500 flex items-center justify-center text-white">JD</div>
<span class="hidden md:inline">John Doe</span>
<i class="fas fa-chevron-down text-xs hidden md:inline"></i>
</button>
</div>
</div>
</header>
<!-- Main Content -->
<main class="flex-1 overflow-auto p-6 bg-gray-50">
<!-- Calendar Header -->
<div class="flex justify-between items-center mb-6">
<div>
<h1 class="text-2xl font-bold text-gray-800">Calendrier</h1>
<p class="text-gray-600">Visualisez vos événements et échéances</p>
</div>
<div class="flex space-x-2">
<button
class="bg-indigo-600 text-white px-4 py-2 rounded-md hover:bg-indigo-700 flex items-center space-x-2"
onclick="openModal('newEventModal')"
>
<i class="fas fa-plus"></i>
<span>Nouvel événement</span>
</button>
<div class="relative">
<button class="px-4 py-2 border border-gray-300 rounded-md hover:bg-gray-50 flex items-center space-x-2">
<span id="current-view">Mois</span>
<i class="fas fa-chevron-down text-xs"></i>
</button>
<div class="dropdown-menu hidden absolute right-0 mt-1 w-32 bg-white rounded-md shadow-lg z-10 border border-gray-200">
<div class="py-1">
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" onclick="changeView('day')">Jour</a>
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" onclick="changeView('week')">Semaine</a>
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" onclick="changeView('month')">Mois</a>
</div>
</div>
</div>
</div>
</div>
<!-- Calendar Navigation -->
<div class="bg-white p-4 rounded-lg shadow-sm border border-gray-100 mb-6 flex items-center justify-between">
<div class="flex items-center space-x-4">
<button class="p-2 rounded-full hover:bg-gray-100" onclick="navigateCalendar('prev')">
<i class="fas fa-chevron-left"></i>
</button>
<h2 class="text-xl font-semibold" id="calendar-title">Juillet 2023</h2>
<button class="p-2 rounded-full hover:bg-gray-100" onclick="navigateCalendar('next')">
<i class="fas fa-chevron-right"></i>
</button>
<button class="px-3 py-1 border border-gray-300 rounded-md text-sm hover:bg-gray-50" onclick="goToToday()">
Aujourd'hui
</button>
</div>
<div class="flex items-center space-x-2">
<div class="relative">
<button class="px-3 py-1 border border-gray-300 rounded-md text-sm hover:bg-gray-50 flex items-center space-x-1">
<i class="fas fa-filter"></i>
<span>Filtres</span>
</button>
</div>
</div>
</div>
<!-- Calendar View -->
<div id="calendar-view">
<!-- Month View (default) -->
<div class="bg-white rounded-lg shadow-sm border border-gray-100 overflow-hidden" id="month-view">
<!-- Weekday Headers -->
<div class="grid grid-cols-7 bg-gray-50 border-b border-gray-200">
<div class="py-2 text-center text-xs font-medium text-gray-500 uppercase">Dim</div>
<div class="py-2 text-center text-xs font-medium text-gray-500 uppercase">Lun</div>
<div class="py-2 text-center text-xs font-medium text-gray-500 uppercase">Mar</div>
<div class="py-2 text-center text-xs font-medium text-gray-500 uppercase">Mer</div>
<div class="py-2 text-center text-xs font-medium text-gray-500 uppercase">Jeu</div>
<div class="py-2 text-center text-xs font-medium text-gray-500 uppercase">Ven</div>
<div class="py-2 text-center text-xs font-medium text-gray-500 uppercase">Sam</div>
</div>
<!-- Calendar Grid -->
<div class="grid grid-cols-7" id="month-grid">
<!-- Days will be dynamically inserted here -->
</div>
</div>
<!-- Week View (hidden by default) -->
<div class="bg-white rounded-lg shadow-sm border border-gray-100 overflow-hidden hidden" id="week-view">
<div class="grid grid-cols-8">
<div class="bg-gray-50 border-r border-gray-200"></div>
<div class="bg-gray-50 py-2 text-center text-xs font-medium text-gray-500 uppercase border-b border-gray-200">Lundi</div>
<div class="bg-gray-50 py-2 text-center text-xs font-medium text-gray-500 uppercase border-b border-gray-200">Mardi</div>
<div class="bg-gray-50 py-2 text-center text-xs font-medium text-gray-500 uppercase border-b border-gray-200">Mercredi</div>
<div class="bg-gray-50 py-2 text-center text-xs font-medium text-gray-500 uppercase border-b border-gray-200">Jeudi</div>
<div class="bg-gray-50 py-2 text-center text-xs font-medium text-gray-500 uppercase border-b border-gray-200">Vendredi</div>
<div class="bg-gray-50 py-2 text-center text-xs font-medium text-gray-500 uppercase border-b border-gray-200">Samedi</div>
<div class="bg-gray-50 py-2 text-center text-xs font-medium text-gray-500 uppercase border-b border-gray-200">Dimanche</div>
</div>
<div class="grid grid-cols-8 divide-x divide-gray-200" id="week-grid">
<!-- Time slots will be dynamically inserted here -->
</div>
</div>
<!-- Day View (hidden by default) -->
<div class="bg-white rounded-lg shadow-sm border border-gray-100 overflow-hidden hidden" id="day-view">
<div class="grid grid-cols-1">
<div class="bg-gray-50 py-2 text-center text-sm font-medium text-gray-700 border-b border-gray-200" id="day-header">
Lundi 17 Juillet 2023
</div>
</div>
<div class="grid grid-cols-1 divide-y divide-gray-200" id="day-grid">
<!-- Time slots will be dynamically inserted here -->
</div>
</div>
</div>
<!-- Events List for Selected Day -->
<div class="mt-6 hidden" id="events-section">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-semibold text-gray-800" id="events-day-title">Événements pour le 17 Juillet 2023</h3>
<button
class="bg-indigo-600 text-white px-3 py-1 rounded-md hover:bg-indigo-700 flex items-center space-x-1 text-sm"
onclick="openModal('newEventModal')"
>
<i class="fas fa-plus text-xs"></i>
<span>Ajouter</span>
</button>
</div>
<div class="space-y-3" id="events-list">
<!-- Event cards will be dynamically inserted here -->
</div>
<div class="hidden flex-col items-center justify-center py-12 text-center" id="no-events">
<div class="w-20 h-20 bg-indigo-50 rounded-full flex items-center justify-center mb-4">
<i class="fas fa-calendar-day text-indigo-500 text-2xl"></i>
</div>
<h3 class="text-lg font-medium text-gray-900 mb-2">Aucun événement aujourd'hui</h3>
<p class="text-gray-500 max-w-md">Ajoutez un nouvel événement pour commencer à planifier votre journée.</p>
</div>
</div>
</main>
</div>
</div>
<!-- New Event Modal -->
<div class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden" id="newEventModal">
<div class="bg-white rounded-lg shadow-xl w-full max-w-md">
<div class="p-6">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-semibold">Nouvel événement</h3>
<button class="text-gray-400 hover:text-gray-500" onclick="closeModal('newEventModal')">
<i class="fas fa-times"></i>
</button>
</div>
<form id="eventForm">
<div class="mb-4">
<label class="block text-gray-700 text-sm font-medium mb-2" for="event-title">Titre*</label>
<input
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
id="event-title"
type="text"
placeholder="Ex: Réunion avec l'équipe"
required
>
</div>
<div class="grid grid-cols-2 gap-4 mb-4">
<div>
<label class="block text-gray-700 text-sm font-medium mb-2" for="event-start-date">Date de début*</label>
<input
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
id="event-start-date"
type="date"
required
>
</div>
<div>
<label class="block text-gray-700 text-sm font-medium mb-2" for="event-start-time">Heure</label>
<input
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
id="event-start-time"
type="time"
>
</div>
</div>
<div class="grid grid-cols-2 gap-4 mb-4">
<div>
<label class="block text-gray-700 text-sm font-medium mb-2" for="event-end-date">Date de fin</label>
<input
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
id="event-end-date"
type="date"
>
</div>
<div>
<label class="block text-gray-700 text-sm font-medium mb-2" for="event-end-time">Heure</label>
<input
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
id="event-end-time"
type="time"
>
</div>
</div>
<div class="mb-4">
<label class="block text-gray-700 text-sm font-medium mb-2" for="event-color">Couleur</label>
<div class="flex space-x-2">
<div class="w-6 h-6 rounded-full bg-indigo-500 cursor-pointer border-2 border-indigo-600" onclick="selectColor('indigo')"></div>
<div class="w-6 h-6 rounded-full bg-red-500 cursor-pointer border-2 border-white hover:border-gray-200" onclick="selectColor('red')"></div>
<div class="w-6 h-6 rounded-full bg-yellow-500 cursor-pointer border-2 border-white hover:border-gray-200" onclick="selectColor('yellow')"></div>
<div class="w-6 h-6 rounded-full bg-green-500 cursor-pointer border-2 border-white hover:border-gray-200" onclick="selectColor('green')"></div>
<div class="w-6 h-6 rounded-full bg-blue-500 cursor-pointer border-2 border-white hover:border-gray-200" onclick="selectColor('blue')"></div>
<div class="w-6 h-6 rounded-full bg-purple-500 cursor-pointer border-2 border-white hover:border-gray-200" onclick="selectColor('purple')"></div>
</div>
<input type="hidden" id="event-color" value="indigo">
</div>
<div class="mb-4">
<label class="block text-gray-700 text-sm font-medium mb-2" for="event-description">Description</label>
<textarea
class="w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"
id="event-description"
rows="3"
placeholder="Détails de l'événement..."
></textarea>
</div>
<div class="flex justify-end space-x-3">
<button
type="button"
class="px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
onclick="closeModal('newEventModal')"
>
Annuler
</button>
<button
type="submit"
class="px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
>
Créer
</button>
</div>
</form>
</div>
</div>
</div>
<!-- JavaScript -->
<script>
// Mock data for events
const events = [
{
id: 1,
title: "Réunion d'équipe",
startDate: "2023-07-17",
startTime: "09:00",
endDate: "2023-07-17",
endTime: "10:30",
color: "indigo",
description: "Réunion hebdomadaire avec toute l'équipe pour faire le point sur les projets en cours.",
attendees: ["JD", "AL", "MP"]
},
{
id: 2,
title: "Déjeuner avec client",
startDate: "2023-07-18",
startTime: "12:30",
endDate: "2023-07-18",
endTime: "14:00",
color: "green",
description: "Déjeuner avec le client ABC Corp pour discuter du nouveau projet.",
attendees: ["JD", "TM"]
},
{
id: 3,
title: "Présentation projet",
startDate: "2023-07-20",
startTime: "14:00",
endDate: "2023-07-20",
endTime: "15:30",
color: "blue",
description: "Présentation du nouveau projet à la direction.",
attendees: ["JD", "AL", "SM"]
},
{
id: 4,
title: "Échéance rapport",
startDate: "2023-07-22",
color: "red",
description: "Dernier jour pour soumettre le rapport trimestriel.",
attendees: []
},
{
id: 5,
title: "Formation technique",
startDate: "2023-07-25",
startTime: "10:00",
endDate: "2023-07-25",
endTime: "17:00",
color: "purple",
description: "Journée de formation sur les nouvelles technologies.",
attendees: ["JD", "AL", "DL", "SM"]
}
];
// Calendar state
let currentDate = new Date();
let currentView = "month";
let selectedDate = new Date();
// DOM Ready
document.addEventListener('DOMContentLoaded', function() {
// Initialize calendar
renderCalendar();
// Handle form submission
document.getElementById('eventForm').addEventListener('submit', function(e) {
e.preventDefault();
createNewEvent();
});
// Initialize dropdown menus
initDropdowns();
// Set today's date in the event form
const today = new Date().toISOString().split('T')[0];
document.getElementById('event-start-date').value = today;
document.getElementById('event-end-date').value = today;
});
// Render calendar based on current view and date
function renderCalendar() {
updateCalendarTitle();
if (currentView === "month") {
renderMonthView();
document.getElementById('month-view').classList.remove('hidden');
document.getElementById('week-view').classList.add('hidden');
document.getElementById('day-view').classList.add('hidden');
} else if (currentView === "week") {
renderWeekView();
document.getElementById('month-view').classList.add('hidden');
document.getElementById('week-view').classList.remove('hidden');
document.getElementById('day-view').classList.add('hidden');
} else {
renderDayView();
document.getElementById('month-view').classList.add('hidden');
document.getElementById('week-view').classList.add('hidden');
document.getElementById('day-view').classList.remove('hidden');
}
// Show events for selected date if not in month view
if (currentView !== "month") {
showEventsForDate(selectedDate);
}
}
// Update the calendar title based on current view and date
function updateCalendarTitle() {
const titleElement = document.getElementById('calendar-title');
const options = { year: 'numeric', month: 'long' };
if (currentView === "month") {
titleElement.textContent = currentDate.toLocaleDateString('fr-FR', options);
} else if (currentView === "week") {
const weekStart = new Date(currentDate);
weekStart.setDate(currentDate.getDate() - currentDate.getDay() + 1);
const weekEnd = new Date(weekStart);
weekEnd.setDate(weekStart.getDate() + 6);
titleElement.textContent = `${weekStart.toLocaleDateString('fr-FR', { day: 'numeric', month: 'short' })} - ${weekEnd.toLocaleDateString('fr-FR', { day: 'numeric', month: 'short', year: 'numeric' })}`;
} else {
titleElement.textContent = selectedDate.toLocaleDateString('fr-FR', { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' });
}
}
// Render the month view
function renderMonthView() {
const monthGrid = document.getElementById('month-grid');
monthGrid.innerHTML = '';
const firstDayOfMonth = new Date(currentDate.getFullYear(), currentDate.getMonth(), 1);
const lastDayOfMonth = new Date(currentDate.getFullYear(), currentDate.getMonth() + 1, 0);
const startingDay = firstDayOfMonth.getDay();
const daysInMonth = lastDayOfMonth.getDate();
// Previous month's days
const prevMonthLastDay = new Date(currentDate.getFullYear(), currentDate.getMonth(), 0).getDate();
for (let i = 0; i < startingDay; i++) {
const dayElement = document.createElement('div');
dayElement.className = 'p-2 h-24 border border-gray-100 text-gray-400';
dayElement.textContent = prevMonthLastDay - startingDay + i + 1;
monthGrid.appendChild(dayElement);
}
// Current month's days
const today = new Date();
for (let i = 1; i <= daysInMonth; i++) {
const dayElement = document.createElement('div');
dayElement.className = 'p-2 h-24 border border-gray-100 relative calendar-day';
// Check if this day has events
const dayDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), i);
const hasEvents = events.some(event => {
const eventDate = new Date(event.startDate);
return eventDate.getDate() === dayDate.getDate() &&
eventDate.getMonth() === dayDate.getMonth() &&
eventDate.getFullYear() === dayDate.getFullYear();
});
if (hasEvents) {
dayElement.classList.add('has-events');
}
// Highlight today
if (dayDate.getDate() === today.getDate() &&
dayDate.getMonth() === today.getMonth() &&
dayDate.getFullYear() === today.getFullYear()) {
dayElement.classList.add('today');
}
// Highlight selected date
if (dayDate.getDate() === selectedDate.getDate() &&
dayDate.getMonth() === selectedDate.getMonth() &&
dayDate.getFullYear() === selectedDate.getFullYear()) {
dayElement.classList.add('selected');
}
dayElement.innerHTML = `
<div class="text-right font-medium">${i}</div>
<div class="mt-1 space-y-1 overflow-y-auto max-h-16">
${renderDayEvents(dayDate)}
</div>
`;
dayElement.addEventListener('click', () => {
selectedDate = dayDate;
renderMonthView();
showEventsForDate(selectedDate);
});
monthGrid.appendChild(dayElement);
}
// Next month's days
const daysLeft = 42 - (startingDay + daysInMonth);
for (let i = 1; i <= daysLeft; i++) {
const dayElement = document.createElement('div');
dayElement.className = 'p-2 h-24 border border-gray-100 text-gray-400';
dayElement.textContent = i;
monthGrid.appendChild(dayElement);
}
}
// Render events for a specific day in month view
function renderDayEvents(dayDate) {
const dayEvents = events.filter(event => {
const eventDate = new Date(event.startDate);
return eventDate.getDate() === dayDate.getDate() &&
eventDate.getMonth() === dayDate.getMonth() &&
eventDate.getFullYear() === dayDate.getFullYear();
});
if (dayEvents.length === 0) return '';
return dayEvents.slice(0, 2).map(event => {
let colorClass = 'bg-indigo-100 text-indigo-800';
if (event.color === 'red') colorClass = 'bg-red-100 text-red-800';
else if (event.color === 'yellow') colorClass = 'bg-yellow-100 text-yellow-800';
else if (event.color === 'green') colorClass = 'bg-green-100 text-green-800';
else if (event.color === 'blue') colorClass = 'bg-blue-100 text-blue-800';
else if (event.color === 'purple') colorClass = 'bg-purple-100 text-purple-800';
return `
<div class="text-xs p-1 rounded truncate ${colorClass}">
${event.startTime ? event.startTime + ' ' : ''}${event.title}
</div>
`;
}).join('');
}
// Render the week view
function renderWeekView() {
const weekGrid = document.getElementById('week-grid');
weekGrid.innerHTML = '';
// Time column
const timeColumn = document.createElement('div');
timeColumn.className = 'bg-gray-50 border-r border-gray-200';
for (let hour = 8; hour < 20; hour++) {
const timeSlot = document.createElement('div');
timeSlot.className = 'h-16 border-b border-gray-200 relative';
timeSlot.innerHTML = `
<span class="absolute top-0 left-1 text-xs text-gray-500 -mt-2">${hour}:00</span>
`;
timeColumn.appendChild(timeSlot);
}
weekGrid.appendChild(timeColumn);
// Week days
const weekStart = new Date(currentDate);
weekStart.setDate(currentDate.getDate() - currentDate.getDay() + 1);
for (let i = 0; i < 7; i++) {
const dayDate = new Date(weekStart);
dayDate.setDate(weekStart.getDate() + i);
const dayColumn = document.createElement('div');
dayColumn.className = 'relative';
// Highlight today
const today = new Date();
if (dayDate.getDate() === today.getDate() &&
dayDate.getMonth() === today.getMonth() &&
dayDate.getFullYear() === today.getFullYear()) {
dayColumn.classList.add('bg-blue-50');
}
// Highlight selected date
if (dayDate.getDate() === selectedDate.getDate() &&
dayDate.getMonth() === selectedDate.getMonth() &&
dayDate.getFullYear() === selectedDate.getFullYear()) {
dayColumn.classList.add('bg-indigo-50');
}
for (let hour = 8; hour < 20; hour++) {
const timeSlot = document.createElement('div');
timeSlot.className = 'h-16 border-b border-gray-200 relative';
// Add events to time slots
const slotStart = new Date(dayDate);
slotStart.setHours(hour, 0, 0, 0);
const slotEnd = new Date(dayDate);
slotEnd.setHours(hour + 1, 0, 0, 0);
const slotEvents = events.filter(event => {
if (!event.startTime) return false;
const eventStart = new Date(`${event.startDate}T${event.startTime}`);
return eventStart >= slotStart && eventStart < slotEnd;
});
if (slotEvents.length > 0) {
slotEvents.forEach(event => {
const eventElement = document.createElement('div');
eventElement.className = 'absolute top-0 left-0 right-0 mx-1 mt-1 p-1 text-xs rounded overflow-hidden';
let colorClass = 'bg-indigo-100 text-indigo-800 border-l-4 border-indigo-500';
if (event.color === 'red') colorClass = 'bg-red-100 text-red-800 border-l-4 border-red-500';
else if (event.color === 'yellow') colorClass = 'bg-yellow-100 text-yellow-800 border-l-4 border-yellow-500';
else if (event.color === 'green') colorClass = 'bg-green-100 text-green-800 border-l-4 border-green-500';
else if (event.color === 'blue') colorClass = 'bg-blue-100 text-blue-800 border-l-4 border-blue-500';
else if (event.color === 'purple') colorClass = 'bg-purple-100 text-purple-800 border-l-4 border-purple-500';
eventElement.className += ' ' + colorClass;
eventElement.textContent = event.title;
eventElement.title = `${event.title}\n${event.startTime} - ${event.endTime || ''}\n${event.description || ''}`;
timeSlot.appendChild(eventElement);
});
}
timeSlot.addEventListener('click', () => {
selectedDate = dayDate;
showEventsForDate(selectedDate);
// Open new event modal with pre-filled time
document.getElementById('event-start-date').value = dayDate.toISOString().split('T')[0];
document.getElementById('event-start-time').value = `${hour.toString().padStart(2, '0')}:00`;
const endHour = hour + 1;
document.getElementById('event-end-date').value = dayDate.toISOString().split('T')[0];
document.getElementById('event-end-time').value = `${endHour.toString().padStart(2, '0')}:00`;
openModal('newEventModal');
});
dayColumn.appendChild(timeSlot);
}
// Day header
const dayHeader = document.createElement('div');
dayHeader.className = 'text-center py-1 font-medium';
dayHeader.textContent = dayDate.toLocaleDateString('fr-FR', { weekday: 'short', day: 'numeric' });
dayHeader.addEventListener('click', () => {
selectedDate = dayDate;
changeView('day');
});
dayColumn.prepend(dayHeader);
weekGrid.appendChild(dayColumn);
}
}
// Render the day view
function renderDayView() {
const dayHeader = document.getElementById('day-header');
dayHeader.textContent = selectedDate.toLocaleDateString('fr-FR', { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' });
const dayGrid = document.getElementById('day-grid');
dayGrid.innerHTML = '';
for (let hour = 8; hour < 20; hour++) {
const timeSlot = document.createElement('div');
timeSlot.className = 'h-16 relative';
timeSlot.innerHTML = `
<div class="absolute top-0 left-0 w-16 text-right pr-2 text-xs text-gray-500 -mt-2">${hour}:00</div>
<div class="ml-16 h-full border-t border-gray-200"></div>
`;
// Add events to time slots
const slotStart = new Date(selectedDate);
slotStart.setHours(hour, 0, 0, 0);
const slotEnd = new Date(selectedDate);
slotEnd.setHours(hour + 1, 0, 0, 0);
const slotEvents = events.filter(event => {
if (!event.startTime) return false;
const eventStart = new Date(`${event.startDate}T${event.startTime}`);
return eventStart >= slotStart && eventStart < slotEnd;
});
if (slotEvents.length > 0) {
slotEvents.forEach(event => {
const eventElement = document.createElement('div');
eventElement.className = 'absolute top-0 left-16 right-0 mx-4 mt-1 p-2 text-sm rounded overflow-hidden event-card';
let colorClass = 'bg-indigo-100 text-indigo-800 border-l-4 border-indigo-500';
if (event.color === 'red') colorClass = 'bg-red-100 text-red-800 border-l-4 border-red-500';
else if (event.color === 'yellow') colorClass = 'bg-yellow-100 text-yellow-800 border-l-4 border-yellow-500';
else if (event.color === 'green') colorClass = 'bg-green-100 text-green-800 border-l-4 border-green-500';
else if (event.color === 'blue') colorClass = 'bg-blue-100 text-blue-800 border-l-4 border-blue-500';
else if (event.color === 'purple') colorClass = 'bg-purple-100 text-purple-800 border-l-4 border-purple-500';
eventElement.className += ' ' + colorClass;
const timeRange = event.endTime
? `${event.startTime} - ${event.endTime}`
: `${event.startTime}`;
eventElement.innerHTML = `
<div class="font-medium">${event.title}</div>
<div class="text-xs mt-1">${timeRange}</div>
${event.description ? `<div class="text-xs mt-1 text-gray-600">${event.description}</div>` : ''}
${event.attendees && event.attendees.length > 0 ? `
<div class="mt-2 flex -space-x-2">
${event.attendees.map(user =>
`<div class="w-6 h-6 rounded-full border-2 border-white bg-indigo-500 flex items-center justify-center text-white text-xs">${user}</div>`
).join('')}
</div>
` : ''}
`;
timeSlot.appendChild(eventElement);
});
}
timeSlot.addEventListener('click', () => {
// Open new event modal with pre-filled time
document.getElementById('event-start-date').value = selectedDate.toISOString().split('T')[0];
document.getElementById('event-start-time').value = `${hour.toString().padStart(2, '0')}:00`;
const endHour = hour + 1;
document.getElementById('event-end-date').value = selectedDate.toISOString().split('T')[0];
document.getElementById('event-end-time').value = `${endHour.toString().padStart(2, '0')}:00`;
openModal('newEventModal');
});
dayGrid.appendChild(timeSlot);
}
}
// Show events for a specific date
function showEventsForDate(date) {
const eventsSection = document.getElementById('events-section');
const eventsList = document.getElementById('events-list');
const noEvents = document.getElementById('no-events');
const eventsDayTitle = document.getElementById('events-day-title');
const dateEvents = events.filter(event => {
const eventDate = new Date(event.startDate);
return eventDate.getDate() === date.getDate() &&
eventDate.getMonth() === date.getMonth() &&
eventDate.getFullYear() === date.getFullYear();
});
eventsDayTitle.textContent = `Événements pour le ${date.toLocaleDateString('fr-FR', { weekday: 'long', day: 'numeric', month: 'long', year: 'numeric' })}`;
if (dateEvents.length === 0) {
eventsList.innerHTML = '';
noEvents.classList.remove('hidden');
} else {
noEvents.classList.add('hidden');
eventsList.innerHTML = '';
dateEvents.forEach(event => {
const eventCard = document.createElement('div');
eventCard.className = 'bg-white rounded-lg shadow-sm border border-gray-100 overflow-hidden event-card p-4';
let colorClass = 'bg-indigo-100 text-indigo-800';
if (event.color === 'red') colorClass = 'bg-red-100 text-red-800';
else if (event.color === 'yellow') colorClass = 'bg-yellow-100 text-yellow-800';
else if (event.color === 'green') colorClass = 'bg-green-100 text-green-800';
else if (event.color === 'blue') colorClass = 'bg-blue-100 text-blue-800';
else if (event.color === 'purple') colorClass = 'bg-purple-100 text-purple-800';
const timeRange = event.startTime
? (event.endTime
? `${event.startTime} - ${event.endTime}`
: `${event.startTime}`)
: 'Toute la journée';
eventCard.innerHTML = `
<div class="flex justify-between items-start">
<div>
<h3 class="font-medium text-gray-900">${event.title}</h3>
<div class="mt-1 flex items-center space-x-2">
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${colorClass}">
${timeRange}
</span>
</div>
</div>
<div class="dropdown relative">
<button class="text-gray-400 hover:text-gray-600">
<i class="fas fa-ellipsis-h"></i>
</button>
<div class="dropdown-menu hidden absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg z-10 border border-gray-200">
<div class="py-1">
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Modifier</a>
<a href="#" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">Supprimer</a>
</div>
</div>
</div>
</div>
${event.description ? `<p class="mt-2 text-sm text-gray-600">${event.description}</p>` : ''}
${event.attendees && event.attendees.length > 0 ? `
<div class="mt-4 flex items-center">
<span class="text-xs text-gray-500 mr-2">Participants :</span>
<div class="flex -space-x-2">
${event.attendees.map(user =>
`<div class="w-6 h-6 rounded-full border-2 border-white bg-indigo-500 flex items-center justify-center text-white text-xs">${user}</div>`
).join('')}
</div>
</div>
` : ''}
`;
eventsList.appendChild(eventCard);
});
}
eventsSection.classList.remove('hidden');
}
// Change calendar view
function changeView(view) {
currentView = view;
document.getElementById('current-view').textContent =
view === 'day' ? 'Jour' : view === 'week' ? 'Semaine' : 'Mois';
renderCalendar();
}
// Navigate calendar (previous/next month/week/day)
function navigateCalendar(direction) {
if (currentView === "month") {
if (direction === "prev") {
currentDate.setMonth(currentDate.getMonth() - 1);
} else {
currentDate.setMonth(currentDate.getMonth() + 1);
}
} else if (currentView === "week") {
if (direction === "prev") {
currentDate.setDate(currentDate.getDate() - 7);
} else {
currentDate.setDate(currentDate.getDate() + 7);
}
} else {
if (direction === "prev") {
currentDate.setDate(currentDate.getDate() - 1);
} else {
currentDate.setDate(currentDate.getDate() + 1);
}
selectedDate = new Date(currentDate);
}
renderCalendar();
}
// Go to today's date
function goToToday() {
currentDate = new Date();
selectedDate = new Date();
renderCalendar();
}
// Create a new event
function createNewEvent() {
const title = document.getElementById('event-title').value;
const startDate = document.getElementById('event-start-date').value;
const startTime = document.getElementById('event-start-time').value;
const endDate = document.getElementById('event-end-date').value;
const endTime = document.getElementById('event-end-time').value;
const color = document.getElementById('event-color').value;
const description = document.getElementById('event-description').value;
// Create a new event object
const newEvent = {
id: events.length + 1,
title: title,
startDate: startDate,
startTime: startTime || null,
endDate: endDate || startDate,
endTime: endTime || null,
color: color,
description: description || null,
attendees: ["JD"]
};
// Add to our events array (in a real app, this would be an API call)
events.push(newEvent);
// Reload calendar
renderCalendar();
// Close modal
closeModal('newEventModal');
// Reset form
document.getElementById('eventForm').reset();
document.getElementById('event-color').value = "indigo";
// Show events for the selected date
selectedDate = new Date(startDate);
showEventsForDate(selectedDate);
}
// Select color for new event
function selectColor(color) {
document.getElementById('event-color').value = color;
// Update UI to show selected color
document.querySelectorAll('#eventForm [class*="bg-"].border-indigo-600').forEach(el => {
el.classList.remove('border-indigo-600');
el.classList.add('border-white');
});
document.querySelector(`#eventForm .bg-${color}-500`).classList.remove('border-white');
document.querySelector(`#eventForm .bg-${color}-500`).classList.add('border-indigo-600');
}
// Initialize dropdown menus
function initDropdowns() {
document.querySelectorAll('.dropdown button').forEach(button => {
button.addEventListener('click', function(e) {
e.stopPropagation();
const dropdown = this.parentElement;
const menu = dropdown.querySelector('.dropdown-menu');
// Close all other dropdowns first
document.querySelectorAll('.dropdown-menu').forEach(m => {
if (m !== menu) m.classList.add('hidden');
});
// Toggle current dropdown
menu.classList.toggle('hidden');
});
});
// Close dropdowns when clicking elsewhere
document.addEventListener('click', function() {
document.querySelectorAll('.dropdown-menu').forEach(menu => {
menu.classList.add('hidden');
});
});
}
// Modal functions
function openModal(modalId) {
document.getElementById(modalId).classList.remove('hidden');
}
function closeModal(modalId) {
document.getElementById(modalId).classList.add('hidden');
}
</script>
</body>
</html>