|
|
<!DOCTYPE html> |
|
|
<html lang="it"> |
|
|
<head> |
|
|
<meta charset="UTF-8"> |
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
|
<title>TaskFlow - Gestione Attività</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> |
|
|
|
|
|
::-webkit-scrollbar { |
|
|
width: 8px; |
|
|
} |
|
|
::-webkit-scrollbar-track { |
|
|
background: #f1f1f1; |
|
|
} |
|
|
::-webkit-scrollbar-thumb { |
|
|
background: #888; |
|
|
border-radius: 4px; |
|
|
} |
|
|
::-webkit-scrollbar-thumb:hover { |
|
|
background: #555; |
|
|
} |
|
|
|
|
|
|
|
|
@keyframes fadeIn { |
|
|
from { opacity: 0; transform: translateY(10px); } |
|
|
to { opacity: 1; transform: translateY(0); } |
|
|
} |
|
|
|
|
|
.task-item { |
|
|
animation: fadeIn 0.3s ease-out forwards; |
|
|
} |
|
|
|
|
|
|
|
|
.current-day { |
|
|
background-color: #3b82f6; |
|
|
color: white; |
|
|
border-radius: 50%; |
|
|
} |
|
|
</style> |
|
|
</head> |
|
|
<body class="bg-gray-100 font-sans"> |
|
|
<div class="flex h-screen overflow-hidden"> |
|
|
|
|
|
<div class="hidden md:flex md:flex-shrink-0"> |
|
|
<div class="flex flex-col w-64 bg-indigo-700 text-white"> |
|
|
<div class="flex items-center justify-center h-16 px-4"> |
|
|
<div class="flex items-center"> |
|
|
<i class="fas fa-tasks text-2xl mr-2"></i> |
|
|
<span class="text-xl font-semibold">TaskFlow</span> |
|
|
</div> |
|
|
</div> |
|
|
<div class="flex flex-col flex-grow px-4 py-4 overflow-y-auto"> |
|
|
<div class="space-y-1"> |
|
|
<button id="all-tasks-btn" class="flex items-center w-full px-4 py-2 text-sm font-medium text-white bg-indigo-800 rounded-md group"> |
|
|
<i class="fas fa-list mr-3"></i> |
|
|
Tutte le attività |
|
|
</button> |
|
|
<button id="today-tasks-btn" class="flex items-center w-full px-4 py-2 text-sm font-medium text-indigo-100 hover:text-white hover:bg-indigo-600 rounded-md group"> |
|
|
<i class="fas fa-calendar-day mr-3"></i> |
|
|
Oggi |
|
|
</button> |
|
|
<button id="upcoming-tasks-btn" class="flex items-center w-full px-4 py-2 text-sm font-medium text-indigo-100 hover:text-white hover:bg-indigo-600 rounded-md group"> |
|
|
<i class="fas fa-calendar-week mr-3"></i> |
|
|
Prossimi |
|
|
</button> |
|
|
<button id="important-tasks-btn" class="flex items-center w-full px-4 py-2 text-sm font-medium text-indigo-100 hover:text-white hover:bg-indigo-600 rounded-md group"> |
|
|
<i class="fas fa-star mr-3"></i> |
|
|
Importanti |
|
|
</button> |
|
|
<button id="completed-tasks-btn" class="flex items-center w-full px-4 py-2 text-sm font-medium text-indigo-100 hover:text-white hover:bg-indigo-600 rounded-md group"> |
|
|
<i class="fas fa-check-circle mr-3"></i> |
|
|
Completate |
|
|
</button> |
|
|
</div> |
|
|
|
|
|
<div class="mt-8"> |
|
|
<h3 class="px-4 text-xs font-semibold text-indigo-200 uppercase tracking-wider">Categorie</h3> |
|
|
<div class="mt-2 space-y-1" id="categories-list"> |
|
|
|
|
|
</div> |
|
|
<div class="mt-2"> |
|
|
<button id="add-category-btn" class="flex items-center w-full px-4 py-2 text-sm font-medium text-indigo-100 hover:text-white hover:bg-indigo-600 rounded-md group"> |
|
|
<i class="fas fa-plus mr-3"></i> |
|
|
Aggiungi categoria |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="mt-auto mb-4"> |
|
|
<div class="flex items-center px-4 py-2 text-sm text-indigo-200"> |
|
|
<i class="fas fa-moon mr-3"></i> |
|
|
Modalità scura |
|
|
<label class="ml-auto relative inline-flex items-center cursor-pointer"> |
|
|
<input type="checkbox" id="dark-mode-toggle" class="sr-only peer"> |
|
|
<div class="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-indigo-300 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-indigo-600"></div> |
|
|
</label> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="md:hidden fixed bottom-0 left-0 right-0 bg-white shadow-lg z-10"> |
|
|
<div class="flex justify-around"> |
|
|
<button id="mobile-all-tasks" class="p-4 text-indigo-600"> |
|
|
<i class="fas fa-list text-xl"></i> |
|
|
</button> |
|
|
<button id="mobile-today-tasks" class="p-4 text-gray-500"> |
|
|
<i class="fas fa-calendar-day text-xl"></i> |
|
|
</button> |
|
|
<button id="mobile-add-task" class="p-4 text-gray-500"> |
|
|
<i class="fas fa-plus-circle text-2xl text-indigo-600"></i> |
|
|
</button> |
|
|
<button id="mobile-upcoming-tasks" class="p-4 text-gray-500"> |
|
|
<i class="fas fa-calendar-week text-xl"></i> |
|
|
</button> |
|
|
<button id="mobile-important-tasks" class="p-4 text-gray-500"> |
|
|
<i class="fas fa-star text-xl"></i> |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="flex flex-col flex-1 overflow-hidden"> |
|
|
|
|
|
<div class="flex items-center justify-between h-16 px-4 bg-white border-b border-gray-200"> |
|
|
<div class="flex items-center"> |
|
|
<button id="sidebar-toggle" class="md:hidden text-gray-500 focus:outline-none"> |
|
|
<i class="fas fa-bars text-xl"></i> |
|
|
</button> |
|
|
<h1 id="current-view" class="ml-4 text-lg font-semibold text-gray-900">Tutte le attività</h1> |
|
|
</div> |
|
|
<div class="flex items-center space-x-4"> |
|
|
<div class="relative"> |
|
|
<button id="search-btn" class="p-1 text-gray-500 hover:text-gray-700"> |
|
|
<i class="fas fa-search"></i> |
|
|
</button> |
|
|
<div id="search-container" class="hidden absolute right-0 mt-2 w-64 bg-white rounded-md shadow-lg z-10"> |
|
|
<input type="text" id="search-input" class="w-full px-4 py-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500" placeholder="Cerca attività..."> |
|
|
</div> |
|
|
</div> |
|
|
<button id="add-task-btn" class="flex items-center px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500"> |
|
|
<i class="fas fa-plus mr-2"></i> |
|
|
<span class="hidden md:inline">Nuova attività</span> |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="flex flex-1 overflow-hidden"> |
|
|
|
|
|
<div class="flex-1 overflow-y-auto p-4"> |
|
|
<div id="task-list-container"> |
|
|
<div id="no-tasks-message" class="flex flex-col items-center justify-center h-64 text-gray-500"> |
|
|
<i class="fas fa-tasks text-5xl mb-4"></i> |
|
|
<p class="text-xl">Nessuna attività trovata</p> |
|
|
<button id="add-first-task-btn" class="mt-4 px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700"> |
|
|
Aggiungi la tua prima attività |
|
|
</button> |
|
|
</div> |
|
|
<div id="task-list" class="space-y-3"> |
|
|
|
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div class="hidden lg:flex lg:flex-col w-80 border-l border-gray-200 bg-white"> |
|
|
<div class="p-4 border-b border-gray-200"> |
|
|
<h2 class="text-lg font-semibold">Calendario</h2> |
|
|
<div class="mt-4"> |
|
|
<div class="flex justify-between items-center mb-4"> |
|
|
<button id="prev-month" class="p-1 rounded-full hover:bg-gray-100"> |
|
|
<i class="fas fa-chevron-left"></i> |
|
|
</button> |
|
|
<h3 id="current-month" class="font-medium">Settembre 2023</h3> |
|
|
<button id="next-month" class="p-1 rounded-full hover:bg-gray-100"> |
|
|
<i class="fas fa-chevron-right"></i> |
|
|
</button> |
|
|
</div> |
|
|
<div id="calendar" class="grid grid-cols-7 gap-1"> |
|
|
|
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="flex-1 overflow-y-auto p-4"> |
|
|
<h2 class="text-lg font-semibold mb-4">Dettagli attività</h2> |
|
|
<div id="task-details" class="hidden"> |
|
|
<h3 id="task-detail-title" class="text-xl font-semibold mb-2"></h3> |
|
|
<div class="flex items-center text-sm text-gray-500 mb-4"> |
|
|
<i class="far fa-calendar-alt mr-2"></i> |
|
|
<span id="task-detail-date"></span> |
|
|
</div> |
|
|
<div class="mb-4"> |
|
|
<span id="task-detail-category" class="inline-block px-2 py-1 text-xs font-semibold rounded-full"></span> |
|
|
</div> |
|
|
<p id="task-detail-description" class="text-gray-700 mb-4"></p> |
|
|
<div class="flex space-x-2"> |
|
|
<button id="edit-task-btn" class="px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700"> |
|
|
Modifica |
|
|
</button> |
|
|
<button id="delete-task-btn" class="px-4 py-2 bg-red-600 text-white rounded-md hover:bg-red-700"> |
|
|
Elimina |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
<div id="no-task-selected" class="flex flex-col items-center justify-center h-64 text-gray-500"> |
|
|
<i class="fas fa-info-circle text-5xl mb-4"></i> |
|
|
<p class="text-center">Seleziona un'attività per visualizzare i dettagli</p> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="task-modal" class="fixed inset-0 z-50 hidden overflow-y-auto"> |
|
|
<div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> |
|
|
<div class="fixed inset-0 transition-opacity" aria-hidden="true"> |
|
|
<div class="absolute inset-0 bg-gray-500 opacity-75"></div> |
|
|
</div> |
|
|
|
|
|
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">​</span> |
|
|
|
|
|
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full"> |
|
|
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> |
|
|
<div class="sm:flex sm:items-start"> |
|
|
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left w-full"> |
|
|
<h3 id="modal-title" class="text-lg leading-6 font-medium text-gray-900 mb-4"> |
|
|
Nuova attività |
|
|
</h3> |
|
|
<form id="task-form" class="space-y-4"> |
|
|
<input type="hidden" id="task-id"> |
|
|
<div> |
|
|
<label for="task-title" class="block text-sm font-medium text-gray-700">Titolo</label> |
|
|
<input type="text" id="task-title" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500" required> |
|
|
</div> |
|
|
<div> |
|
|
<label for="task-description" class="block text-sm font-medium text-gray-700">Descrizione</label> |
|
|
<textarea id="task-description" rows="3" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"></textarea> |
|
|
</div> |
|
|
<div class="grid grid-cols-2 gap-4"> |
|
|
<div> |
|
|
<label for="task-date" class="block text-sm font-medium text-gray-700">Data</label> |
|
|
<input type="date" id="task-date" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"> |
|
|
</div> |
|
|
<div> |
|
|
<label for="task-time" class="block text-sm font-medium text-gray-700">Ora</label> |
|
|
<input type="time" id="task-time" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"> |
|
|
</div> |
|
|
</div> |
|
|
<div> |
|
|
<label for="task-category" class="block text-sm font-medium text-gray-700">Categoria</label> |
|
|
<div class="flex mt-1"> |
|
|
<select id="task-category" class="flex-1 border border-gray-300 rounded-l-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"> |
|
|
<option value="">Nessuna categoria</option> |
|
|
</select> |
|
|
<button type="button" id="new-category-btn" class="inline-flex items-center px-3 py-2 border border-l-0 border-gray-300 bg-gray-50 text-gray-500 hover:bg-gray-100 rounded-r-md"> |
|
|
<i class="fas fa-plus"></i> |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
<div class="flex items-center"> |
|
|
<input type="checkbox" id="task-important" class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded"> |
|
|
<label for="task-important" class="ml-2 block text-sm text-gray-700">Contrassegna come importante</label> |
|
|
</div> |
|
|
</form> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse"> |
|
|
<button type="button" id="save-task-btn" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-indigo-600 text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:ml-3 sm:w-auto sm:text-sm"> |
|
|
Salva |
|
|
</button> |
|
|
<button type="button" id="cancel-task-btn" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"> |
|
|
Annulla |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div id="category-modal" class="fixed inset-0 z-50 hidden overflow-y-auto"> |
|
|
<div class="flex items-center justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"> |
|
|
<div class="fixed inset-0 transition-opacity" aria-hidden="true"> |
|
|
<div class="absolute inset-0 bg-gray-500 opacity-75"></div> |
|
|
</div> |
|
|
|
|
|
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">​</span> |
|
|
|
|
|
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-md sm:w-full"> |
|
|
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4"> |
|
|
<div class="sm:flex sm:items-start"> |
|
|
<div class="mt-3 text-center sm:mt-0 sm:ml-4 sm:text-left w-full"> |
|
|
<h3 class="text-lg leading-6 font-medium text-gray-900 mb-4"> |
|
|
Nuova categoria |
|
|
</h3> |
|
|
<form id="category-form" class="space-y-4"> |
|
|
<div> |
|
|
<label for="category-name" class="block text-sm font-medium text-gray-700">Nome categoria</label> |
|
|
<input type="text" id="category-name" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500" required> |
|
|
</div> |
|
|
<div> |
|
|
<label for="category-color" class="block text-sm font-medium text-gray-700">Colore</label> |
|
|
<select id="category-color" class="mt-1 block w-full border border-gray-300 rounded-md shadow-sm py-2 px-3 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500"> |
|
|
<option value="bg-red-500 text-red-100">Rosso</option> |
|
|
<option value="bg-blue-500 text-blue-100">Blu</option> |
|
|
<option value="bg-green-500 text-green-100">Verde</option> |
|
|
<option value="bg-yellow-500 text-yellow-100">Giallo</option> |
|
|
<option value="bg-purple-500 text-purple-100">Viola</option> |
|
|
<option value="bg-pink-500 text-pink-100">Rosa</option> |
|
|
<option value="bg-indigo-500 text-indigo-100">Indaco</option> |
|
|
<option value="bg-gray-500 text-gray-100">Grigio</option> |
|
|
</select> |
|
|
</div> |
|
|
</form> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse"> |
|
|
<button type="button" id="save-category-btn" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-indigo-600 text-base font-medium text-white hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:ml-3 sm:w-auto sm:text-sm"> |
|
|
Salva |
|
|
</button> |
|
|
<button type="button" id="cancel-category-btn" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"> |
|
|
Annulla |
|
|
</button> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<script> |
|
|
|
|
|
const state = { |
|
|
tasks: [], |
|
|
categories: [ |
|
|
{ id: 1, name: "Lavoro", color: "bg-blue-500 text-blue-100" }, |
|
|
{ id: 2, name: "Personale", color: "bg-green-500 text-green-100" }, |
|
|
{ id: 3, name: "Studio", color: "bg-purple-500 text-purple-100" } |
|
|
], |
|
|
currentView: 'all', |
|
|
selectedTask: null, |
|
|
currentMonth: new Date().getMonth(), |
|
|
currentYear: new Date().getFullYear(), |
|
|
darkMode: false |
|
|
}; |
|
|
|
|
|
|
|
|
const elements = { |
|
|
|
|
|
sidebarToggle: document.getElementById('sidebar-toggle'), |
|
|
|
|
|
|
|
|
currentView: document.getElementById('current-view'), |
|
|
allTasksBtn: document.getElementById('all-tasks-btn'), |
|
|
todayTasksBtn: document.getElementById('today-tasks-btn'), |
|
|
upcomingTasksBtn: document.getElementById('upcoming-tasks-btn'), |
|
|
importantTasksBtn: document.getElementById('important-tasks-btn'), |
|
|
completedTasksBtn: document.getElementById('completed-tasks-btn'), |
|
|
|
|
|
|
|
|
mobileAllTasks: document.getElementById('mobile-all-tasks'), |
|
|
mobileTodayTasks: document.getElementById('mobile-today-tasks'), |
|
|
mobileUpcomingTasks: document.getElementById('mobile-upcoming-tasks'), |
|
|
mobileImportantTasks: document.getElementById('mobile-important-tasks'), |
|
|
mobileAddTask: document.getElementById('mobile-add-task'), |
|
|
|
|
|
|
|
|
taskListContainer: document.getElementById('task-list-container'), |
|
|
noTasksMessage: document.getElementById('no-tasks-message'), |
|
|
taskList: document.getElementById('task-list'), |
|
|
addFirstTaskBtn: document.getElementById('add-first-task-btn'), |
|
|
|
|
|
|
|
|
taskDetails: document.getElementById('task-details'), |
|
|
noTaskSelected: document.getElementById('no-task-selected'), |
|
|
taskDetailTitle: document.getElementById('task-detail-title'), |
|
|
taskDetailDate: document.getElementById('task-detail-date'), |
|
|
taskDetailCategory: document.getElementById('task-detail-category'), |
|
|
taskDetailDescription: document.getElementById('task-detail-description'), |
|
|
editTaskBtn: document.getElementById('edit-task-btn'), |
|
|
deleteTaskBtn: document.getElementById('delete-task-btn'), |
|
|
|
|
|
|
|
|
calendar: document.getElementById('calendar'), |
|
|
currentMonthDisplay: document.getElementById('current-month'), |
|
|
prevMonth: document.getElementById('prev-month'), |
|
|
nextMonth: document.getElementById('next-month'), |
|
|
|
|
|
|
|
|
searchBtn: document.getElementById('search-btn'), |
|
|
searchContainer: document.getElementById('search-container'), |
|
|
searchInput: document.getElementById('search-input'), |
|
|
|
|
|
|
|
|
addTaskBtn: document.getElementById('add-task-btn'), |
|
|
|
|
|
|
|
|
taskModal: document.getElementById('task-modal'), |
|
|
modalTitle: document.getElementById('modal-title'), |
|
|
taskForm: document.getElementById('task-form'), |
|
|
taskId: document.getElementById('task-id'), |
|
|
taskTitle: document.getElementById('task-title'), |
|
|
taskDescription: document.getElementById('task-description'), |
|
|
taskDate: document.getElementById('task-date'), |
|
|
taskTime: document.getElementById('task-time'), |
|
|
taskCategory: document.getElementById('task-category'), |
|
|
taskImportant: document.getElementById('task-important'), |
|
|
newCategoryBtn: document.getElementById('new-category-btn'), |
|
|
saveTaskBtn: document.getElementById('save-task-btn'), |
|
|
cancelTaskBtn: document.getElementById('cancel-task-btn'), |
|
|
|
|
|
|
|
|
categoriesList: document.getElementById('categories-list'), |
|
|
addCategoryBtn: document.getElementById('add-category-btn'), |
|
|
|
|
|
|
|
|
categoryModal: document.getElementById('category-modal'), |
|
|
categoryForm: document.getElementById('category-form'), |
|
|
categoryName: document.getElementById('category-name'), |
|
|
categoryColor: document.getElementById('category-color'), |
|
|
saveCategoryBtn: document.getElementById('save-category-btn'), |
|
|
cancelCategoryBtn: document.getElementById('cancel-category-btn'), |
|
|
|
|
|
|
|
|
darkModeToggle: document.getElementById('dark-mode-toggle') |
|
|
}; |
|
|
|
|
|
|
|
|
function init() { |
|
|
|
|
|
if (state.tasks.length === 0) { |
|
|
loadSampleTasks(); |
|
|
} |
|
|
|
|
|
|
|
|
setupEventListeners(); |
|
|
|
|
|
|
|
|
renderCategories(); |
|
|
renderTaskCategories(); |
|
|
renderCalendar(); |
|
|
renderTasks(); |
|
|
|
|
|
|
|
|
const today = new Date(); |
|
|
const formattedDate = today.toISOString().split('T')[0]; |
|
|
elements.taskDate.value = formattedDate; |
|
|
|
|
|
|
|
|
checkDarkModePreference(); |
|
|
} |
|
|
|
|
|
|
|
|
function loadSampleTasks() { |
|
|
const sampleTasks = [ |
|
|
{ |
|
|
id: 1, |
|
|
title: "Completare il progetto TaskFlow", |
|
|
description: "Implementare tutte le funzionalità principali del task manager", |
|
|
date: "2023-09-15", |
|
|
time: "15:00", |
|
|
category: 1, |
|
|
important: true, |
|
|
completed: false |
|
|
}, |
|
|
{ |
|
|
id: 2, |
|
|
title: "Fare la spesa settimanale", |
|
|
description: "Acquistare frutta, verdura e altri generi alimentari", |
|
|
date: "2023-09-10", |
|
|
time: "10:00", |
|
|
category: 2, |
|
|
important: false, |
|
|
completed: false |
|
|
}, |
|
|
{ |
|
|
id: 3, |
|
|
title: "Studiare per l'esame di JavaScript", |
|
|
description: "Rivedere i concetti avanzati di JavaScript e fare esercizi pratici", |
|
|
date: "2023-09-20", |
|
|
time: "14:00", |
|
|
category: 3, |
|
|
important: true, |
|
|
completed: false |
|
|
}, |
|
|
{ |
|
|
id: 4, |
|
|
title: "Incontrare il team di sviluppo", |
|
|
description: "Discutere i progressi del progetto e pianificare le prossime attività", |
|
|
date: "2023-09-12", |
|
|
time: "09:30", |
|
|
category: 1, |
|
|
important: true, |
|
|
completed: false |
|
|
}, |
|
|
{ |
|
|
id: 5, |
|
|
title: "Andare in palestra", |
|
|
description: "Allenamento completo di 1 ora", |
|
|
date: "2023-09-08", |
|
|
time: "18:00", |
|
|
category: 2, |
|
|
important: false, |
|
|
completed: true |
|
|
} |
|
|
]; |
|
|
|
|
|
state.tasks = sampleTasks; |
|
|
} |
|
|
|
|
|
|
|
|
function setupEventListeners() { |
|
|
|
|
|
elements.sidebarToggle.addEventListener('click', toggleSidebar); |
|
|
|
|
|
|
|
|
elements.allTasksBtn.addEventListener('click', () => switchView('all')); |
|
|
elements.todayTasksBtn.addEventListener('click', () => switchView('today')); |
|
|
elements.upcomingTasksBtn.addEventListener('click', () => switchView('upcoming')); |
|
|
elements.importantTasksBtn.addEventListener('click', () => switchView('important')); |
|
|
elements.completedTasksBtn.addEventListener('click', () => switchView('completed')); |
|
|
|
|
|
|
|
|
elements.mobileAllTasks.addEventListener('click', () => switchView('all')); |
|
|
elements.mobileTodayTasks.addEventListener('click', () => switchView('today')); |
|
|
elements.mobileUpcomingTasks.addEventListener('click', () => switchView('upcoming')); |
|
|
elements.mobileImportantTasks.addEventListener('click', () => switchView('important')); |
|
|
elements.mobileAddTask.addEventListener('click', openAddTaskModal); |
|
|
|
|
|
|
|
|
elements.addTaskBtn.addEventListener('click', openAddTaskModal); |
|
|
elements.addFirstTaskBtn.addEventListener('click', openAddTaskModal); |
|
|
|
|
|
|
|
|
elements.saveTaskBtn.addEventListener('click', saveTask); |
|
|
elements.cancelTaskBtn.addEventListener('click', closeTaskModal); |
|
|
elements.newCategoryBtn.addEventListener('click', openAddCategoryModal); |
|
|
|
|
|
|
|
|
elements.addCategoryBtn.addEventListener('click', openAddCategoryModal); |
|
|
elements.saveCategoryBtn.addEventListener('click', saveCategory); |
|
|
elements.cancelCategoryBtn.addEventListener('click', closeCategoryModal); |
|
|
|
|
|
|
|
|
elements.searchBtn.addEventListener('click', toggleSearch); |
|
|
elements.searchInput.addEventListener('input', handleSearch); |
|
|
|
|
|
|
|
|
elements.prevMonth.addEventListener('click', goToPreviousMonth); |
|
|
elements.nextMonth.addEventListener('click', goToNextMonth); |
|
|
|
|
|
|
|
|
elements.darkModeToggle.addEventListener('change', toggleDarkMode); |
|
|
} |
|
|
|
|
|
|
|
|
function toggleSidebar() { |
|
|
const sidebar = document.querySelector('.md\\:hidden'); |
|
|
if (sidebar) { |
|
|
sidebar.classList.toggle('hidden'); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function switchView(view) { |
|
|
state.currentView = view; |
|
|
|
|
|
|
|
|
updateActiveViewButton(); |
|
|
|
|
|
|
|
|
updateViewTitle(); |
|
|
|
|
|
|
|
|
renderTasks(); |
|
|
|
|
|
|
|
|
elements.searchContainer.classList.add('hidden'); |
|
|
} |
|
|
|
|
|
|
|
|
function updateActiveViewButton() { |
|
|
|
|
|
const viewButtons = [ |
|
|
elements.allTasksBtn, |
|
|
elements.todayTasksBtn, |
|
|
elements.upcomingTasksBtn, |
|
|
elements.importantTasksBtn, |
|
|
elements.completedTasksBtn |
|
|
]; |
|
|
|
|
|
viewButtons.forEach(button => { |
|
|
button.classList.remove('bg-indigo-800', 'text-white'); |
|
|
button.classList.add('text-indigo-100', 'hover:text-white', 'hover:bg-indigo-600'); |
|
|
}); |
|
|
|
|
|
|
|
|
let activeButton; |
|
|
switch (state.currentView) { |
|
|
case 'all': |
|
|
activeButton = elements.allTasksBtn; |
|
|
break; |
|
|
case 'today': |
|
|
activeButton = elements.todayTasksBtn; |
|
|
break; |
|
|
case 'upcoming': |
|
|
activeButton = elements.upcomingTasksBtn; |
|
|
break; |
|
|
case 'important': |
|
|
activeButton = elements.importantTasksBtn; |
|
|
break; |
|
|
case 'completed': |
|
|
activeButton = elements.completedTasksBtn; |
|
|
break; |
|
|
} |
|
|
|
|
|
if (activeButton) { |
|
|
activeButton.classList.remove('text-indigo-100', 'hover:text-white', 'hover:bg-indigo-600'); |
|
|
activeButton.classList.add('bg-indigo-800', 'text-white'); |
|
|
} |
|
|
|
|
|
|
|
|
const mobileButtons = [ |
|
|
elements.mobileAllTasks, |
|
|
elements.mobileTodayTasks, |
|
|
elements.mobileUpcomingTasks, |
|
|
elements.mobileImportantTasks |
|
|
]; |
|
|
|
|
|
mobileButtons.forEach(button => { |
|
|
button.classList.remove('text-indigo-600'); |
|
|
button.classList.add('text-gray-500'); |
|
|
}); |
|
|
|
|
|
let mobileActiveButton; |
|
|
switch (state.currentView) { |
|
|
case 'all': |
|
|
mobileActiveButton = elements.mobileAllTasks; |
|
|
break; |
|
|
case 'today': |
|
|
mobileActiveButton = elements.mobileTodayTasks; |
|
|
break; |
|
|
case 'upcoming': |
|
|
mobileActiveButton = elements.mobileUpcomingTasks; |
|
|
break; |
|
|
case 'important': |
|
|
mobileActiveButton = elements.mobileImportantTasks; |
|
|
break; |
|
|
} |
|
|
|
|
|
if (mobileActiveButton) { |
|
|
mobileActiveButton.classList.remove('text-gray-500'); |
|
|
mobileActiveButton.classList.add('text-indigo-600'); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function updateViewTitle() { |
|
|
let title; |
|
|
switch (state.currentView) { |
|
|
case 'all': |
|
|
title = 'Tutte le attività'; |
|
|
break; |
|
|
case 'today': |
|
|
title = 'Attività di oggi'; |
|
|
break; |
|
|
case 'upcoming': |
|
|
title = 'Prossime attività'; |
|
|
break; |
|
|
case 'important': |
|
|
title = 'Attività importanti'; |
|
|
break; |
|
|
case 'completed': |
|
|
title = 'Attività completate'; |
|
|
break; |
|
|
default: |
|
|
title = 'Tutte le attività'; |
|
|
} |
|
|
|
|
|
elements.currentView.textContent = title; |
|
|
} |
|
|
|
|
|
|
|
|
function renderTasks() { |
|
|
let filteredTasks = []; |
|
|
|
|
|
switch (state.currentView) { |
|
|
case 'all': |
|
|
filteredTasks = state.tasks.filter(task => !task.completed); |
|
|
break; |
|
|
case 'today': |
|
|
const today = new Date().toISOString().split('T')[0]; |
|
|
filteredTasks = state.tasks.filter(task => task.date === today && !task.completed); |
|
|
break; |
|
|
case 'upcoming': |
|
|
const todayDate = new Date(); |
|
|
todayDate.setHours(0, 0, 0, 0); |
|
|
filteredTasks = state.tasks.filter(task => { |
|
|
const taskDate = new Date(task.date); |
|
|
return taskDate >= todayDate && !task.completed; |
|
|
}); |
|
|
break; |
|
|
case 'important': |
|
|
filteredTasks = state.tasks.filter(task => task.important && !task.completed); |
|
|
break; |
|
|
case 'completed': |
|
|
filteredTasks = state.tasks.filter(task => task.completed); |
|
|
break; |
|
|
} |
|
|
|
|
|
|
|
|
filteredTasks.sort((a, b) => { |
|
|
const dateA = new Date(`${a.date}T${a.time || '00:00'}`); |
|
|
const dateB = new Date(`${b.date}T${b.time || '00:00'}`); |
|
|
return dateA - dateB; |
|
|
}); |
|
|
|
|
|
|
|
|
elements.taskList.innerHTML = ''; |
|
|
|
|
|
if (filteredTasks.length === 0) { |
|
|
elements.noTasksMessage.classList.remove('hidden'); |
|
|
elements.taskListContainer.classList.add('flex', 'items-center', 'justify-center'); |
|
|
} else { |
|
|
elements.noTasksMessage.classList.add('hidden'); |
|
|
elements.taskListContainer.classList.remove('flex', 'items-center', 'justify-center'); |
|
|
|
|
|
|
|
|
filteredTasks.forEach(task => { |
|
|
const taskElement = createTaskElement(task); |
|
|
elements.taskList.appendChild(taskElement); |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function createTaskElement(task) { |
|
|
const taskElement = document.createElement('div'); |
|
|
taskElement.className = 'task-item bg-white p-4 rounded-lg shadow hover:shadow-md transition-shadow cursor-pointer'; |
|
|
taskElement.dataset.taskId = task.id; |
|
|
|
|
|
|
|
|
const taskDate = new Date(task.date); |
|
|
const formattedDate = taskDate.toLocaleDateString('it-IT', { |
|
|
weekday: 'short', |
|
|
day: 'numeric', |
|
|
month: 'short' |
|
|
}); |
|
|
|
|
|
|
|
|
let timeHtml = ''; |
|
|
if (task.time) { |
|
|
timeHtml = `<span class="text-gray-500 ml-2">${task.time}</span>`; |
|
|
} |
|
|
|
|
|
|
|
|
let categoryHtml = ''; |
|
|
if (task.category) { |
|
|
const category = state.categories.find(cat => cat.id === task.category); |
|
|
if (category) { |
|
|
categoryHtml = `<span class="inline-block px-2 py-1 text-xs font-semibold rounded-full ${category.color}">${category.name}</span>`; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const importantIcon = task.important ? '<i class="fas fa-star text-yellow-500 mr-2"></i>' : ''; |
|
|
|
|
|
taskElement.innerHTML = ` |
|
|
<div class="flex items-start justify-between"> |
|
|
<div class="flex items-center"> |
|
|
<button class="complete-task-btn mr-3 p-1 rounded-full border border-gray-300 hover:bg-gray-100"> |
|
|
<i class="fas fa-check text-gray-400"></i> |
|
|
</button> |
|
|
<div> |
|
|
<h3 class="font-medium text-gray-900">${importantIcon}${task.title}</h3> |
|
|
<div class="flex items-center mt-1 text-sm text-gray-500"> |
|
|
<i class="far fa-calendar-alt mr-1"></i> |
|
|
<span>${formattedDate}</span> |
|
|
${timeHtml} |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<div> |
|
|
${categoryHtml} |
|
|
</div> |
|
|
</div> |
|
|
`; |
|
|
|
|
|
|
|
|
const completeBtn = taskElement.querySelector('.complete-task-btn'); |
|
|
completeBtn.addEventListener('click', (e) => { |
|
|
e.stopPropagation(); |
|
|
toggleTaskCompletion(task.id); |
|
|
}); |
|
|
|
|
|
taskElement.addEventListener('click', () => showTaskDetails(task.id)); |
|
|
|
|
|
return taskElement; |
|
|
} |
|
|
|
|
|
|
|
|
function toggleTaskCompletion(taskId) { |
|
|
const taskIndex = state.tasks.findIndex(task => task.id === taskId); |
|
|
if (taskIndex !== -1) { |
|
|
state.tasks[taskIndex].completed = !state.tasks[taskIndex].completed; |
|
|
renderTasks(); |
|
|
|
|
|
|
|
|
if (state.currentView === 'completed' && state.tasks[taskIndex].completed) { |
|
|
|
|
|
} else { |
|
|
|
|
|
const taskElement = document.querySelector(`.task-item[data-task-id="${taskId}"]`); |
|
|
if (taskElement) { |
|
|
taskElement.remove(); |
|
|
|
|
|
|
|
|
if (elements.taskList.children.length === 0) { |
|
|
elements.noTasksMessage.classList.remove('hidden'); |
|
|
elements.taskListContainer.classList.add('flex', 'items-center', 'justify-center'); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (state.selectedTask && state.selectedTask.id === taskId) { |
|
|
showTaskDetails(taskId); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function showTaskDetails(taskId) { |
|
|
const task = state.tasks.find(task => task.id === taskId); |
|
|
if (task) { |
|
|
state.selectedTask = task; |
|
|
|
|
|
|
|
|
const taskDate = new Date(task.date); |
|
|
const formattedDate = taskDate.toLocaleDateString('it-IT', { |
|
|
weekday: 'long', |
|
|
day: 'numeric', |
|
|
month: 'long', |
|
|
year: 'numeric' |
|
|
}); |
|
|
|
|
|
|
|
|
let timeHtml = ''; |
|
|
if (task.time) { |
|
|
timeHtml = ` alle ${task.time}`; |
|
|
} |
|
|
|
|
|
|
|
|
let categoryHtml = ''; |
|
|
if (task.category) { |
|
|
const category = state.categories.find(cat => cat.id === task.category); |
|
|
if (category) { |
|
|
categoryHtml = `<span class="${category.color}">${category.name}</span>`; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const importantIcon = task.important ? '<i class="fas fa-star text-yellow-500 mr-2"></i>' : ''; |
|
|
|
|
|
|
|
|
elements.taskDetailTitle.innerHTML = `${importantIcon}${task.title}`; |
|
|
elements.taskDetailDate.textContent = `${formattedDate}${timeHtml}`; |
|
|
|
|
|
if (categoryHtml) { |
|
|
elements.taskDetailCategory.innerHTML = categoryHtml; |
|
|
elements.taskDetailCategory.classList.remove('hidden'); |
|
|
} else { |
|
|
elements.taskDetailCategory.classList.add('hidden'); |
|
|
} |
|
|
|
|
|
elements.taskDetailDescription.textContent = task.description || 'Nessuna descrizione fornita'; |
|
|
|
|
|
|
|
|
elements.taskDetails.classList.remove('hidden'); |
|
|
elements.noTaskSelected.classList.add('hidden'); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function openAddTaskModal() { |
|
|
elements.modalTitle.textContent = 'Nuova attività'; |
|
|
elements.taskId.value = ''; |
|
|
elements.taskTitle.value = ''; |
|
|
elements.taskDescription.value = ''; |
|
|
|
|
|
|
|
|
const today = new Date(); |
|
|
const formattedDate = today.toISOString().split('T')[0]; |
|
|
elements.taskDate.value = formattedDate; |
|
|
|
|
|
elements.taskTime.value = ''; |
|
|
elements.taskCategory.value = ''; |
|
|
elements.taskImportant.checked = false; |
|
|
|
|
|
elements.taskModal.classList.remove('hidden'); |
|
|
} |
|
|
|
|
|
|
|
|
function openEditTaskModal(taskId) { |
|
|
const task = state.tasks.find(task => task.id === taskId); |
|
|
if (task) { |
|
|
elements.modalTitle.textContent = 'Modifica attività'; |
|
|
elements.taskId.value = task.id; |
|
|
elements.taskTitle.value = task.title; |
|
|
elements.taskDescription.value = task.description || ''; |
|
|
elements.taskDate.value = task.date; |
|
|
elements.taskTime.value = task.time || ''; |
|
|
elements.taskCategory.value = task.category || ''; |
|
|
elements.taskImportant.checked = task.important || false; |
|
|
|
|
|
elements.taskModal.classList.remove('hidden'); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function closeTaskModal() { |
|
|
elements.taskModal.classList.add('hidden'); |
|
|
} |
|
|
|
|
|
|
|
|
function saveTask() { |
|
|
const title = elements.taskTitle.value.trim(); |
|
|
if (!title) { |
|
|
alert('Il titolo è obbligatorio'); |
|
|
return; |
|
|
} |
|
|
|
|
|
const taskData = { |
|
|
id: elements.taskId.value ? parseInt(elements.taskId.value) : Date.now(), |
|
|
title: title, |
|
|
description: elements.taskDescription.value.trim(), |
|
|
date: elements.taskDate.value, |
|
|
time: elements.taskTime.value || null, |
|
|
category: elements.taskCategory.value ? parseInt(elements.taskCategory.value) : null, |
|
|
important: elements.taskImportant.checked, |
|
|
completed: false |
|
|
}; |
|
|
|
|
|
if (elements.taskId.value) { |
|
|
|
|
|
const taskIndex = state.tasks.findIndex(task => task.id === taskData.id); |
|
|
if (taskIndex !== -1) { |
|
|
state.tasks[taskIndex] = { |
|
|
...state.tasks[taskIndex], |
|
|
...taskData |
|
|
}; |
|
|
} |
|
|
} else { |
|
|
|
|
|
state.tasks.push(taskData); |
|
|
} |
|
|
|
|
|
closeTaskModal(); |
|
|
renderTasks(); |
|
|
|
|
|
|
|
|
if (state.selectedTask && state.selectedTask.id === taskData.id) { |
|
|
showTaskDetails(taskData.id); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function deleteTask() { |
|
|
if (state.selectedTask) { |
|
|
if (confirm('Sei sicuro di voler eliminare questa attività?')) { |
|
|
const taskId = state.selectedTask.id; |
|
|
state.tasks = state.tasks.filter(task => task.id !== taskId); |
|
|
|
|
|
|
|
|
const taskElement = document.querySelector(`.task-item[data-task-id="${taskId}"]`); |
|
|
if (taskElement) { |
|
|
taskElement.remove(); |
|
|
} |
|
|
|
|
|
|
|
|
elements.taskDetails.classList.add('hidden'); |
|
|
elements.noTaskSelected.classList.remove('hidden'); |
|
|
state.selectedTask = null; |
|
|
|
|
|
|
|
|
if (elements.taskList.children.length === 0) { |
|
|
elements.noTasksMessage.classList.remove('hidden'); |
|
|
elements.taskListContainer.classList.add('flex', 'items-center', 'justify-center'); |
|
|
} |
|
|
|
|
|
renderTasks(); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function renderCategories() { |
|
|
|
|
|
elements.categoriesList.innerHTML = ''; |
|
|
|
|
|
|
|
|
state.categories.forEach(category => { |
|
|
const categoryElement = document.createElement('button'); |
|
|
categoryElement.className = 'flex items-center w-full px-4 py-2 text-sm font-medium text-indigo-100 hover:text-white hover:bg-indigo-600 rounded-md group'; |
|
|
categoryElement.dataset.categoryId = category.id; |
|
|
categoryElement.innerHTML = ` |
|
|
<span class="inline-block w-3 h-3 rounded-full ${category.color.replace('text-', 'bg-').split(' ')[0]} mr-3"></span> |
|
|
${category.name} |
|
|
`; |
|
|
|
|
|
categoryElement.addEventListener('click', () => filterByCategory(category.id)); |
|
|
elements.categoriesList.appendChild(categoryElement); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
function renderTaskCategories() { |
|
|
|
|
|
elements.taskCategory.innerHTML = '<option value="">Nessuna categoria</option>'; |
|
|
|
|
|
|
|
|
state.categories.forEach(category => { |
|
|
const option = document.createElement('option'); |
|
|
option.value = category.id; |
|
|
option.textContent = category.name; |
|
|
elements.taskCategory.appendChild(option); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
function filterByCategory(categoryId) { |
|
|
state.currentView = 'category'; |
|
|
state.selectedCategory = categoryId; |
|
|
|
|
|
|
|
|
const category = state.categories.find(cat => cat.id === categoryId); |
|
|
elements.currentView.textContent = `Categoria: ${category.name}`; |
|
|
|
|
|
|
|
|
renderTasksByCategory(categoryId); |
|
|
|
|
|
|
|
|
updateActiveViewButton(); |
|
|
} |
|
|
|
|
|
|
|
|
function renderTasksByCategory(categoryId) { |
|
|
const filteredTasks = state.tasks.filter(task => |
|
|
task.category === categoryId && !task.completed |
|
|
); |
|
|
|
|
|
|
|
|
elements.taskList.innerHTML = ''; |
|
|
|
|
|
if (filteredTasks.length === 0) { |
|
|
elements.noTasksMessage.classList.remove('hidden'); |
|
|
elements.taskListContainer.classList.add('flex', 'items-center', 'justify-center'); |
|
|
} else { |
|
|
elements.noTasksMessage.classList.add('hidden'); |
|
|
elements.taskListContainer.classList.remove('flex', 'items-center', 'justify-center'); |
|
|
|
|
|
|
|
|
filteredTasks.forEach(task => { |
|
|
const taskElement = createTaskElement(task); |
|
|
elements.taskList.appendChild(taskElement); |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function openAddCategoryModal() { |
|
|
elements.categoryName.value = ''; |
|
|
elements.categoryColor.value = 'bg-blue-500 text-blue-100'; |
|
|
elements.categoryModal.classList.remove('hidden'); |
|
|
|
|
|
|
|
|
closeTaskModal(); |
|
|
} |
|
|
|
|
|
|
|
|
function closeCategoryModal() { |
|
|
elements.categoryModal.classList.add('hidden'); |
|
|
} |
|
|
|
|
|
|
|
|
function saveCategory() { |
|
|
const name = elements.categoryName.value.trim(); |
|
|
if (!name) { |
|
|
alert('Il nome della categoria è obbligatorio'); |
|
|
return; |
|
|
} |
|
|
|
|
|
const categoryData = { |
|
|
id: Date.now(), |
|
|
name: name, |
|
|
color: elements.categoryColor.value |
|
|
}; |
|
|
|
|
|
state.categories.push(categoryData); |
|
|
|
|
|
closeCategoryModal(); |
|
|
renderCategories(); |
|
|
renderTaskCategories(); |
|
|
} |
|
|
|
|
|
|
|
|
function toggleSearch() { |
|
|
elements.searchContainer.classList.toggle('hidden'); |
|
|
if (!elements.searchContainer.classList.contains('hidden')) { |
|
|
elements.searchInput.focus(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function handleSearch() { |
|
|
const query = elements.searchInput.value.toLowerCase(); |
|
|
|
|
|
if (query === '') { |
|
|
renderTasks(); |
|
|
return; |
|
|
} |
|
|
|
|
|
const filteredTasks = state.tasks.filter(task => |
|
|
task.title.toLowerCase().includes(query) || |
|
|
(task.description && task.description.toLowerCase().includes(query)) |
|
|
); |
|
|
|
|
|
|
|
|
elements.taskList.innerHTML = ''; |
|
|
|
|
|
if (filteredTasks.length === 0) { |
|
|
elements.noTasksMessage.classList.remove('hidden'); |
|
|
elements.taskListContainer.classList.add('flex', 'items-center', 'justify-center'); |
|
|
} else { |
|
|
elements.noTasksMessage.classList.add('hidden'); |
|
|
elements.taskListContainer.classList.remove('flex', 'items-center', 'justify-center'); |
|
|
|
|
|
|
|
|
filteredTasks.forEach(task => { |
|
|
const taskElement = createTaskElement(task); |
|
|
elements.taskList.appendChild(taskElement); |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function renderCalendar() { |
|
|
|
|
|
elements.calendar.innerHTML = ''; |
|
|
|
|
|
|
|
|
const monthNames = ['Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio', 'Giugno', 'Luglio', 'Agosto', 'Settembre', 'Ottobre', 'Novembre', 'Dicembre']; |
|
|
elements.currentMonthDisplay.textContent = `${monthNames[state.currentMonth]} ${state.currentYear}`; |
|
|
|
|
|
|
|
|
const firstDay = new Date(state.currentYear, state.currentMonth, 1).getDay(); |
|
|
const daysInMonth = new Date(state.currentYear, state.currentMonth + 1, 0).getDate(); |
|
|
|
|
|
|
|
|
const adjustedFirstDay = firstDay === 0 ? 6 : firstDay - 1; |
|
|
|
|
|
|
|
|
const dayNames = ['Lun', 'Mar', 'Mer', 'Gio', 'Ven', 'Sab', 'Dom']; |
|
|
dayNames.forEach(day => { |
|
|
const dayElement = document.createElement('div'); |
|
|
dayElement.className = 'text-center text-sm font-medium text-gray-500 py-1'; |
|
|
dayElement.textContent = day; |
|
|
elements.calendar.appendChild(dayElement); |
|
|
}); |
|
|
|
|
|
|
|
|
for (let i = 0; i < adjustedFirstDay; i++) { |
|
|
const emptyElement = document.createElement('div'); |
|
|
emptyElement.className = 'h-8'; |
|
|
elements.calendar.appendChild(emptyElement); |
|
|
} |
|
|
|
|
|
|
|
|
const today = new Date(); |
|
|
const currentDay = today.getDate(); |
|
|
const currentMonth = today.getMonth(); |
|
|
const currentYear = today.getFullYear(); |
|
|
|
|
|
for (let day = 1; day <= daysInMonth; day++) { |
|
|
const dayElement = document.createElement('div'); |
|
|
dayElement.className = 'text-center py-1 cursor-pointer hover:bg-gray-100'; |
|
|
|
|
|
|
|
|
if (day === currentDay && state.currentMonth === currentMonth && state.currentYear === currentYear) { |
|
|
dayElement.classList.add('current-day'); |
|
|
} |
|
|
|
|
|
dayElement.textContent = day; |
|
|
|
|
|
|
|
|
dayElement.addEventListener('click', () => { |
|
|
filterByDate(day); |
|
|
}); |
|
|
|
|
|
elements.calendar.appendChild(dayElement); |
|
|
} |
|
|
|
|
|
|
|
|
state.tasks.forEach(task => { |
|
|
if (!task.completed) { |
|
|
const taskDate = new Date(task.date); |
|
|
if (taskDate.getMonth() === state.currentMonth && taskDate.getFullYear() === state.currentYear) { |
|
|
const day = taskDate.getDate(); |
|
|
const dayElement = elements.calendar.children[adjustedFirstDay + day + 6]; |
|
|
|
|
|
if (dayElement) { |
|
|
const indicator = document.createElement('div'); |
|
|
indicator.className = 'w-1 h-1 mx-auto rounded-full bg-indigo-500'; |
|
|
dayElement.appendChild(indicator); |
|
|
} |
|
|
} |
|
|
} |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
function filterByDate(day) { |
|
|
const date = new Date(state.currentYear, state.currentMonth, day); |
|
|
const formattedDate = date.toISOString().split('T')[0]; |
|
|
|
|
|
state.currentView = 'date'; |
|
|
elements.currentView.textContent = `Attività per ${day}/${state.currentMonth + 1}/${state.currentYear}`; |
|
|
|
|
|
const filteredTasks = state.tasks.filter(task => |
|
|
task.date === formattedDate && !task.completed |
|
|
); |
|
|
|
|
|
|
|
|
elements.taskList.innerHTML = ''; |
|
|
|
|
|
if (filteredTasks.length === 0) { |
|
|
elements.noTasksMessage.classList.remove('hidden'); |
|
|
elements.taskListContainer.classList.add('flex', 'items-center', 'justify-center'); |
|
|
} else { |
|
|
elements.noTasksMessage.classList.add('hidden'); |
|
|
elements.taskListContainer.classList.remove('flex', 'items-center', 'justify-center'); |
|
|
|
|
|
|
|
|
filteredTasks.forEach(task => { |
|
|
const taskElement = createTaskElement(task); |
|
|
elements.taskList.appendChild(taskElement); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
updateActiveViewButton(); |
|
|
} |
|
|
|
|
|
|
|
|
function goToPreviousMonth() { |
|
|
if (state.currentMonth === 0) { |
|
|
state.currentMonth = 11; |
|
|
state.currentYear--; |
|
|
} else { |
|
|
state.currentMonth--; |
|
|
} |
|
|
renderCalendar(); |
|
|
} |
|
|
|
|
|
|
|
|
function goToNextMonth() { |
|
|
if (state.currentMonth === 11) { |
|
|
state.currentMonth = 0; |
|
|
state.currentYear++; |
|
|
} else { |
|
|
state.currentMonth++; |
|
|
} |
|
|
renderCalendar(); |
|
|
} |
|
|
|
|
|
|
|
|
function checkDarkModePreference() { |
|
|
const darkModePref = localStorage.getItem('darkMode') === 'true'; |
|
|
if (darkModePref) { |
|
|
enableDarkMode(); |
|
|
} else { |
|
|
disableDarkMode(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function toggleDarkMode() { |
|
|
if (state.darkMode) { |
|
|
disableDarkMode(); |
|
|
} else { |
|
|
enableDarkMode(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
function enableDarkMode() { |
|
|
document.documentElement.classList.add('dark'); |
|
|
localStorage.setItem('darkMode', 'true'); |
|
|
state.darkMode = true; |
|
|
elements.darkModeToggle.checked = true; |
|
|
} |
|
|
|
|
|
|
|
|
function disableDarkMode() { |
|
|
document.documentElement.classList.remove('dark'); |
|
|
localStorage.setItem('darkMode', 'false'); |
|
|
state.darkMode = false; |
|
|
elements.darkModeToggle.checked = false; |
|
|
} |
|
|
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', init); |
|
|
|
|
|
|
|
|
document.addEventListener('click', function(e) { |
|
|
if (e.target.id === 'edit-task-btn') { |
|
|
openEditTaskModal(state.selectedTask.id); |
|
|
} else if (e.target.id === 'delete-task-btn') { |
|
|
deleteTask(); |
|
|
} |
|
|
}); |
|
|
</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=federi/taskflow" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
|
|
</html> |