task-manager-si / index.html
stephaneibens's picture
supprime aussi les catégories existantes - Follow Up Deployment
35ef0a1 verified
Raw History Blame Contribute Delete
37.4 kB
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Todo List Avancée</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>
/* Animation pour les notifications */
@keyframes slideIn {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
@keyframes fadeOut {
from { opacity: 1; }
to { opacity: 0; }
}
.notification {
animation: slideIn 0.3s ease-out;
}
.notification.hide {
animation: fadeOut 0.5s ease-out forwards;
}
/* Style personnalisé pour le datepicker */
input[type="datetime-local"]::-webkit-calendar-picker-indicator {
filter: invert(0.5);
cursor: pointer;
}
/* Style pour les tâches complétées */
.completed {
opacity: 0.7;
position: relative;
}
.completed::after {
content: "";
position: absolute;
left: 0;
top: 50%;
width: 100%;
height: 1px;
background: currentColor;
transform: translateY(-50%);
}
</style>
</head>
<body class="bg-gray-100 min-h-screen font-sans">
<div class="container mx-auto px-4 py-8 max-w-6xl">
<div class="flex flex-col md:flex-row gap-6">
<!-- Sidebar -->
<div class="w-full md:w-64 bg-white rounded-lg shadow-md p-4 h-fit">
<h1 class="text-2xl font-bold text-gray-800 mb-6 flex items-center gap-2">
<i class="fas fa-tasks"></i>
<span>Todo List</span>
</h1>
<div class="mb-6">
<h2 class="text-lg font-semibold text-gray-700 mb-2">Catégories</h2>
<ul id="categoriesList" class="space-y-1">
<li>
<button onclick="filterTasks('all')" class="w-full text-left px-3 py-2 rounded hover:bg-gray-100 flex items-center gap-2 text-blue-600">
<i class="fas fa-list"></i>
<span>Toutes les tâches</span>
</button>
</li>
</ul>
<div class="mt-4 flex gap-2">
<input type="text" id="newCategoryName" placeholder="Nouvelle catégorie" class="flex-1 px-3 py-2 border border-gray-300 rounded-md text-sm">
<button onclick="addNewCategory()" class="px-3 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 text-sm">
<i class="fas fa-plus"></i>
</button>
</div>
</div>
<div class="mb-6">
<h2 class="text-lg font-semibold text-gray-700 mb-2">Priorités</h2>
<ul class="space-y-1">
<li>
<button onclick="filterByPriority('all')" class="w-full text-left px-3 py-2 rounded hover:bg-gray-100 flex items-center gap-2">
<i class="fas fa-flag text-gray-400"></i>
<span>Toutes</span>
</button>
</li>
<li>
<button onclick="filterByPriority('high')" class="w-full text-left px-3 py-2 rounded hover:bg-gray-100 flex items-center gap-2">
<i class="fas fa-flag text-red-500"></i>
<span>Haute</span>
</button>
</li>
<li>
<button onclick="filterByPriority('medium')" class="w-full text-left px-3 py-2 rounded hover:bg-gray-100 flex items-center gap-2">
<i class="fas fa-flag text-yellow-500"></i>
<span>Moyenne</span>
</button>
</li>
<li>
<button onclick="filterByPriority('low')" class="w-full text-left px-3 py-2 rounded hover:bg-gray-100 flex items-center gap-2">
<i class="fas fa-flag text-green-500"></i>
<span>Basse</span>
</button>
</li>
</ul>
</div>
<div>
<h2 class="text-lg font-semibold text-gray-700 mb-2">Statut</h2>
<ul class="space-y-1">
<li>
<button onclick="filterByStatus('all')" class="w-full text-left px-3 py-2 rounded hover:bg-gray-100 flex items-center gap-2">
<i class="fas fa-circle text-gray-400"></i>
<span>Toutes</span>
</button>
</li>
<li>
<button onclick="filterByStatus('completed')" class="w-full text-left px-3 py-2 rounded hover:bg-gray-100 flex items-center gap-2">
<i class="fas fa-check-circle text-green-500"></i>
<span>Complétées</span>
</button>
</li>
<li>
<button onclick="filterByStatus('pending')" class="w-full text-left px-3 py-2 rounded hover:bg-gray-100 flex items-center gap-2">
<i class="fas fa-hourglass-half text-blue-500"></i>
<span>En attente</span>
</button>
</li>
<li>
<button onclick="filterByStatus('overdue')" class="w-full text-left px-3 py-2 rounded hover:bg-gray-100 flex items-center gap-2">
<i class="fas fa-exclamation-circle text-red-500"></i>
<span>En retard</span>
</button>
</li>
</ul>
</div>
</div>
<!-- Main Content -->
<div class="flex-1">
<!-- Add Task Form -->
<div class="bg-white rounded-lg shadow-md p-4 mb-6">
<h2 class="text-lg font-bold text-gray-800 mb-3">Ajouter une nouvelle tâche</h2>
<form id="addTaskForm" class="space-y-3">
<div>
<label for="taskTitle" class="block text-sm font-medium text-gray-700 mb-1">Titre de la tâche</label>
<input type="text" id="taskTitle" required class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<label for="taskCategory" class="block text-sm font-medium text-gray-700 mb-1">Catégorie</label>
<select id="taskCategory" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
<!-- Les catégories seront ajoutées dynamiquement -->
</select>
</div>
<div>
<label for="taskPriority" class="block text-sm font-medium text-gray-700 mb-1">Priorité</label>
<select id="taskPriority" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
<option value="high">Haute</option>
<option value="medium" selected>Moyenne</option>
<option value="low">Basse</option>
</select>
</div>
<div>
<label for="taskDueDate" class="block text-sm font-medium text-gray-700 mb-1">Date d'échéance</label>
<input type="datetime-local" id="taskDueDate" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
</div>
<div class="flex items-center">
<input type="checkbox" id="taskReminder" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded">
<label for="taskReminder" class="ml-2 block text-sm text-gray-700">Activer un rappel</label>
</div>
<div id="reminderOptions" class="hidden pl-6 border-l-2 border-gray-200 ml-2">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label for="reminderTime" class="block text-sm font-medium text-gray-700 mb-1">Temps avant rappel</label>
<select id="reminderTime" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
<option value="5">5 minutes avant</option>
<option value="15">15 minutes avant</option>
<option value="30" selected>30 minutes avant</option>
<option value="60">1 heure avant</option>
<option value="1440">1 jour avant</option>
</select>
</div>
<div>
<label for="reminderMethod" class="block text-sm font-medium text-gray-700 mb-1">Méthode de rappel</label>
<select id="reminderMethod" class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
<option value="notification">Notification</option>
<option value="email">Email</option>
</select>
</div>
</div>
</div>
<div class="flex justify-end">
<button type="submit" class="px-6 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2">
<i class="fas fa-plus mr-2"></i>Ajouter la tâche
</button>
</div>
</form>
</div>
<!-- Tasks List -->
<div class="bg-white rounded-lg shadow-md p-6">
<div class="flex justify-between items-center mb-6">
<h2 class="text-xl font-bold text-gray-800">Mes Tâches</h2>
<div class="flex items-center gap-2">
<span id="taskCount" class="text-sm text-gray-500">0 tâches</span>
<button onclick="clearCompletedTasks()" class="text-sm text-red-500 hover:text-red-700">
<i class="fas fa-trash-alt mr-1"></i>Effacer complétées
</button>
</div>
</div>
<div class="mb-4">
<input type="text" id="searchTask" placeholder="Rechercher des tâches..." class="w-full px-4 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
</div>
<div id="tasksList" class="space-y-3">
<!-- Les tâches seront ajoutées ici dynamiquement -->
<div class="text-center py-10 text-gray-500">
<i class="fas fa-tasks text-4xl mb-2"></i>
<p>Aucune tâche pour le moment</p>
<p class="text-sm">Ajoutez votre première tâche ci-dessus</p>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Notification Area -->
<div id="notificationArea" class="fixed bottom-4 right-4 space-y-2 w-80 z-50"></div>
<script>
// Variables globales
let tasks = JSON.parse(localStorage.getItem('tasks')) || [];
let categories = JSON.parse(localStorage.getItem('categories')) || [];
let currentFilter = { category: 'all', priority: 'all', status: 'all' };
let activeReminders = {};
// Initialisation
document.addEventListener('DOMContentLoaded', function() {
renderCategories();
renderTasks();
updateTaskCount();
setupReminderToggle();
setupSearch();
checkForOverdueTasks();
// Vérifier les rappels toutes les minutes
setInterval(checkReminders, 60000);
});
// Gestion du formulaire
document.getElementById('addTaskForm').addEventListener('submit', function(e) {
e.preventDefault();
const title = document.getElementById('taskTitle').value.trim();
const category = document.getElementById('taskCategory').value;
const priority = document.getElementById('taskPriority').value;
const dueDate = document.getElementById('taskDueDate').value;
const hasReminder = document.getElementById('taskReminder').checked;
const reminderTime = hasReminder ? parseInt(document.getElementById('reminderTime').value) : null;
const reminderMethod = hasReminder ? document.getElementById('reminderMethod').value : null;
if (!title) return;
const newTask = {
id: Date.now().toString(),
title,
description: '',
category,
priority,
dueDate: dueDate || null,
completed: false,
createdAt: new Date().toISOString(),
reminder: hasReminder ? {
time: reminderTime,
method: reminderMethod,
triggered: false
} : null
};
tasks.push(newTask);
saveTasks();
renderTasks();
updateTaskCount();
// Réinitialiser le formulaire
this.reset();
document.getElementById('reminderOptions').classList.add('hidden');
// Planifier le rappel si nécessaire
if (hasReminder && dueDate) {
scheduleReminder(newTask);
}
showNotification('Tâche ajoutée avec succès!', 'success');
});
// Fonction pour afficher les tâches
function renderTasks(filteredTasks = null) {
const tasksList = document.getElementById('tasksList');
const tasksToRender = filteredTasks || filterTasks();
if (tasksToRender.length === 0) {
tasksList.innerHTML = `
<div class="text-center py-10 text-gray-500">
<i class="fas fa-tasks text-4xl mb-2"></i>
<p>Aucune tâche trouvée</p>
<p class="text-sm">Essayez de modifier vos filtres</p>
</div>
`;
return;
}
tasksList.innerHTML = '';
tasksToRender.forEach(task => {
const taskElement = createTaskElement(task);
tasksList.appendChild(taskElement);
});
}
// Fonction pour créer un élément de tâche
function createTaskElement(task) {
const taskElement = document.createElement('div');
taskElement.className = `border rounded-lg p-4 ${task.completed ? 'bg-gray-50 completed' : 'bg-white'} ${isTaskOverdue(task) && !task.completed ? 'border-red-200' : 'border-gray-200'}`;
taskElement.dataset.id = task.id;
// Trouver la catégorie de la tâche
const taskCategory = categories.find(cat => cat.id === task.category);
const categoryIcon = taskCategory ? taskCategory.icon : 'ellipsis-h';
const categoryColor = taskCategory ? taskCategory.color : 'gray';
const categoryName = taskCategory ? taskCategory.name : 'Inconnue';
// Icône de priorité
const priorityIcons = {
high: 'flag',
medium: 'flag',
low: 'flag'
};
const priorityColors = {
high: 'red',
medium: 'yellow',
low: 'green'
};
// Date formatée
const dueDateFormatted = task.dueDate ? new Date(task.dueDate).toLocaleString('fr-FR', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit'
}) : 'Pas de date';
// Statut de la tâche
let statusText = '';
let statusColor = '';
if (task.completed) {
statusText = 'Complétée';
statusColor = 'green';
} else if (isTaskOverdue(task)) {
statusText = 'En retard';
statusColor = 'red';
} else if (task.dueDate) {
statusText = 'En cours';
statusColor = 'blue';
} else {
statusText = 'Sans date';
statusColor = 'gray';
}
taskElement.innerHTML = `
<div class="flex justify-between items-start mb-2">
<div class="flex items-center gap-2">
<input type="checkbox" ${task.completed ? 'checked' : ''} onchange="toggleTaskCompletion('${task.id}')" class="h-5 w-5 rounded border-gray-300 text-blue-600 focus:ring-blue-500">
<h3 class="font-medium ${task.completed ? 'line-through text-gray-500' : 'text-gray-800'}">${task.title}</h3>
</div>
<div class="flex items-center gap-2">
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-${priorityColors[task.priority]}-100 text-${priorityColors[task.priority]}-800">
<i class="fas fa-${priorityIcons[task.priority]} text-${priorityColors[task.priority]}-500 mr-1"></i>
${task.priority === 'high' ? 'Haute' : task.priority === 'medium' ? 'Moyenne' : 'Basse'}
</span>
<button onclick="deleteTask('${task.id}')" class="text-gray-400 hover:text-red-500">
<i class="fas fa-trash-alt"></i>
</button>
</div>
</div>
${task.description ? `<p class="text-sm text-gray-600 mb-3">${task.description}</p>` : ''}
<div class="flex flex-wrap items-center justify-between gap-2 text-sm">
<div class="flex items-center gap-2">
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-${categoryColor}-100 text-${categoryColor}-800">
<i class="fas fa-${categoryIcon} text-${categoryColor}-500 mr-1"></i>
${categoryName}
</span>
<span class="text-gray-500">
<i class="far fa-calendar-alt mr-1"></i>
${dueDateFormatted}
</span>
</div>
<div class="flex items-center gap-2">
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-${statusColor}-100 text-${statusColor}-800">
${statusText}
</span>
${task.reminder ? `
<span class="text-gray-500">
<i class="fas fa-bell ${task.reminder.triggered ? 'text-yellow-500' : ''} mr-1"></i>
Rappel ${task.reminder.time} min avant
</span>
` : ''}
</div>
</div>
`;
return taskElement;
}
// Fonction pour basculer l'état de complétion d'une tâche
function toggleTaskCompletion(taskId) {
const taskIndex = tasks.findIndex(task => task.id === taskId);
if (taskIndex !== -1) {
tasks[taskIndex].completed = !tasks[taskIndex].completed;
saveTasks();
renderTasks();
updateTaskCount();
if (tasks[taskIndex].completed) {
showNotification('Tâche marquée comme complétée!', 'success');
}
}
}
// Fonction pour supprimer une tâche
function deleteTask(taskId) {
if (confirm('Êtes-vous sûr de vouloir supprimer cette tâche?')) {
tasks = tasks.filter(task => task.id !== taskId);
saveTasks();
renderTasks();
updateTaskCount();
showNotification('Tâche supprimée avec succès!', 'success');
// Annuler tout rappel planifié pour cette tâche
if (activeReminders[taskId]) {
clearTimeout(activeReminders[taskId]);
delete activeReminders[taskId];
}
}
}
// Fonction pour effacer toutes les tâches complétées
function clearCompletedTasks() {
if (confirm('Êtes-vous sûr de vouloir supprimer toutes les tâches complétées?')) {
// Annuler les rappels pour les tâches complétées
tasks.forEach(task => {
if (task.completed && activeReminders[task.id]) {
clearTimeout(activeReminders[task.id]);
delete activeReminders[task.id];
}
});
tasks = tasks.filter(task => !task.completed);
saveTasks();
renderTasks();
updateTaskCount();
showNotification('Tâches complétées effacées!', 'success');
}
}
// Fonction pour filtrer les tâches par catégorie
function filterTasks(category = 'all') {
currentFilter.category = category;
return applyFilters();
}
// Fonction pour filtrer les tâches par priorité
function filterByPriority(priority = 'all') {
currentFilter.priority = priority;
return applyFilters();
}
// Fonction pour filtrer les tâches par statut
function filterByStatus(status = 'all') {
currentFilter.status = status;
return applyFilters();
}
// Fonction pour appliquer tous les filtres
function applyFilters() {
let filteredTasks = [...tasks];
// Filtrer par catégorie
if (currentFilter.category !== 'all') {
filteredTasks = filteredTasks.filter(task => task.category === currentFilter.category);
}
// Filtrer par priorité
if (currentFilter.priority !== 'all') {
filteredTasks = filteredTasks.filter(task => task.priority === currentFilter.priority);
}
// Filtrer par statut
if (currentFilter.status !== 'all') {
if (currentFilter.status === 'completed') {
filteredTasks = filteredTasks.filter(task => task.completed);
} else if (currentFilter.status === 'pending') {
filteredTasks = filteredTasks.filter(task => !task.completed && !isTaskOverdue(task));
} else if (currentFilter.status === 'overdue') {
filteredTasks = filteredTasks.filter(task => !task.completed && isTaskOverdue(task));
}
}
renderTasks(filteredTasks);
return filteredTasks;
}
// Fonction pour vérifier si une tâche est en retard
function isTaskOverdue(task) {
if (!task.dueDate || task.completed) return false;
return new Date(task.dueDate) < new Date();
}
// Fonction pour vérifier les tâches en retard au chargement
function checkForOverdueTasks() {
const overdueTasks = tasks.filter(task => isTaskOverdue(task));
if (overdueTasks.length > 0) {
showNotification(`Vous avez ${overdueTasks.length} tâche(s) en retard!`, 'warning');
}
}
// Fonction pour configurer la recherche
function setupSearch() {
const searchInput = document.getElementById('searchTask');
searchInput.addEventListener('input', function() {
const searchTerm = this.value.toLowerCase();
const filteredTasks = tasks.filter(task =>
task.title.toLowerCase().includes(searchTerm) ||
(task.description && task.description.toLowerCase().includes(searchTerm))
);
renderTasks(filteredTasks);
});
}
// Fonction pour configurer le toggle des rappels
function setupReminderToggle() {
const reminderCheckbox = document.getElementById('taskReminder');
const reminderOptions = document.getElementById('reminderOptions');
reminderCheckbox.addEventListener('change', function() {
if (this.checked) {
reminderOptions.classList.remove('hidden');
} else {
reminderOptions.classList.add('hidden');
}
});
}
// Fonction pour planifier un rappel
function scheduleReminder(task) {
if (!task.reminder || !task.dueDate) return;
const dueDate = new Date(task.dueDate);
const reminderTime = task.reminder.time * 60000; // Convertir en millisecondes
const reminderDate = new Date(dueDate.getTime() - reminderTime);
const now = new Date();
const timeUntilReminder = reminderDate.getTime() - now.getTime();
// Si le rappel est dans le futur
if (timeUntilReminder > 0) {
activeReminders[task.id] = setTimeout(() => {
triggerReminder(task);
}, timeUntilReminder);
}
}
// Fonction pour déclencher un rappel
function triggerReminder(task) {
// Marquer le rappel comme déclenché
const taskIndex = tasks.findIndex(t => t.id === task.id);
if (taskIndex !== -1 && tasks[taskIndex].reminder) {
tasks[taskIndex].reminder.triggered = true;
saveTasks();
renderTasks();
}
// Afficher la notification
const method = task.reminder.method;
const message = `Rappel: "${task.title}" est due à ${new Date(task.dueDate).toLocaleTimeString('fr-FR', {hour: '2-digit', minute:'2-digit'})}`;
if (method === 'notification') {
showNotification(message, 'info', 10000);
} else if (method === 'email') {
// En production, vous enverriez un vrai email ici
console.log(`Email envoyé: ${message}`);
showNotification(`Rappel envoyé par email: ${task.title}`, 'info');
}
// Supprimer le rappel des rappels actifs
delete activeReminders[task.id];
}
// Fonction pour vérifier périodiquement les rappels
function checkReminders() {
const now = new Date();
tasks.forEach(task => {
if (task.reminder && !task.reminder.triggered && task.dueDate) {
const dueDate = new Date(task.dueDate);
const reminderTime = task.reminder.time * 60000;
const reminderDate = new Date(dueDate.getTime() - reminderTime);
// Si le rappel devrait être déclenché maintenant
if (reminderDate <= now && dueDate > now) {
triggerReminder(task);
}
}
});
}
// Fonction pour afficher une notification
function showNotification(message, type = 'info', duration = 5000) {
const notificationArea = document.getElementById('notificationArea');
const notificationId = Date.now().toString();
const colors = {
success: 'bg-green-100 border-green-500 text-green-700',
error: 'bg-red-100 border-red-500 text-red-700',
warning: 'bg-yellow-100 border-yellow-500 text-yellow-700',
info: 'bg-blue-100 border-blue-500 text-blue-700'
};
const icons = {
success: 'fa-check-circle',
error: 'fa-exclamation-circle',
warning: 'fa-exclamation-triangle',
info: 'fa-info-circle'
};
const notification = document.createElement('div');
notification.id = `notification-${notificationId}`;
notification.className = `notification p-4 border-l-4 rounded-lg shadow-md ${colors[type]} flex items-start`;
notification.innerHTML = `
<i class="fas ${icons[type]} mt-1 mr-3"></i>
<div class="flex-1">
<p class="text-sm font-medium">${message}</p>
</div>
<button onclick="dismissNotification('${notificationId}')" class="ml-2 text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i>
</button>
`;
notificationArea.appendChild(notification);
// Fermer automatiquement après la durée spécifiée
setTimeout(() => {
dismissNotification(notificationId);
}, duration);
}
// Fonction pour fermer une notification
function dismissNotification(id) {
const notification = document.getElementById(`notification-${id}`);
if (notification) {
notification.classList.add('hide');
setTimeout(() => {
notification.remove();
}, 500);
}
}
// Fonction pour mettre à jour le compteur de tâches
function updateTaskCount() {
const totalTasks = tasks.length;
const completedTasks = tasks.filter(task => task.completed).length;
const pendingTasks = totalTasks - completedTasks;
document.getElementById('taskCount').textContent =
`${pendingTasks} en cours • ${completedTasks} complétées • ${totalTasks} au total`;
}
// Fonction pour sauvegarder les tâches dans le localStorage
function saveTasks() {
localStorage.setItem('tasks', JSON.stringify(tasks));
}
// Fonction pour sauvegarder les catégories
function saveCategories() {
localStorage.setItem('categories', JSON.stringify(categories));
}
// Fonction pour afficher les catégories
function renderCategories() {
const categoriesList = document.getElementById('categoriesList');
const taskCategorySelect = document.getElementById('taskCategory');
// Garder seulement le bouton "Toutes les tâches"
categoriesList.innerHTML = categoriesList.children[0].outerHTML;
taskCategorySelect.innerHTML = '';
// Ajouter les catégories personnalisées
categories.forEach(category => {
// Ajouter à la liste des catégories
const categoryItem = document.createElement('li');
categoryItem.innerHTML = `
<button onclick="filterTasks('${category.id}')" class="w-full text-left px-3 py-2 rounded hover:bg-gray-100 flex items-center gap-2">
<i class="fas fa-${category.icon} text-${category.color}-500"></i>
<span>${category.name}</span>
</button>
`;
categoriesList.appendChild(categoryItem);
// Ajouter au select du formulaire
const option = document.createElement('option');
option.value = category.id;
option.textContent = category.name;
taskCategorySelect.appendChild(option);
});
}
// Fonction pour ajouter une nouvelle catégorie
function addNewCategory() {
const nameInput = document.getElementById('newCategoryName');
const name = nameInput.value.trim();
if (!name) return;
// Générer un ID unique
const id = name.toLowerCase().replace(/\s+/g, '-');
// Vérifier si la catégorie existe déjà
if (categories.some(cat => cat.id === id)) {
showNotification('Cette catégorie existe déjà!', 'error');
return;
}
// Icônes et couleurs par défaut
const icons = ['briefcase', 'user', 'shopping-cart', 'home', 'utensils', 'book', 'heart', 'star'];
const colors = ['red', 'blue', 'green', 'purple', 'yellow', 'indigo', 'pink', 'gray'];
// Ajouter la nouvelle catégorie
categories.push({
id,
name,
icon: icons[Math.floor(Math.random() * icons.length)],
color: colors[Math.floor(Math.random() * colors.length)]
});
saveCategories();
renderCategories();
nameInput.value = '';
showNotification('Catégorie ajoutée avec succès!', 'success');
}
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=stephaneibens/task-manager-si" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>