Download index.html from federi/taskflow: direct link, hf CLI and curl.
- Browser
- Download file 63.9 kB
-
https://huggingface.co/spaces/federi/taskflow/resolve/main/index.html
- Command line
-
hf download hf://spaces/federi/taskflow/index.html
-
curl -L -o index.html https://huggingface.co/spaces/federi/taskflow/resolve/main/index.html
63.9 kB
| <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> | |
| /* Custom scrollbar */ | |
| ::-webkit-scrollbar { | |
| width: 8px; | |
| } | |
| ::-webkit-scrollbar-track { | |
| background: #f1f1f1; | |
| } | |
| ::-webkit-scrollbar-thumb { | |
| background: #888; | |
| border-radius: 4px; | |
| } | |
| ::-webkit-scrollbar-thumb:hover { | |
| background: #555; | |
| } | |
| /* Animation for task completion */ | |
| @keyframes fadeIn { | |
| from { opacity: 0; transform: translateY(10px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| .task-item { | |
| animation: fadeIn 0.3s ease-out forwards; | |
| } | |
| /* Calendar day highlight */ | |
| .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"> | |
| <!-- Sidebar --> | |
| <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"> | |
| <!-- Categories will be added dynamically --> | |
| </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> | |
| <!-- Mobile sidebar --> | |
| <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> | |
| <!-- Main content --> | |
| <div class="flex flex-col flex-1 overflow-hidden"> | |
| <!-- Top navigation --> | |
| <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> | |
| <!-- Content area --> | |
| <div class="flex flex-1 overflow-hidden"> | |
| <!-- Task list --> | |
| <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"> | |
| <!-- Tasks will be added dynamically --> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Calendar and task details (hidden on mobile) --> | |
| <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"> | |
| <!-- Calendar days will be added dynamically --> | |
| </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> | |
| <!-- Add/Edit Task Modal --> | |
| <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> | |
| <!-- Add Category Modal --> | |
| <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> | |
| // State management | |
| 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 | |
| }; | |
| // DOM Elements | |
| const elements = { | |
| // Sidebar | |
| sidebarToggle: document.getElementById('sidebar-toggle'), | |
| // Views | |
| 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'), | |
| // Mobile views | |
| 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'), | |
| // Task list | |
| taskListContainer: document.getElementById('task-list-container'), | |
| noTasksMessage: document.getElementById('no-tasks-message'), | |
| taskList: document.getElementById('task-list'), | |
| addFirstTaskBtn: document.getElementById('add-first-task-btn'), | |
| // Task details | |
| 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 | |
| calendar: document.getElementById('calendar'), | |
| currentMonthDisplay: document.getElementById('current-month'), | |
| prevMonth: document.getElementById('prev-month'), | |
| nextMonth: document.getElementById('next-month'), | |
| // Search | |
| searchBtn: document.getElementById('search-btn'), | |
| searchContainer: document.getElementById('search-container'), | |
| searchInput: document.getElementById('search-input'), | |
| // Add task | |
| addTaskBtn: document.getElementById('add-task-btn'), | |
| // Task modal | |
| 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'), | |
| // Categories | |
| categoriesList: document.getElementById('categories-list'), | |
| addCategoryBtn: document.getElementById('add-category-btn'), | |
| // Category modal | |
| 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'), | |
| // Dark mode | |
| darkModeToggle: document.getElementById('dark-mode-toggle') | |
| }; | |
| // Initialize the app | |
| function init() { | |
| // Load sample tasks if none exist | |
| if (state.tasks.length === 0) { | |
| loadSampleTasks(); | |
| } | |
| // Set up event listeners | |
| setupEventListeners(); | |
| // Render initial views | |
| renderCategories(); | |
| renderTaskCategories(); | |
| renderCalendar(); | |
| renderTasks(); | |
| // Set current date in date picker | |
| const today = new Date(); | |
| const formattedDate = today.toISOString().split('T')[0]; | |
| elements.taskDate.value = formattedDate; | |
| // Check for dark mode preference | |
| checkDarkModePreference(); | |
| } | |
| // Load sample tasks | |
| 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; | |
| } | |
| // Set up event listeners | |
| function setupEventListeners() { | |
| // Sidebar toggle | |
| elements.sidebarToggle.addEventListener('click', toggleSidebar); | |
| // View buttons | |
| 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')); | |
| // Mobile view buttons | |
| 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); | |
| // Add task buttons | |
| elements.addTaskBtn.addEventListener('click', openAddTaskModal); | |
| elements.addFirstTaskBtn.addEventListener('click', openAddTaskModal); | |
| // Task modal | |
| elements.saveTaskBtn.addEventListener('click', saveTask); | |
| elements.cancelTaskBtn.addEventListener('click', closeTaskModal); | |
| elements.newCategoryBtn.addEventListener('click', openAddCategoryModal); | |
| // Category modal | |
| elements.addCategoryBtn.addEventListener('click', openAddCategoryModal); | |
| elements.saveCategoryBtn.addEventListener('click', saveCategory); | |
| elements.cancelCategoryBtn.addEventListener('click', closeCategoryModal); | |
| // Search | |
| elements.searchBtn.addEventListener('click', toggleSearch); | |
| elements.searchInput.addEventListener('input', handleSearch); | |
| // Calendar navigation | |
| elements.prevMonth.addEventListener('click', goToPreviousMonth); | |
| elements.nextMonth.addEventListener('click', goToNextMonth); | |
| // Dark mode toggle | |
| elements.darkModeToggle.addEventListener('change', toggleDarkMode); | |
| } | |
| // Toggle sidebar on mobile | |
| function toggleSidebar() { | |
| const sidebar = document.querySelector('.md\\:hidden'); | |
| if (sidebar) { | |
| sidebar.classList.toggle('hidden'); | |
| } | |
| } | |
| // Switch between views | |
| function switchView(view) { | |
| state.currentView = view; | |
| // Update active button in sidebar | |
| updateActiveViewButton(); | |
| // Update current view title | |
| updateViewTitle(); | |
| // Render tasks for the selected view | |
| renderTasks(); | |
| // Close search if open | |
| elements.searchContainer.classList.add('hidden'); | |
| } | |
| // Update active view button in sidebar | |
| function updateActiveViewButton() { | |
| // Reset all buttons | |
| 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'); | |
| }); | |
| // Set active button | |
| 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'); | |
| } | |
| // Update mobile buttons | |
| 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'); | |
| } | |
| } | |
| // Update view title | |
| 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; | |
| } | |
| // Render tasks based on current view | |
| 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; | |
| } | |
| // Sort tasks by date and time | |
| 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; | |
| }); | |
| // Clear task list | |
| 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'); | |
| // Add tasks to the list | |
| filteredTasks.forEach(task => { | |
| const taskElement = createTaskElement(task); | |
| elements.taskList.appendChild(taskElement); | |
| }); | |
| } | |
| } | |
| // Create task element | |
| 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; | |
| // Format date | |
| const taskDate = new Date(task.date); | |
| const formattedDate = taskDate.toLocaleDateString('it-IT', { | |
| weekday: 'short', | |
| day: 'numeric', | |
| month: 'short' | |
| }); | |
| // Format time if exists | |
| let timeHtml = ''; | |
| if (task.time) { | |
| timeHtml = `<span class="text-gray-500 ml-2">${task.time}</span>`; | |
| } | |
| // Get category | |
| 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>`; | |
| } | |
| } | |
| // Important icon | |
| 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> | |
| `; | |
| // Add event listeners | |
| const completeBtn = taskElement.querySelector('.complete-task-btn'); | |
| completeBtn.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| toggleTaskCompletion(task.id); | |
| }); | |
| taskElement.addEventListener('click', () => showTaskDetails(task.id)); | |
| return taskElement; | |
| } | |
| // Toggle task completion | |
| 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 viewing completed tasks, keep the task in the list | |
| if (state.currentView === 'completed' && state.tasks[taskIndex].completed) { | |
| // Do nothing, task will stay in the list | |
| } else { | |
| // Otherwise, remove the task from the current view | |
| const taskElement = document.querySelector(`.task-item[data-task-id="${taskId}"]`); | |
| if (taskElement) { | |
| taskElement.remove(); | |
| // Show no tasks message if list is empty | |
| if (elements.taskList.children.length === 0) { | |
| elements.noTasksMessage.classList.remove('hidden'); | |
| elements.taskListContainer.classList.add('flex', 'items-center', 'justify-center'); | |
| } | |
| } | |
| } | |
| // Update task details if the task is selected | |
| if (state.selectedTask && state.selectedTask.id === taskId) { | |
| showTaskDetails(taskId); | |
| } | |
| } | |
| } | |
| // Show task details | |
| function showTaskDetails(taskId) { | |
| const task = state.tasks.find(task => task.id === taskId); | |
| if (task) { | |
| state.selectedTask = task; | |
| // Format date | |
| const taskDate = new Date(task.date); | |
| const formattedDate = taskDate.toLocaleDateString('it-IT', { | |
| weekday: 'long', | |
| day: 'numeric', | |
| month: 'long', | |
| year: 'numeric' | |
| }); | |
| // Format time if exists | |
| let timeHtml = ''; | |
| if (task.time) { | |
| timeHtml = ` alle ${task.time}`; | |
| } | |
| // Get category | |
| 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>`; | |
| } | |
| } | |
| // Important icon | |
| const importantIcon = task.important ? '<i class="fas fa-star text-yellow-500 mr-2"></i>' : ''; | |
| // Update task details | |
| 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'; | |
| // Show details and hide placeholder | |
| elements.taskDetails.classList.remove('hidden'); | |
| elements.noTaskSelected.classList.add('hidden'); | |
| } | |
| } | |
| // Open add task modal | |
| function openAddTaskModal() { | |
| elements.modalTitle.textContent = 'Nuova attività'; | |
| elements.taskId.value = ''; | |
| elements.taskTitle.value = ''; | |
| elements.taskDescription.value = ''; | |
| // Set today's date as default | |
| 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'); | |
| } | |
| // Open edit task modal | |
| 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'); | |
| } | |
| } | |
| // Close task modal | |
| function closeTaskModal() { | |
| elements.taskModal.classList.add('hidden'); | |
| } | |
| // Save task | |
| 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) { | |
| // Update existing task | |
| const taskIndex = state.tasks.findIndex(task => task.id === taskData.id); | |
| if (taskIndex !== -1) { | |
| state.tasks[taskIndex] = { | |
| ...state.tasks[taskIndex], | |
| ...taskData | |
| }; | |
| } | |
| } else { | |
| // Add new task | |
| state.tasks.push(taskData); | |
| } | |
| closeTaskModal(); | |
| renderTasks(); | |
| // If we're editing, update the task details | |
| if (state.selectedTask && state.selectedTask.id === taskData.id) { | |
| showTaskDetails(taskData.id); | |
| } | |
| } | |
| // Delete task | |
| 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); | |
| // Remove task from list | |
| const taskElement = document.querySelector(`.task-item[data-task-id="${taskId}"]`); | |
| if (taskElement) { | |
| taskElement.remove(); | |
| } | |
| // Hide task details | |
| elements.taskDetails.classList.add('hidden'); | |
| elements.noTaskSelected.classList.remove('hidden'); | |
| state.selectedTask = null; | |
| // Show no tasks message if list is empty | |
| if (elements.taskList.children.length === 0) { | |
| elements.noTasksMessage.classList.remove('hidden'); | |
| elements.taskListContainer.classList.add('flex', 'items-center', 'justify-center'); | |
| } | |
| renderTasks(); | |
| } | |
| } | |
| } | |
| // Render categories in sidebar and task form | |
| function renderCategories() { | |
| // Clear categories list | |
| elements.categoriesList.innerHTML = ''; | |
| // Add categories to sidebar | |
| 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); | |
| }); | |
| } | |
| // Render task categories in select dropdown | |
| function renderTaskCategories() { | |
| // Clear select | |
| elements.taskCategory.innerHTML = '<option value="">Nessuna categoria</option>'; | |
| // Add categories to select | |
| state.categories.forEach(category => { | |
| const option = document.createElement('option'); | |
| option.value = category.id; | |
| option.textContent = category.name; | |
| elements.taskCategory.appendChild(option); | |
| }); | |
| } | |
| // Filter tasks by category | |
| function filterByCategory(categoryId) { | |
| state.currentView = 'category'; | |
| state.selectedCategory = categoryId; | |
| // Update view title | |
| const category = state.categories.find(cat => cat.id === categoryId); | |
| elements.currentView.textContent = `Categoria: ${category.name}`; | |
| // Render tasks | |
| renderTasksByCategory(categoryId); | |
| // Update active buttons | |
| updateActiveViewButton(); | |
| } | |
| // Render tasks by category | |
| function renderTasksByCategory(categoryId) { | |
| const filteredTasks = state.tasks.filter(task => | |
| task.category === categoryId && !task.completed | |
| ); | |
| // Clear task list | |
| 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'); | |
| // Add tasks to the list | |
| filteredTasks.forEach(task => { | |
| const taskElement = createTaskElement(task); | |
| elements.taskList.appendChild(taskElement); | |
| }); | |
| } | |
| } | |
| // Open add category modal | |
| function openAddCategoryModal() { | |
| elements.categoryName.value = ''; | |
| elements.categoryColor.value = 'bg-blue-500 text-blue-100'; | |
| elements.categoryModal.classList.remove('hidden'); | |
| // Close task modal if open | |
| closeTaskModal(); | |
| } | |
| // Close category modal | |
| function closeCategoryModal() { | |
| elements.categoryModal.classList.add('hidden'); | |
| } | |
| // Save category | |
| 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(); | |
| } | |
| // Toggle search | |
| function toggleSearch() { | |
| elements.searchContainer.classList.toggle('hidden'); | |
| if (!elements.searchContainer.classList.contains('hidden')) { | |
| elements.searchInput.focus(); | |
| } | |
| } | |
| // Handle search | |
| 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)) | |
| ); | |
| // Clear task list | |
| 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'); | |
| // Add tasks to the list | |
| filteredTasks.forEach(task => { | |
| const taskElement = createTaskElement(task); | |
| elements.taskList.appendChild(taskElement); | |
| }); | |
| } | |
| } | |
| // Render calendar | |
| function renderCalendar() { | |
| // Clear calendar | |
| elements.calendar.innerHTML = ''; | |
| // Set month and year in header | |
| const monthNames = ['Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio', 'Giugno', 'Luglio', 'Agosto', 'Settembre', 'Ottobre', 'Novembre', 'Dicembre']; | |
| elements.currentMonthDisplay.textContent = `${monthNames[state.currentMonth]} ${state.currentYear}`; | |
| // Get first day of month and total days in month | |
| const firstDay = new Date(state.currentYear, state.currentMonth, 1).getDay(); | |
| const daysInMonth = new Date(state.currentYear, state.currentMonth + 1, 0).getDate(); | |
| // Adjust first day (Sunday = 0 in JavaScript, but we want Monday as first day) | |
| const adjustedFirstDay = firstDay === 0 ? 6 : firstDay - 1; | |
| // Add day names | |
| 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); | |
| }); | |
| // Add empty cells for days before the first day of the month | |
| for (let i = 0; i < adjustedFirstDay; i++) { | |
| const emptyElement = document.createElement('div'); | |
| emptyElement.className = 'h-8'; | |
| elements.calendar.appendChild(emptyElement); | |
| } | |
| // Add day cells | |
| 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'; | |
| // Highlight current day | |
| if (day === currentDay && state.currentMonth === currentMonth && state.currentYear === currentYear) { | |
| dayElement.classList.add('current-day'); | |
| } | |
| dayElement.textContent = day; | |
| // Add event listener | |
| dayElement.addEventListener('click', () => { | |
| filterByDate(day); | |
| }); | |
| elements.calendar.appendChild(dayElement); | |
| } | |
| // Add tasks indicators | |
| 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]; // +6 for day names | |
| if (dayElement) { | |
| const indicator = document.createElement('div'); | |
| indicator.className = 'w-1 h-1 mx-auto rounded-full bg-indigo-500'; | |
| dayElement.appendChild(indicator); | |
| } | |
| } | |
| } | |
| }); | |
| } | |
| // Filter by date | |
| 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 | |
| ); | |
| // Clear task list | |
| 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'); | |
| // Add tasks to the list | |
| filteredTasks.forEach(task => { | |
| const taskElement = createTaskElement(task); | |
| elements.taskList.appendChild(taskElement); | |
| }); | |
| } | |
| // Update active buttons | |
| updateActiveViewButton(); | |
| } | |
| // Go to previous month | |
| function goToPreviousMonth() { | |
| if (state.currentMonth === 0) { | |
| state.currentMonth = 11; | |
| state.currentYear--; | |
| } else { | |
| state.currentMonth--; | |
| } | |
| renderCalendar(); | |
| } | |
| // Go to next month | |
| function goToNextMonth() { | |
| if (state.currentMonth === 11) { | |
| state.currentMonth = 0; | |
| state.currentYear++; | |
| } else { | |
| state.currentMonth++; | |
| } | |
| renderCalendar(); | |
| } | |
| // Check dark mode preference | |
| function checkDarkModePreference() { | |
| const darkModePref = localStorage.getItem('darkMode') === 'true'; | |
| if (darkModePref) { | |
| enableDarkMode(); | |
| } else { | |
| disableDarkMode(); | |
| } | |
| } | |
| // Toggle dark mode | |
| function toggleDarkMode() { | |
| if (state.darkMode) { | |
| disableDarkMode(); | |
| } else { | |
| enableDarkMode(); | |
| } | |
| } | |
| // Enable dark mode | |
| function enableDarkMode() { | |
| document.documentElement.classList.add('dark'); | |
| localStorage.setItem('darkMode', 'true'); | |
| state.darkMode = true; | |
| elements.darkModeToggle.checked = true; | |
| } | |
| // Disable dark mode | |
| function disableDarkMode() { | |
| document.documentElement.classList.remove('dark'); | |
| localStorage.setItem('darkMode', 'false'); | |
| state.darkMode = false; | |
| elements.darkModeToggle.checked = false; | |
| } | |
| // Initialize the app when DOM is loaded | |
| document.addEventListener('DOMContentLoaded', init); | |
| // Add event listeners for task details buttons | |
| 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> |