Affichage des noms des collaborateurs en congé dans le calendrier.
Ajout du champ "Autre" dans le formulaire de demande de congé. Mise en place de l’export du calendrier aux formats ICS et CSV.
This commit is contained in:
@@ -57,9 +57,9 @@ const Calendar = () => {
|
||||
// Fallback avec quelques jours fériés fixes si l'API échoue
|
||||
return [
|
||||
{ date: new Date(year, 0, 1), name: 'Jour de l\'An' },
|
||||
{ date: new Date(year, 4, 1), name: 'Fête du Travail' },
|
||||
{ date: new Date(year, 5, 1), name: 'Fête du Travail' },
|
||||
{ date: new Date(year, 6, 14), name: 'Fête Nationale' },
|
||||
{ date: new Date(year, 11, 25), name: 'Noël' }
|
||||
{ date: new Date(year, 12, 25), name: 'Noël' }
|
||||
];
|
||||
} finally {
|
||||
setIsLoadingHolidays(false);
|
||||
@@ -298,6 +298,89 @@ const Calendar = () => {
|
||||
|
||||
const days = getDaysInMonth(currentDate);
|
||||
|
||||
// Export CSV
|
||||
const exportTeamLeavesToGoogleCSV = (teamLeaves) => {
|
||||
const csvRows = [
|
||||
['Subject', 'Start Date', 'Start Time', 'End Date', 'End Time', 'All Day Event', 'Description', 'Location', 'Private'],
|
||||
...teamLeaves.map(leave => {
|
||||
// Conversion date YYYY-MM-DD -> MM/DD/YYYY
|
||||
const formatDate = dateStr => {
|
||||
const [year, month, day] = dateStr.split('-');
|
||||
return `${month}/${day}/${year}`;
|
||||
};
|
||||
|
||||
return [
|
||||
`Congé - ${leave.employee_name}`, // Subject
|
||||
formatDate(leave.start_date), // Start Date
|
||||
'', // Start Time (vide car congé toute la journée)
|
||||
formatDate(leave.end_date), // End Date
|
||||
'', // End Time
|
||||
'TRUE', // All Day Event
|
||||
`Type de congé: ${leave.type}`, // Description
|
||||
'', // Location vide
|
||||
'TRUE' // Privé
|
||||
];
|
||||
})
|
||||
];
|
||||
|
||||
const csvContent = csvRows.map(row => row.join(',')).join('\n');
|
||||
|
||||
// Ajouter BOM UTF-8 pour Excel/Google Sheets
|
||||
const BOM = '\uFEFF';
|
||||
const blob = new Blob([BOM + csvContent], { type: 'text/csv;charset=utf-8;' });
|
||||
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.setAttribute('href', url);
|
||||
link.setAttribute('download', 'conges_equipe_googleagenda.csv');
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Export ICS
|
||||
const exportTeamLeavesToICS = (teamLeaves) => {
|
||||
const ICS_HEADER =
|
||||
`BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
PRODID:-//TonEntreprise//CongesEquipe//FR
|
||||
CALSCALE:GREGORIAN`;
|
||||
|
||||
const ICS_FOOTER = 'END:VCALENDAR';
|
||||
|
||||
const events = teamLeaves.map(leave => {
|
||||
// Format date YYYYMMDD
|
||||
const startDate = leave.start_date.replace(/-/g, '');
|
||||
const endDate = leave.end_date.replace(/-/g, '');
|
||||
|
||||
return `BEGIN:VEVENT
|
||||
SUMMARY:Congé - ${leave.employee_name}
|
||||
DTSTART;VALUE=DATE:${startDate}
|
||||
DTEND;VALUE=DATE:${endDate}
|
||||
DESCRIPTION:Type de congé: ${leave.type}
|
||||
STATUS:CONFIRMED
|
||||
END:VEVENT`;
|
||||
}).join('\n');
|
||||
|
||||
const icsContent = `${ICS_HEADER}\n${events}\n${ICS_FOOTER}`;
|
||||
|
||||
// Blob pour téléchargement
|
||||
const blob = new Blob([icsContent], { type: 'text/calendar;charset=utf-8;' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = 'conges_equipe.ics';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
};
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 flex">
|
||||
<Sidebar isOpen={sidebarOpen} onToggle={() => setSidebarOpen(!sidebarOpen)} />
|
||||
@@ -410,8 +493,8 @@ const Calendar = () => {
|
||||
<div
|
||||
key={index}
|
||||
className={`
|
||||
min-h-[60px] lg:min-h-[80px] p-1 lg:p-2 text-center cursor-pointer rounded-lg transition-colors relative flex flex-col
|
||||
${!date ? '' :
|
||||
min-h-[60px] lg:min-h-[80px] p-1 lg:p-2 text-center cursor-pointer rounded-lg transition-colors relative flex flex-col
|
||||
${!date ? '' :
|
||||
isPastDate(date) ? 'bg-gray-200 text-gray-500 cursor-not-allowed opacity-60' :
|
||||
isHoliday(date) ? 'bg-red-100 text-red-800 cursor-not-allowed border border-red-200' :
|
||||
isToday(date) ? 'bg-blue-100 text-blue-800 font-semibold' :
|
||||
@@ -419,28 +502,58 @@ const Calendar = () => {
|
||||
hasLeave(date) ? 'bg-green-100 text-green-800' :
|
||||
'hover:bg-gray-50'
|
||||
}
|
||||
${isSelectingRange && selectedDate && !selectedEndDate && date && date > selectedDate && !isPastDate(date) && !isHoliday(date) ? 'bg-blue-50' : ''}
|
||||
`}
|
||||
${isSelectingRange && selectedDate && !selectedEndDate && date && date > selectedDate && !isPastDate(date) && !isHoliday(date) ? 'bg-blue-50' : ''}
|
||||
`}
|
||||
onClick={() => handleDateClick(date)}
|
||||
onContextMenu={(e) => handleContextMenu(e, date)}
|
||||
title={isHoliday(date) ? getHolidayName(date) : ''}
|
||||
>
|
||||
{date && (
|
||||
<div className="flex flex-col items-center justify-center h-full">
|
||||
<div className="flex flex-col items-center justify-between h-full">
|
||||
{/* Date number */}
|
||||
<span className="text-xs lg:text-sm">{date.getDate()}</span>
|
||||
|
||||
|
||||
{isHoliday(date) && getHolidayName(date) && (
|
||||
<span className="text-xs text-red-700 font-medium mt-1 text-center leading-tight hidden lg:block">
|
||||
{getHolidayName(date).length > 8 ? getHolidayName(date).substring(0, 8) + '...' : getHolidayName(date)}
|
||||
</span>
|
||||
<div className="text-[10px] text-red-700 font-medium mt-1 text-center leading-tight break-words">
|
||||
{getHolidayName(date)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
{/* Leave info: names of employees with leave */}
|
||||
{hasLeave(date) && (
|
||||
<div className="w-1.5 h-1.5 lg:w-2 lg:h-2 bg-green-500 rounded-full mt-1"></div>
|
||||
<div className="mt-1 flex flex-col items-center space-y-0.5 text-[10px] lg:text-xs text-green-700 text-center leading-tight">
|
||||
{teamLeaves
|
||||
.filter(leave => {
|
||||
const start = new Date(leave.start_date);
|
||||
const end = new Date(leave.end_date);
|
||||
return date >= start && date <= end;
|
||||
})
|
||||
.map((leave, i) => (
|
||||
<div
|
||||
key={i}
|
||||
title={`${leave.employee_name} - ${leave.type}`}
|
||||
className="flex items-center gap-1"
|
||||
>
|
||||
{/* Optional colored dot for leave type */}
|
||||
<span
|
||||
className="inline-block w-2 h-2 rounded-full"
|
||||
style={{ backgroundColor: leave.color || '#3B82F6' }}
|
||||
></span>
|
||||
<span className="text-[10px] lg:text-xs text-green-800 leading-tight break-words text-center">
|
||||
{leave.employee_name}
|
||||
</span>
|
||||
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Legend */}
|
||||
<div className="flex items-center gap-3 lg:gap-6 mt-6 pt-6 border-t border-gray-100 flex-wrap text-xs lg:text-sm">
|
||||
@@ -518,6 +631,19 @@ const Calendar = () => {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{/* button csv */}
|
||||
<div className="p-4">
|
||||
<div className="flex justify-end gap-2 mb-4">
|
||||
<button onClick={() => exportTeamLeavesToGoogleCSV(teamLeaves)} className="bg-blue-600 text-white px-3 py-2 rounded hover:bg-blue-700">
|
||||
Export CSV
|
||||
</button>
|
||||
<button onClick={() => exportTeamLeavesToICS(teamLeaves)} className="bg-green-600 text-white px-3 py-2 rounded hover:bg-green-700">
|
||||
Export ICS
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Ton rendu de calendrier ici */}
|
||||
</div>
|
||||
|
||||
{/* Modal nouvelle demande */}
|
||||
{showNewRequestModal && (
|
||||
|
||||
@@ -52,7 +52,12 @@ const Requests = () => {
|
||||
|
||||
// Filtre par type
|
||||
if (typeFilter !== 'all') {
|
||||
filtered = filtered.filter(request => request.type === typeFilter);
|
||||
if (typeFilter === 'Autres') {
|
||||
const otherTypes = ['Récup', 'Congés sans solde', 'Congés pour évènement familial', 'Congé maternité', 'Congé paternité', 'Congé parental', 'Congé parental à temps partiel'];
|
||||
filtered = filtered.filter(request => otherTypes.includes(request.type));
|
||||
} else {
|
||||
filtered = filtered.filter(request => request.type === typeFilter);
|
||||
}
|
||||
}
|
||||
|
||||
setFilteredRequests(filtered);
|
||||
@@ -84,7 +89,7 @@ const Requests = () => {
|
||||
};
|
||||
|
||||
const fetchAllRequests = async () => {
|
||||
console.log('🔍 Requests - Début fetchAllRequests pour user:', user?.id);
|
||||
console.log('Requests - Début fetchAllRequests pour user:', user?.id);
|
||||
|
||||
try {
|
||||
const url = `http://localhost/GTA/project/public/getRequests.php?user_id=${user.id}`;
|
||||
@@ -381,6 +386,7 @@ const Requests = () => {
|
||||
<option value="Congés payés">Congés payés</option>
|
||||
<option value="RTT">RTT</option>
|
||||
<option value="Congé maladie">Congé maladie</option>
|
||||
<option value="Autres">Autres types de congés</option> {/* Nouvelle option */}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user