todo-list / index.html
ffkj09's picture
Add 2 files
167b58d verified
Raw
History Blame Contribute Delete
61.4 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Todo List Admin Dashboard</title>
<script src="https://cdn.tailwindcss.com"></script>
<script>
tailwind.config = {
darkMode: 'class',
theme: {
extend: {
spacing: {
'128': '32rem',
'144': '36rem',
}
}
}
}
</script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.sidebar {
transition: all 0.3s ease;
width: 260px;
min-width: 260px;
}
.sidebar.collapsed {
width: 70px;
min-width: 70px;
}
.sidebar.collapsed .sidebar-text {
display: none;
}
.sidebar.collapsed .logo-text {
display: none;
}
.sidebar.collapsed .menu-item {
justify-content: center;
padding-left: 0;
padding-right: 0;
}
.sidebar.collapsed .menu-item i {
margin-right: 0;
}
.sidebar.collapsed .user-info {
flex-direction: column;
align-items: center;
padding: 0.5rem;
}
.sidebar.collapsed .user-avatar {
margin-right: 0;
margin-bottom: 0.5rem;
}
.sidebar.collapsed .user-details {
display: none;
}
.sidebar.collapsed .toggle-btn {
margin-left: 0;
justify-content: center;
}
.sidebar.collapsed .projects-title {
display: none;
}
.sidebar.collapsed .project-item span:last-child {
display: none;
}
.sidebar.collapsed .project-item {
justify-content: center;
}
.sidebar.collapsed .logout-btn span {
display: none;
}
.sidebar.collapsed .logout-btn {
justify-content: center;
}
.main-content {
transition: all 0.3s ease;
}
.main-content.expanded {
}
.todo-item:hover .todo-actions {
opacity: 1;
}
.todo-actions {
opacity: 0;
transition: opacity 0.2s ease;
}
.priority-high {
border-left: 4px solid #ef4444;
}
.priority-medium {
border-left: 4px solid #f59e0b;
}
.priority-low {
border-left: 4px solid #10b981;
}
/* Mobile menu styles */
.mobile-menu-button {
display: none;
}
@media (max-width: 1024px) {
.sidebar {
position: fixed;
z-index: 50;
height: 100vh;
transform: translateX(-100%);
transition: transform 0.3s ease;
}
.sidebar.open {
transform: translateX(0);
}
.mobile-menu-button {
display: block;
}
.main-content {
margin-left: 0;
}
.stats-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 640px) {
.stats-grid {
grid-template-columns: 1fr;
}
.header-actions {
display: none;
}
.mobile-header-actions {
display: flex;
}
.filter-controls {
flex-direction: column;
gap: 0.5rem;
}
.filter-controls > div {
width: 100%;
}
.task-modal-content {
width: 95%;
margin: 0.5rem auto;
}
}
</style>
</head>
<body class="bg-gray-100 font-sans dark:bg-gray-900">
<div class="flex h-screen overflow-hidden">
<!-- Mobile menu button -->
<div class="md:hidden fixed top-4 left-4 z-50">
<button id="mobileMenuButton" class="mobile-menu-button p-2 rounded-md bg-white shadow text-gray-700 dark:bg-gray-800 dark:text-white">
<i class="fas fa-bars"></i>
</button>
</div>
<!-- Sidebar -->
<div id="sidebar" class="sidebar bg-indigo-800 text-white flex-shrink-0 flex flex-col dark:bg-gray-800">
<div class="p-4 flex items-center space-x-3 border-b border-indigo-700 dark:border-gray-700">
<div class="w-8 h-8 rounded-full bg-indigo-600 flex items-center justify-center">
<i class="fas fa-check"></i>
</div>
<span class="logo-text text-xl font-bold">Todo Admin</span>
</div>
<div class="p-4 border-b border-indigo-700 dark:border-gray-700 user-info flex items-center justify-between">
<div class="flex items-center">
<div class="w-10 h-10 rounded-full bg-indigo-600 flex items-center justify-center user-avatar">
<i class="fas fa-user"></i>
</div>
<div class="user-details ml-3">
<div class="sidebar-text font-medium">Admin User</div>
<div class="sidebar-text text-xs text-indigo-300 dark:text-gray-400">Administrator</div>
</div>
</div>
<button id="toggleSidebar" class="text-indigo-300 hover:text-white toggle-btn dark:text-gray-400">
<i class="fas fa-chevron-left"></i>
</button>
</div>
<nav class="flex-1 overflow-y-auto py-4">
<div class="space-y-1 px-2">
<a href="#" class="menu-item flex items-center px-3 py-2 bg-indigo-900 rounded-md text-white dark:bg-gray-700">
<i class="fas fa-tachometer-alt w-5 mr-3"></i>
<span class="sidebar-text">Dashboard</span>
</a>
<a href="#" class="menu-item flex items-center px-3 py-2 hover:bg-indigo-700 rounded-md text-indigo-100 hover:text-white dark:hover:bg-gray-700">
<i class="fas fa-tasks w-5 mr-3"></i>
<span class="sidebar-text">All Tasks</span>
<span id="totalTasksBadge" class="ml-auto bg-indigo-600 text-xs px-2 py-0.5 rounded-full dark:bg-gray-600">0</span>
</a>
<a href="#" class="menu-item flex items-center px-3 py-2 hover:bg-indigo-700 rounded-md text-indigo-100 hover:text-white dark:hover:bg-gray-700">
<i class="fas fa-calendar-alt w-5 mr-3"></i>
<span class="sidebar-text">Calendar</span>
</a>
<a href="#" class="menu-item flex items-center px-3 py-2 hover:bg-indigo-700 rounded-md text-indigo-100 hover:text-white dark:hover:bg-gray-700">
<i class="fas fa-users w-5 mr-3"></i>
<span class="sidebar-text">Users</span>
</a>
<a href="#" class="menu-item flex items-center px-3 py-2 hover:bg-indigo-700 rounded-md text-indigo-100 hover:text-white dark:hover:bg-gray-700">
<i class="fas fa-cog w-5 mr-3"></i>
<span class="sidebar-text">Settings</span>
</a>
</div>
<div class="px-4 mt-8">
<h3 class="sidebar-text projects-title text-xs uppercase font-semibold text-indigo-400 mb-2 dark:text-gray-400">Projects</h3>
<div class="space-y-1">
<a href="#" class="project-item flex items-center px-3 py-2 hover:bg-indigo-700 rounded-md text-indigo-100 hover:text-white dark:hover:bg-gray-700">
<span class="w-2 h-2 rounded-full bg-green-400 mr-3"></span>
<span class="sidebar-text">Website Redesign</span>
</a>
<a href="#" class="project-item flex items-center px-3 py-2 hover:bg-indigo-700 rounded-md text-indigo-100 hover:text-white dark:hover:bg-gray-700">
<span class="w-2 h-2 rounded-full bg-blue-400 mr-3"></span>
<span class="sidebar-text">Mobile App</span>
</a>
<a href="#" class="project-item flex items-center px-3 py-2 hover:bg-indigo-700 rounded-md text-indigo-100 hover:text-white dark:hover:bg-gray-700">
<span class="w-2 h-2 rounded-full bg-purple-400 mr-3"></span>
<span class="sidebar-text">Marketing</span>
</a>
</div>
</div>
</nav>
<div class="p-4 border-t border-indigo-700 dark:border-gray-700">
<button class="logout-btn flex items-center px-3 py-2 hover:bg-indigo-700 rounded-md text-indigo-100 hover:text-white dark:hover:bg-gray-700 w-full">
<i class="fas fa-sign-out-alt w-5 mr-3"></i>
<span class="sidebar-text">Logout</span>
</button>
</div>
</div>
<!-- Main Content -->
<div class="main-content flex-1 flex flex-col overflow-hidden">
<!-- Top Navigation -->
<header class="bg-white shadow-sm dark:bg-gray-800">
<div class="px-4 sm:px-6 py-4 flex items-center justify-between">
<h1 class="text-xl sm:text-2xl font-semibold text-gray-800 dark:text-white">Task Dashboard</h1>
<div class="flex items-center space-x-2 sm:space-x-4">
<!-- Mobile header actions (visible on small screens) -->
<div class="mobile-header-actions hidden">
<button id="mobileDarkModeToggle" class="p-2 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700">
<i id="mobileDarkModeIcon" class="fas fa-moon text-gray-500 dark:text-yellow-300"></i>
</button>
</div>
<!-- Regular header actions (hidden on small screens) -->
<div class="header-actions flex items-center space-x-2 sm:space-x-4">
<button id="toggleDarkMode" class="p-2 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700">
<i id="darkModeIcon" class="fas fa-moon text-gray-500 dark:text-yellow-300"></i>
</button>
<div class="relative">
<button class="p-2 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700">
<i class="fas fa-bell text-gray-500 dark:text-gray-300"></i>
<span class="absolute top-0 right-0 w-2 h-2 rounded-full bg-red-500"></span>
</button>
</div>
<div class="relative">
<button class="p-2 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700">
<i class="fas fa-envelope text-gray-500 dark:text-gray-300"></i>
<span class="absolute top-0 right-0 w-2 h-2 rounded-full bg-blue-500"></span>
</button>
</div>
<div class="relative">
<button id="userMenuButton" class="flex items-center space-x-2">
<div class="w-8 h-8 rounded-full bg-indigo-100 dark:bg-gray-700 flex items-center justify-center">
<i class="fas fa-user text-indigo-600 dark:text-gray-300"></i>
</div>
<span class="hidden sm:inline text-sm font-medium dark:text-white">Admin</span>
<i class="fas fa-chevron-down text-xs dark:text-gray-300"></i>
</button>
</div>
</div>
</div>
</div>
</header>
<!-- Main Content Area -->
<main class="flex-1 overflow-y-auto p-4 sm:p-6">
<!-- Stats Cards -->
<div class="stats-grid grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 sm:gap-6 mb-6">
<div class="bg-white rounded-lg shadow p-4 sm:p-6 dark:bg-gray-800">
<div class="flex items-center justify-between">
<div>
<p class="text-gray-500 text-sm dark:text-gray-300">Total Tasks</p>
<h3 id="totalTasksCount" class="text-xl sm:text-2xl font-bold dark:text-white">0</h3>
</div>
<div class="p-2 sm:p-3 rounded-full bg-indigo-100 text-indigo-600 dark:bg-gray-700 dark:text-indigo-300">
<i class="fas fa-tasks"></i>
</div>
</div>
<div class="mt-2 sm:mt-4">
<div class="flex items-center text-xs sm:text-sm text-green-500 dark:text-green-400">
<i class="fas fa-arrow-up mr-1"></i>
<span>12% from last week</span>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow p-4 sm:p-6 dark:bg-gray-800">
<div class="flex items-center justify-between">
<div>
<p class="text-gray-500 text-sm dark:text-gray-300">Completed</p>
<h3 id="completedTasksCount" class="text-xl sm:text-2xl font-bold dark:text-white">0</h3>
</div>
<div class="p-2 sm:p-3 rounded-full bg-green-100 text-green-600 dark:bg-gray-700 dark:text-green-300">
<i class="fas fa-check-circle"></i>
</div>
</div>
<div class="mt-2 sm:mt-4">
<div class="flex items-center text-xs sm:text-sm text-green-500 dark:text-green-400">
<i class="fas fa-arrow-up mr-1"></i>
<span>8% from last week</span>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow p-4 sm:p-6 dark:bg-gray-800">
<div class="flex items-center justify-between">
<div>
<p class="text-gray-500 text-sm dark:text-gray-300">In Progress</p>
<h3 id="inProgressTasksCount" class="text-xl sm:text-2xl font-bold dark:text-white">0</h3>
</div>
<div class="p-2 sm:p-3 rounded-full bg-yellow-100 text-yellow-600 dark:bg-gray-7s00 dark:text-yellow-300">
<i class="fas fa-spinner"></i>
</div>
</div>
<div class="mt-2 sm:mt-4">
<div class="flex items-center text-xs sm:text-sm text-red-500 dark:text-red-400">
<i class="fas fa-arrow-down mr-1"></i>
<span>3% from last week</span>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow p-4 sm:p-6 dark:bg-gray-800">
<div class="flex items-center justify-between">
<div>
<p class="text-gray-500 text-sm dark:text-gray-300">Overdue</p>
<h3 id="overdueTasksCount" class="text-xl sm:text-2xl font-bold dark:text-white">0</h3>
</div>
<div class="p-2 sm:p-3 rounded-full bg-red-100 text-red-600 dark:bg-gray-700 dark:text-red-300">
<i class="fas fa-exclamation-circle"></i>
</div>
</div>
<div class="mt-2 sm:mt-4">
<div class="flex items-center text-xs sm:text-sm text-green-500 dark:text-green-400">
<i class="fas fa-arrow-down mr-1"></i>
<span>2% from last week</span>
</div>
</div>
</div>
</div>
<!-- Add New Task and Filters -->
<div class="bg-white rounded-lg shadow mb-6 dark:bg-gray-800">
<div class="p-4 sm:p-6 border-b border-gray-200 dark:border-gray-700">
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
<h2 class="text-lg font-semibold dark:text-white">Tasks</h2>
<div class="filter-controls flex flex-col sm:flex-row sm:space-x-3">
<div class="relative flex-1">
<select id="projectFilter" class="appearance-none bg-gray-100 border border-gray-200 rounded-md py-2 pl-3 pr-8 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white w-full">
<option value="all">All Projects</option>
<option value="Website Redesign">Website Redesign</option>
<option value="Mobile App">Mobile App</option>
<option value="Marketing">Marketing</option>
</select>
<div class="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
<i class="fas fa-chevron-down text-xs text-gray-500 dark:text-gray-300"></i>
</div>
</div>
<div class="relative flex-1">
<select id="statusFilter" class="appearance-none bg-gray-100 border border-gray-200 rounded-md py-2 pl-3 pr-8 text-sm focus:outline-none focus:ring-2 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white w-full">
<option value="all">All Status</option>
<option value="completed">Completed</option>
<option value="in-progress">In Progress</option>
<option value="pending">Pending</option>
</select>
<div class="absolute inset-y-0 right-0 flex items-center pr-2 pointer-events-none">
<i class="fas fa-chevron-down text-xs text-gray-500 dark:text-gray-300"></i>
</div>
</div>
<button id="addTaskBtn" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-md text-sm font-medium flex items-center justify-center dark:bg-indigo-700 dark:hover:bg-indigo-800">
<i class="fas fa-plus mr-2"></i> <span class="hidden sm:inline">Add Task</span>
</button>
</div>
</div>
</div>
<!-- Task List -->
<div id="taskListContainer" class="divide-y divide-gray-200 dark:divide-gray-700">
<!-- Tasks will be loaded here dynamically -->
</div>
<!-- Pagination -->
<div class="bg-white px-4 py-3 flex items-center justify-between border-t border-gray-200 dark:bg-gray-800 dark:border-gray-700">
<div class="flex-1 flex justify-between sm:hidden">
<a href="#" class="relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 dark:bg-gray-800 dark:border-gray-600 dark:text-white dark:hover:bg-gray-700">
Previous
</a>
<a href="#" class="ml-3 relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 dark:bg-gray-800 dark:border-gray-600 dark:text-white dark:hover:bg-gray-700">
Next
</a>
</div>
<div class="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between">
<div>
<p id="paginationInfo" class="text-sm text-gray-700 dark:text-gray-300">
Showing <span class="font-medium">0</span> to <span class="font-medium">0</span> of <span class="font-medium">0</span> results
</p>
</div>
<div>
<nav class="relative z-0 inline-flex rounded-md shadow-sm -space-x-px" aria-label="Pagination">
<a href="#" class="relative inline-flex items-center px-2 py-2 rounded-l-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50 dark:bg-gray-800 dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-700">
<span class="sr-only">Previous</span>
<i class="fas fa-chevron-left"></i>
</a>
<a href="#" aria-current="page" class="z-10 bg-indigo-50 border-indigo-500 text-indigo-600 relative inline-flex items-center px-4 py-2 border text-sm font-medium dark:bg-gray-700 dark:border-gray-600 dark:text-indigo-300">
1
</a>
<a href="#" class="bg-white border-gray-300 text-gray-500 hover:bg-gray-50 relative inline-flex items-center px-4 py-2 border text-sm font-medium dark:bg-gray-800 dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-700">
2
</a>
<a href="#" class="bg-white border-gray-300 text-gray-500 hover:bg-gray-50 relative inline-flex items-center px-4 py-2 border text-sm font-medium dark:bg-gray-800 dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-700">
3
</a>
<span class="relative inline-flex items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700 dark:bg-gray-800 dark:border-gray-600 dark:text-gray-300">
...
</span>
<a href="#" class="bg-white border-gray-300 text-gray-500 hover:bg-gray-50 relative inline-flex items-center px-4 py-2 border text-sm font-medium dark:bg-gray-800 dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-700">
8
</a>
<a href="#" class="relative inline-flex items-center px-2 py-2 rounded-r-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50 dark:bg-gray-800 dark:border-gray-600 dark:text-gray-300 dark:hover:bg-gray-700">
<span class="sr-only">Next</span>
<i class="fas fa-chevron-right"></i>
</a>
</nav>
</div>
</div>
</div>
</div>
</main>
</div>
</div>
<!-- Add Task Modal -->
<div id="taskModal" class="fixed inset-0 overflow-y-auto hidden z-50">
<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 dark:bg-gray-900 dark:opacity-90"></div>
</div>
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">&#8203;</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 w-full task-modal-content">
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4 dark:bg-gray-800">
<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 dark:text-white" id="modal-title">
Add New Task
</h3>
<form id="taskForm">
<div class="mb-4">
<label for="taskTitle" class="block text-sm font-medium text-gray-700 mb-1 dark:text-gray-300">Task Title</label>
<input type="text" id="taskTitle" class="shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md p-2 border dark:bg-gray-700 dark:border-gray-600 dark:text-white" placeholder="Enter task title" required>
</div>
<div class="mb-4">
<label for="taskDescription" class="block text-sm font-medium text-gray-700 mb-1 dark:text-gray-300">Description</label>
<textarea id="taskDescription" rows="3" class="shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md p-2 border dark:bg-gray-700 dark:border-gray-600 dark:text-white" placeholder="Enter task description"></textarea>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4 mb-4">
<div>
<label for="taskPriority" class="block text-sm font-medium text-gray-700 mb-1 dark:text-gray-300">Priority</label>
<select id="taskPriority" class="shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md p-2 border dark:bg-gray-700 dark:border-gray-600 dark:text-white">
<option value="low">Low</option>
<option value="medium" selected>Medium</option>
<option value="high">High</option>
</select>
</div>
<div>
<label for="taskProject" class="block text-sm font-medium text-gray-700 mb-1 dark:text-gray-300">Project</label>
<select id="taskProject" class="shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md p-2 border dark:bg-gray-700 dark:border-gray-600 dark:text-white">
<option>Website Redesign</option>
<option>Mobile App</option>
<option>Marketing</option>
</select>
</div>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4 mb-4">
<div>
<label for="taskDueDate" class="block text-sm font-medium text-gray-700 mb-1 dark:text-gray-300">Due Date</label>
<input type="date" id="taskDueDate" class="shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md p-2 border dark:bg-gray-700 dark:border-gray-600 dark:text-white">
</div>
<div>
<label for="taskAssignee" class="block text-sm font-medium text-gray-700 mb-1 dark:text-gray-300">Assignee</label>
<select id="taskAssignee" class="shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md p-2 border dark:bg-gray-700 dark:border-gray-600 dark:text-white">
<option>John D.</option>
<option>Sarah M.</option>
<option>Mike T.</option>
<option>Lisa K.</option>
</select>
</div>
</div>
</form>
</div>
</div>
</div>
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse dark:bg-gray-700">
<button type="button" id="saveTaskBtn" 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 dark:bg-indigo-700 dark:hover:bg-indigo-800">
Save Task
</button>
<button type="button" id="cancelTaskBtn" 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 dark:bg-gray-600 dark:border-gray-500 dark:text-white dark:hover:bg-gray-500">
Cancel
</button>
</div>
</div>
</div>
</div>
<!-- Edit Task Modal -->
<div id="editTaskModal" class="fixed inset-0 overflow-y-auto hidden z-50">
<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 dark:bg-gray-900 dark:opacity-90"></div>
</div>
<span class="hidden sm:inline-block sm:align-middle sm:h-screen" aria-hidden="true">&#8203;</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 w-full task-modal-content">
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4 dark:bg-gray-800">
<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 dark:text-white">Edit Task</h3>
<form id="editTaskForm">
<input type="hidden" id="editTaskId">
<div class="mb-4">
<label for="editTaskTitle" class="block text-sm font-medium text-gray-700 mb-1 dark:text-gray-300">Task Title</label>
<input type="text" id="editTaskTitle" class="shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md p-2 border dark:bg-gray-700 dark:border-gray-600 dark:text-white" placeholder="Enter task title" required>
</div>
<div class="mb-4">
<label for="editTaskDescription" class="block text-sm font-medium text-gray-700 mb-1 dark:text-gray-300">Description</label>
<textarea id="editTaskDescription" rows="3" class="shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md p-2 border dark:bg-gray-700 dark:border-gray-600 dark:text-white" placeholder="Enter task description"></textarea>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4 mb-4">
<div>
<label for="editTaskPriority" class="block text-sm font-medium text-gray-700 mb-1 dark:text-gray-300">Priority</label>
<select id="editTaskPriority" class="shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md p-2 border dark:bg-gray-700 dark:border-gray-600 dark:text-white">
<option value="low">Low</option>
<option value="medium">Medium</option>
<option value="high">High</option>
</select>
</div>
<div>
<label for="editTaskProject" class="block text-sm font-medium text-gray-700 mb-1 dark:text-gray-300">Project</label>
<select id="editTaskProject" class="shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md p-2 border dark:bg-gray-700 dark:border-gray-600 dark:text-white">
<option>Website Redesign</option>
<option>Mobile App</option>
<option>Marketing</option>
</select>
</div>
</div>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4 mb-4">
<div>
<label for="editTaskDueDate" class="block text-sm font-medium text-gray-700 mb-1 dark:text-gray-300">Due Date</label>
<input type="date" id="editTaskDueDate" class="shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md p-2 border dark:bg-gray-700 dark:border-gray-600 dark:text-white">
</div>
<div>
<label for="editTaskAssignee" class="block text-sm font-medium text-gray-700 mb-1 dark:text-gray-300">Assignee</label>
<select id="editTaskAssignee" class="shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md p-2 border dark:bg-gray-700 dark:border-gray-600 dark:text-white">
<option>John D.</option>
<option>Sarah M.</option>
<option>Mike T.</option>
<option>Lisa K.</option>
</select>
</div>
</div>
<div class="mb-4">
<label for="editTaskStatus" class="block text-sm font-medium text-gray-700 mb-1 dark:text-gray-300">Status</label>
<select id="editTaskStatus" class="shadow-sm focus:ring-indigo-500 focus:border-indigo-500 block w-full sm:text-sm border-gray-300 rounded-md p-2 border dark:bg-gray-700 dark:border-gray-600 dark:text-white">
<option value="pending">Pending</option>
<option value="in-progress">In Progress</option>
<option value="completed">Completed</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 dark:bg-gray-700">
<button type="button" id="updateTaskBtn" 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 dark:bg-indigo-700 dark:hover:bg-indigo-800">
Update Task
</button>
<button type="button" id="cancelEditTaskBtn" 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 dark:bg-gray-600 dark:border-gray-500 dark:text-white dark:hover:bg-gray-500">
Cancel
</button>
</div>
</div>
</div>
</div>
<script>
// Initialize tasks array from localStorage or create empty array
let tasks = JSON.parse(localStorage.getItem('tasks')) || [];
// DOM Elements
const taskModal = document.getElementById('taskModal');
const editTaskModal = document.getElementById('editTaskModal');
const addTaskBtn = document.getElementById('addTaskBtn');
const saveTaskBtn = document.getElementById('saveTaskBtn');
const cancelTaskBtn = document.getElementById('cancelTaskBtn');
const updateTaskBtn = document.getElementById('updateTaskBtn');
const cancelEditTaskBtn = document.getElementById('cancelEditTaskBtn');
const taskForm = document.getElementById('taskForm');
const editTaskForm = document.getElementById('editTaskForm');
const taskListContainer = document.getElementById('taskListContainer');
const projectFilter = document.getElementById('projectFilter');
const statusFilter = document.getElementById('statusFilter');
const toggleSidebarBtn = document.getElementById('toggleSidebar');
const sidebar = document.getElementById('sidebar');
const mainContent = document.querySelector('.main-content');
const toggleDarkModeBtn = document.getElementById('toggleDarkMode');
const darkModeIcon = document.getElementById('darkModeIcon');
const mobileMenuButton = document.getElementById('mobileMenuButton');
const mobileDarkModeToggle = document.getElementById('mobileDarkModeToggle');
const mobileDarkModeIcon = document.getElementById('mobileDarkModeIcon');
// Stats elements
const totalTasksCount = document.getElementById('totalTasksCount');
const completedTasksCount = document.getElementById('completedTasksCount');
const inProgressTasksCount = document.getElementById('inProgressTasksCount');
const overdueTasksCount = document.getElementById('overdueTasksCount');
const totalTasksBadge = document.getElementById('totalTasksBadge');
// Check for saved theme preference
const currentTheme = localStorage.getItem('theme') || 'light';
if (currentTheme === 'dark') {
document.documentElement.classList.add('dark');
darkModeIcon.classList.remove('fa-moon');
darkModeIcon.classList.add('fa-sun');
mobileDarkModeIcon.classList.remove('fa-moon');
mobileDarkModeIcon.classList.add('fa-sun');
}
// Toggle dark/light mode
function toggleDarkMode() {
if (document.documentElement.classList.contains('dark')) {
document.documentElement.classList.remove('dark');
localStorage.setItem('theme', 'light');
darkModeIcon.classList.remove('fa-sun');
darkModeIcon.classList.add('fa-moon');
mobileDarkModeIcon.classList.remove('fa-sun');
mobileDarkModeIcon.classList.add('fa-moon');
} else {
document.documentElement.classList.add('dark');
localStorage.setItem('theme', 'dark');
darkModeIcon.classList.remove('fa-moon');
darkModeIcon.classList.add('fa-sun');
mobileDarkModeIcon.classList.remove('fa-moon');
mobileDarkModeIcon.classList.add('fa-sun');
}
}
toggleDarkModeBtn.addEventListener('click', toggleDarkMode);
mobileDarkModeToggle.addEventListener('click', toggleDarkMode);
// Toggle sidebar
toggleSidebarBtn.addEventListener('click', function() {
sidebar.classList.toggle('collapsed');
mainContent.classList.toggle('expanded');
// Change the chevron direction
const icon = this.querySelector('i');
if (sidebar.classList.contains('collapsed')) {
icon.classList.remove('fa-chevron-left');
icon.classList.add('fa-chevron-right');
} else {
icon.classList.remove('fa-chevron-right');
icon.classList.add('fa-chevron-left');
}
});
// Toggle mobile menu
mobileMenuButton.addEventListener('click', function() {
sidebar.classList.toggle('open');
});
// Close sidebar when clicking outside on mobile
document.addEventListener('click', function(event) {
const isClickInsideSidebar = sidebar.contains(event.target);
const isClickInsideMobileButton = mobileMenuButton.contains(event.target);
if (!isClickInsideSidebar && !isClickInsideMobileButton && window.innerWidth <= 1024) {
sidebar.classList.remove('open');
}
});
// Event Listeners
addTaskBtn.addEventListener('click', () => {
taskModal.classList.remove('hidden');
});
cancelTaskBtn.addEventListener('click', () => {
taskModal.classList.add('hidden');
taskForm.reset();
});
cancelEditTaskBtn.addEventListener('click', () => {
editTaskModal.classList.add('hidden');
editTaskForm.reset();
});
// Close modals when clicking outside
window.addEventListener('click', (event) => {
if (event.target === taskModal) {
taskModal.classList.add('hidden');
taskForm.reset();
}
if (event.target === editTaskModal) {
editTaskModal.classList.add('hidden');
editTaskForm.reset();
}
});
// Save new task
saveTaskBtn.addEventListener('click', () => {
if (!taskForm.checkValidity()) {
alert('Please fill in all required fields');
return;
}
const newTask = {
id: Date.now().toString(),
title: document.getElementById('taskTitle').value,
description: document.getElementById('taskDescription').value,
priority: document.getElementById('taskPriority').value,
project: document.getElementById('taskProject').value,
dueDate: document.getElementById('taskDueDate').value,
assignee: document.getElementById('taskAssignee').value,
status: 'pending',
createdAt: new Date().toISOString()
};
tasks.push(newTask);
saveTasks();
renderTasks();
taskModal.classList.add('hidden');
taskForm.reset();
});
// Update task
updateTaskBtn.addEventListener('click', () => {
const taskId = document.getElementById('editTaskId').value;
const taskIndex = tasks.findIndex(task => task.id === taskId);
if (taskIndex !== -1) {
tasks[taskIndex] = {
...tasks[taskIndex],
title: document.getElementById('editTaskTitle').value,
description: document.getElementById('editTaskDescription').value,
priority: document.getElementById('editTaskPriority').value,
project: document.getElementById('editTaskProject').value,
dueDate: document.getElementById('editTaskDueDate').value,
assignee: document.getElementById('editTaskAssignee').value,
status: document.getElementById('editTaskStatus').value
};
saveTasks();
renderTasks();
editTaskModal.classList.add('hidden');
}
});
// Filter tasks
projectFilter.addEventListener('change', renderTasks);
statusFilter.addEventListener('change', renderTasks);
// Save tasks to localStorage
function saveTasks() {
localStorage.setItem('tasks', JSON.stringify(tasks));
updateStats();
}
// Render tasks to the DOM
function renderTasks() {
const projectFilterValue = projectFilter.value;
const statusFilterValue = statusFilter.value;
let filteredTasks = [...tasks];
if (projectFilterValue !== 'all') {
filteredTasks = filteredTasks.filter(task => task.project === projectFilterValue);
}
if (statusFilterValue !== 'all') {
filteredTasks = filteredTasks.filter(task => task.status === statusFilterValue);
}
// Sort tasks by priority (high first) and then by due date (earlier first)
filteredTasks.sort((a, b) => {
const priorityOrder = { high: 3, medium: 2, low: 1 };
if (priorityOrder[b.priority] !== priorityOrder[a.priority]) {
return priorityOrder[b.priority] - priorityOrder[a.priority];
}
if (!a.dueDate && !b.dueDate) return 0;
if (!a.dueDate) return 1;
if (!b.dueDate) return -1;
return new Date(a.dueDate) - new Date(b.dueDate);
});
taskListContainer.innerHTML = '';
if (filteredTasks.length === 0) {
taskListContainer.innerHTML = `
<div class="p-8 text-center">
<i class="fas fa-tasks text-4xl text-gray-300 dark:text-gray-500 mb-4"></i>
<p class="text-gray-500 dark:text-gray-400">No tasks found. Create a new task to get started!</p>
</div>
`;
return;
}
filteredTasks.forEach(task => {
const priorityClass = task.priority === 'high' ? 'priority-high' :
task.priority === 'medium' ? 'priority-medium' : 'priority-low';
const isCompleted = task.status === 'completed';
const lineThroughClass = isCompleted ? 'line-through' : '';
const priorityBadgeColor = task.priority === 'high' ? 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300' :
task.priority === 'medium' ? 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-300' : 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300';
const projectBadgeColor = task.project === 'Website Redesign' ? 'bg-indigo-100 text-indigo-800 dark:bg-indigo-900 dark:text-indigo-300' :
task.project === 'Mobile App' ? 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300' : 'bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-300';
const statusBadge = task.status === 'completed' ?
'<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300">Completed</span>' :
task.status === 'in-progress' ?
'<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-300">In Progress</span>' :
'';
const overdueBadge = task.dueDate && new Date(task.dueDate) < new Date() && task.status !== 'completed' ?
'<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300">Overdue</span>' : '';
const taskElement = document.createElement('div');
taskElement.className = `todo-item ${priorityClass} p-4 hover:bg-gray-50 flex items-start dark:hover:bg-gray-700`;
taskElement.innerHTML = `
<div class="flex items-center h-5 mr-3 mt-1">
<input type="checkbox" class="task-checkbox h-4 w-4 text-indigo-600 rounded focus:ring-indigo-500 dark:text-indigo-400" ${isCompleted ? 'checked' : ''} data-id="${task.id}">
</div>
<div class="flex-1 min-w-0">
<div class="flex items-center justify-between">
<p class="text-sm font-medium text-gray-900 truncate ${lineThroughClass} dark:text-white">
${task.title}
</p>
<div class="todo-actions flex space-x-2 ml-2">
<button class="edit-task-btn text-gray-400 hover:text-indigo-600 dark:hover:text-indigo-400" data-id="${task.id}">
<i class="fas fa-pencil-alt text-xs"></i>
</button>
<button class="delete-task-btn text-gray-400 hover:text-red-600 dark:hover:text-red-400" data-id="${task.id}">
<i class="fas fa-trash-alt text-xs"></i>
</button>
</div>
</div>
${task.description ? `
<p class="text-sm text-gray-500 mt-1 ${lineThroughClass} dark:text-gray-300">
${task.description}
</p>
` : ''}
<div class="mt-2 flex flex-wrap items-center">
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${priorityBadgeColor} mr-2 mb-1">
${task.priority.charAt(0).toUpperCase() + task.priority.slice(1)}
</span>
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${projectBadgeColor} mr-2 mb-1">
${task.project}
</span>
${task.dueDate ? `
<span class="text-xs text-gray-500 mr-2 mb-1 flex items-center dark:text-gray-400">
<i class="fas fa-calendar-alt mr-1"></i> Due: ${formatDate(task.dueDate)}
</span>
` : ''}
<span class="text-xs text-gray-500 mr-2 mb-1 flex items-center dark:text-gray-400">
<i class="fas fa-user mr-1"></i> ${task.assignee}
</span>
${statusBadge}
${overdueBadge}
</div>
</div>
`;
taskListContainer.appendChild(taskElement);
});
// Add event listeners to the new elements
document.querySelectorAll('.task-checkbox').forEach(checkbox => {
checkbox.addEventListener('change', function() {
const taskId = this.getAttribute('data-id');
const taskIndex = tasks.findIndex(task => task.id === taskId);
if (taskIndex !== -1) {
tasks[taskIndex].status = this.checked ? 'completed' : 'pending';
saveTasks();
renderTasks();
}
});
});
document.querySelectorAll('.edit-task-btn').forEach(btn => {
btn.addEventListener('click', function() {
const taskId = this.getAttribute('data-id');
const task = tasks.find(task => task.id === taskId);
if (task) {
document.getElementById('editTaskId').value = task.id;
document.getElementById('editTaskTitle').value = task.title;
document.getElementById('editTaskDescription').value = task.description || '';
document.getElementById('editTaskPriority').value = task.priority;
document.getElementById('editTaskProject').value = task.project;
document.getElementById('editTaskDueDate').value = task.dueDate || '';
document.getElementById('editTaskAssignee').value = task.assignee;
document.getElementById('editTaskStatus').value = task.status;
editTaskModal.classList.remove('hidden');
}
});
});
document.querySelectorAll('.delete-task-btn').forEach(btn => {
btn.addEventListener('click', function() {
if (confirm('Are you sure you want to delete this task?')) {
const taskId = this.getAttribute('data-id');
tasks = tasks.filter(task => task.id !== taskId);
saveTasks();
renderTasks();
}
});
});
// Update pagination info
document.getElementById('paginationInfo').innerHTML = `
Showing <span class="font-medium">1</span> to <span class="font-medium">${filteredTasks.length}</span> of <span class="font-medium">${filteredTasks.length}</span> results
`;
}
// Update statistics
function updateStats() {
const total = tasks.length;
const completed = tasks.filter(task => task.status === 'completed').length;
const inProgress = tasks.filter(task => task.status === 'in-progress').length;
const overdue = tasks.filter(task =>
task.dueDate &&
new Date(task.dueDate) < new Date() &&
task.status !== 'completed'
).length;
totalTasksCount.textContent = total;
completedTasksCount.textContent = completed;
inProgressTasksCount.textContent = inProgress;
overdueTasksCount.textContent = overdue;
totalTasksBadge.textContent = total;
}
// Format date for display
function formatDate(dateString) {
const options = { year: 'numeric', month: 'short', day: 'numeric' };
return new Date(dateString).toLocaleDateString(undefined, options);
}
// Initialize the app
function init() {
// Add some sample tasks if localStorage is empty
if (tasks.length === 0) {
tasks = [
{
id: '1',
title: 'Implement user authentication system',
description: 'Create secure login and registration pages with JWT authentication',
priority: 'high',
project: 'Website Redesign',
dueDate: '2023-07-15',
assignee: 'John D.',
status: 'pending',
createdAt: '2023-06-01'
},
{
id: '2',
title: 'Design mobile app dashboard',
description: 'Create wireframes and high-fidelity mockups for the main dashboard',
priority: 'medium',
project: 'Mobile App',
dueDate: '2023-07-20',
assignee: 'Sarah M.',
status: 'in-progress',
createdAt: '2023-06-05'
},
{
id: '3',
title: 'Update documentation',
description: 'Update API documentation with the latest endpoints',
priority: 'low',
project: 'Website Redesign',
dueDate: '2023-07-10',
assignee: 'Mike T.',
status: 'completed',
createdAt: '2023-05-28'
}
];
saveTasks();
}
renderTasks();
updateStats();
}
// Start the app
init();
</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=ffkj09/todo-list" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>