Database-Driven To-Do List Application IN HTML CSS AND JAVASCRIPT WITH PHP AND MY SQL

Project Introduction: Database-Driven To-Do List Application

A modern, full-stack task management system that allows users to efficiently manage their daily tasks with database persistence. This application features both client and admin interfaces, providing secure user authentication, task categorization, priority settings, and deadline management. Built with PHP backend and MySQL database, it offers real-time task updates and a responsive design for seamless user experience across all devices.


File Structure

todo-app/
│
├── index.php                    # Landing page / Login
├── register.php                 # User registration
├── dashboard.php                # User dashboard (client side)
├── admin/
│   ├── index.php               # Admin login
│   ├── dashboard.php           # Admin dashboard
│   ├── users.php               # Manage users
│   └── tasks.php               # Manage all tasks
│
├── includes/
│   ├── config.php              # Database configuration
│   ├── functions.php           # Core functions
│   ├── session.php             # Session management
│   └── auth.php                # Authentication functions
│
├── assets/
│   ├── css/
│   │   ├── style.css          # Main stylesheet
│   │   └── responsive.css     # Responsive design
│   ├── js/
│   │   ├── main.js            # Main JavaScript
│   │   └── tasks.js           # Task management
│   └── images/                 # Image assets
│
├── api/
│   ├── tasks.php              # Task CRUD operations
│   ├── user.php               # User operations
│   └── admin.php              # Admin operations
│
├── sql/
│   └── database.sql           # Database schema
│
└── README.md                   # Project documentation

1. Database Schema (sql/database.sql)

-- Create database
CREATE DATABASE IF NOT EXISTS todo_app;
USE todo_app;
-- Users table
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
full_name VARCHAR(100),
user_type ENUM('user', 'admin') DEFAULT 'user',
status ENUM('active', 'inactive') DEFAULT 'active',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- Tasks table
CREATE TABLE tasks (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
title VARCHAR(200) NOT NULL,
description TEXT,
category VARCHAR(50) DEFAULT 'General',
priority ENUM('Low', 'Medium', 'High', 'Urgent') DEFAULT 'Medium',
status ENUM('Pending', 'In Progress', 'Completed', 'Cancelled') DEFAULT 'Pending',
due_date DATE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
-- Task categories table
CREATE TABLE categories (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) UNIQUE NOT NULL,
description TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Insert default categories
INSERT INTO categories (name, description) VALUES
('General', 'General tasks'),
('Work', 'Work-related tasks'),
('Personal', 'Personal tasks'),
('Shopping', 'Shopping list'),
('Health', 'Health and fitness'),
('Education', 'Learning and courses');
-- Insert default admin user (password: admin123)
INSERT INTO users (username, email, password, full_name, user_type) VALUES
('admin', '[email protected]', '$2y$10$YourHashedPasswordHere', 'Administrator', 'admin');
-- Sample tasks
INSERT INTO tasks (user_id, title, description, category, priority, status, due_date) VALUES
(1, 'Complete project documentation', 'Write comprehensive documentation for the todo app', 'Work', 'High', 'Pending', DATE_ADD(CURDATE(), INTERVAL 3 DAY)),
(1, 'Buy groceries', 'Milk, bread, eggs, fruits, vegetables', 'Shopping', 'Medium', 'Pending', DATE_ADD(CURDATE(), INTERVAL 1 DAY)),
(1, 'Morning exercise', '30 minutes cardio and stretching', 'Health', 'Medium', 'Completed', CURDATE());

2. Configuration File (includes/config.php)

<?php
// Database configuration
define('DB_HOST', 'localhost');
define('DB_USER', 'root');
define('DB_PASS', '');
define('DB_NAME', 'todo_app');
// Application configuration
define('APP_NAME', 'Todo List Manager');
define('APP_URL', 'http://localhost/todo-app');
define('TIMEZONE', 'Asia/Kolkata');
// Set timezone
date_default_timezone_set(TIMEZONE);
// Start session if not started
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
// Database connection
function getConnection() {
try {
$conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
if ($conn->connect_error) {
throw new Exception("Connection failed: " . $conn->connect_error);
}
$conn->set_charset("utf8mb4");
return $conn;
} catch (Exception $e) {
die("Database connection error: " . $e->getMessage());
}
}
// Create connection object
$db = getConnection();
?>

3. Functions File (includes/functions.php)

<?php
require_once 'config.php';
// Sanitize input data
function sanitize($data) {
global $db;
return $db->real_escape_string(trim(htmlspecialchars($data)));
}
// Get user tasks
function getUserTasks($user_id, $status = null, $category = null) {
global $db;
$sql = "SELECT * FROM tasks WHERE user_id = ?";
$params = [$user_id];
$types = "i";
if ($status) {
$sql .= " AND status = ?";
$params[] = $status;
$types .= "s";
}
if ($category) {
$sql .= " AND category = ?";
$params[] = $category;
$types .= "s";
}
$sql .= " ORDER BY 
CASE priority 
WHEN 'Urgent' THEN 1
WHEN 'High' THEN 2
WHEN 'Medium' THEN 3
WHEN 'Low' THEN 4
END,
due_date ASC";
$stmt = $db->prepare($sql);
$stmt->bind_param($types, ...$params);
$stmt->execute();
$result = $stmt->get_result();
$tasks = [];
while ($row = $result->fetch_assoc()) {
$tasks[] = $row;
}
return $tasks;
}
// Add new task
function addTask($user_id, $title, $description, $category, $priority, $due_date) {
global $db;
$sql = "INSERT INTO tasks (user_id, title, description, category, priority, due_date) 
VALUES (?, ?, ?, ?, ?, ?)";
$stmt = $db->prepare($sql);
$stmt->bind_param("isssss", $user_id, $title, $description, $category, $priority, $due_date);
return $stmt->execute();
}
// Update task
function updateTask($task_id, $user_id, $title, $description, $category, $priority, $status, $due_date) {
global $db;
$sql = "UPDATE tasks SET title = ?, description = ?, category = ?, 
priority = ?, status = ?, due_date = ? 
WHERE id = ? AND user_id = ?";
$stmt = $db->prepare($sql);
$stmt->bind_param("ssssssii", $title, $description, $category, $priority, $status, $due_date, $task_id, $user_id);
return $stmt->execute();
}
// Delete task
function deleteTask($task_id, $user_id) {
global $db;
$sql = "DELETE FROM tasks WHERE id = ? AND user_id = ?";
$stmt = $db->prepare($sql);
$stmt->bind_param("ii", $task_id, $user_id);
return $stmt->execute();
}
// Get task statistics
function getTaskStats($user_id) {
global $db;
$stats = [
'total' => 0,
'pending' => 0,
'in_progress' => 0,
'completed' => 0,
'cancelled' => 0,
'overdue' => 0
];
$sql = "SELECT 
COUNT(*) as total,
SUM(CASE WHEN status = 'Pending' THEN 1 ELSE 0 END) as pending,
SUM(CASE WHEN status = 'In Progress' THEN 1 ELSE 0 END) as in_progress,
SUM(CASE WHEN status = 'Completed' THEN 1 ELSE 0 END) as completed,
SUM(CASE WHEN status = 'Cancelled' THEN 1 ELSE 0 END) as cancelled,
SUM(CASE WHEN due_date < CURDATE() AND status != 'Completed' THEN 1 ELSE 0 END) as overdue
FROM tasks WHERE user_id = ?";
$stmt = $db->prepare($sql);
$stmt->bind_param("i", $user_id);
$stmt->execute();
$result = $stmt->get_result();
if ($row = $result->fetch_assoc()) {
$stats = $row;
}
return $stats;
}
// Get all categories
function getCategories() {
global $db;
$result = $db->query("SELECT * FROM categories ORDER BY name");
$categories = [];
while ($row = $result->fetch_assoc()) {
$categories[] = $row;
}
return $categories;
}
?>

4. Main CSS (assets/css/style.css)

/* Global Styles */
:root {
--primary-color: #6366f1;
--secondary-color: #8b5cf6;
--success-color: #10b981;
--danger-color: #ef4444;
--warning-color: #f59e0b;
--dark-color: #1f2937;
--light-color: #f3f4f6;
--text-color: #374151;
--border-color: #e5e7eb;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
line-height: 1.6;
color: var(--text-color);
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
/* Login/Register Forms */
.auth-container {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
padding: 20px;
}
.auth-card {
background: white;
border-radius: 15px;
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
padding: 40px;
width: 100%;
max-width: 400px;
animation: slideUp 0.5s ease;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.auth-header {
text-align: center;
margin-bottom: 30px;
}
.auth-header h2 {
color: var(--dark-color);
font-size: 28px;
margin-bottom: 10px;
}
.auth-header p {
color: #6b7280;
}
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: 500;
color: var(--dark-color);
}
.form-control {
width: 100%;
padding: 12px 15px;
border: 2px solid var(--border-color);
border-radius: 8px;
font-size: 16px;
transition: all 0.3s ease;
}
.form-control:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(99,102,241,0.1);
}
.btn {
display: inline-block;
padding: 12px 24px;
border: none;
border-radius: 8px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
}
.btn-primary {
background: var(--primary-color);
color: white;
}
.btn-primary:hover {
background: var(--secondary-color);
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(99,102,241,0.4);
}
.btn-danger {
background: var(--danger-color);
color: white;
}
.btn-success {
background: var(--success-color);
color: white;
}
.btn-block {
width: 100%;
}
/* Dashboard Layout */
.dashboard {
background: #f8fafc;
min-height: 100vh;
}
.navbar {
background: white;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
padding: 15px 0;
position: sticky;
top: 0;
z-index: 100;
}
.navbar-content {
display: flex;
justify-content: space-between;
align-items: center;
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
.logo {
font-size: 24px;
font-weight: 700;
color: var(--primary-color);
text-decoration: none;
}
.nav-links {
display: flex;
gap: 20px;
align-items: center;
}
.nav-links a {
color: var(--text-color);
text-decoration: none;
font-weight: 500;
transition: color 0.3s;
}
.nav-links a:hover {
color: var(--primary-color);
}
.user-info {
display: flex;
align-items: center;
gap: 10px;
}
.user-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
background: linear-gradient(135deg, var(--primary-color), var(--secondary-color));
color: white;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
}
/* Stats Cards */
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
background: white;
border-radius: 10px;
padding: 20px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
transition: transform 0.3s;
}
.stat-card:hover {
transform: translateY(-5px);
}
.stat-card h3 {
font-size: 14px;
color: #6b7280;
margin-bottom: 10px;
}
.stat-number {
font-size: 32px;
font-weight: 700;
color: var(--dark-color);
}
/* Task Cards */
.tasks-container {
background: white;
border-radius: 10px;
padding: 20px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.tasks-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding-bottom: 20px;
border-bottom: 1px solid var(--border-color);
}
.task-filters {
display: flex;
gap: 10px;
}
.filter-select {
padding: 8px 12px;
border: 1px solid var(--border-color);
border-radius: 5px;
font-size: 14px;
}
.task-card {
background: #f9fafb;
border-radius: 8px;
padding: 15px;
margin-bottom: 10px;
border-left: 4px solid var(--primary-color);
transition: all 0.3s;
}
.task-card:hover {
box-shadow: 0 5px 15px rgba(0,0,0,0.1);
transform: translateX(5px);
}
.task-card.priority-urgent {
border-left-color: var(--danger-color);
}
.task-card.priority-high {
border-left-color: var(--warning-color);
}
.task-card.priority-medium {
border-left-color: var(--primary-color);
}
.task-card.priority-low {
border-left-color: var(--success-color);
}
.task-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.task-title {
font-size: 18px;
font-weight: 600;
color: var(--dark-color);
}
.task-category {
background: #e5e7eb;
padding: 4px 8px;
border-radius: 4px;
font-size: 12px;
}
.task-meta {
display: flex;
gap: 20px;
margin-top: 10px;
font-size: 14px;
color: #6b7280;
}
.task-actions {
display: flex;
gap: 10px;
}
.task-actions button {
padding: 5px 10px;
font-size: 12px;
}
/* Modal */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
z-index: 1000;
}
.modal.show {
display: flex;
align-items: center;
justify-content: center;
}
.modal-content {
background: white;
border-radius: 10px;
padding: 30px;
width: 90%;
max-width: 500px;
max-height: 90vh;
overflow-y: auto;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.close-btn {
font-size: 24px;
cursor: pointer;
color: #6b7280;
}
/* Responsive */
@media (max-width: 768px) {
.stats-grid {
grid-template-columns: 1fr;
}
.tasks-header {
flex-direction: column;
gap: 10px;
}
.task-filters {
width: 100%;
}
.filter-select {
flex: 1;
}
.task-meta {
flex-direction: column;
gap: 5px;
}
}

5. JavaScript (assets/js/tasks.js)

// Task Management System
class TaskManager {
constructor() {
this.init();
}
init() {
this.loadTasks();
this.bindEvents();
}
bindEvents() {
// Add task form
document.getElementById('addTaskForm')?.addEventListener('submit', (e) => {
e.preventDefault();
this.addTask();
});
// Filter tasks
document.getElementById('statusFilter')?.addEventListener('change', () => {
this.filterTasks();
});
document.getElementById('categoryFilter')?.addEventListener('change', () => {
this.filterTasks();
});
// Search tasks
let searchTimer;
document.getElementById('searchTask')?.addEventListener('input', (e) => {
clearTimeout(searchTimer);
searchTimer = setTimeout(() => {
this.searchTasks(e.target.value);
}, 500);
});
}
async loadTasks() {
try {
const response = await fetch('api/tasks.php?action=get_all');
const data = await response.json();
if (data.success) {
this.renderTasks(data.tasks);
this.updateStats(data.stats);
}
} catch (error) {
console.error('Error loading tasks:', error);
this.showNotification('Error loading tasks', 'error');
}
}
async addTask() {
const formData = new FormData(document.getElementById('addTaskForm'));
try {
const response = await fetch('api/tasks.php?action=add', {
method: 'POST',
body: formData
});
const data = await response.json();
if (data.success) {
this.showNotification('Task added successfully', 'success');
this.closeModal('addTaskModal');
this.loadTasks();
} else {
this.showNotification(data.message || 'Error adding task', 'error');
}
} catch (error) {
console.error('Error adding task:', error);
this.showNotification('Error adding task', 'error');
}
}
async updateTask(taskId) {
const formData = new FormData(document.getElementById('editTaskForm'));
formData.append('task_id', taskId);
try {
const response = await fetch('api/tasks.php?action=update', {
method: 'POST',
body: formData
});
const data = await response.json();
if (data.success) {
this.showNotification('Task updated successfully', 'success');
this.closeModal('editTaskModal');
this.loadTasks();
} else {
this.showNotification(data.message || 'Error updating task', 'error');
}
} catch (error) {
console.error('Error updating task:', error);
this.showNotification('Error updating task', 'error');
}
}
async deleteTask(taskId) {
if (!confirm('Are you sure you want to delete this task?')) {
return;
}
try {
const response = await fetch('api/tasks.php?action=delete', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ task_id: taskId })
});
const data = await response.json();
if (data.success) {
this.showNotification('Task deleted successfully', 'success');
this.loadTasks();
} else {
this.showNotification(data.message || 'Error deleting task', 'error');
}
} catch (error) {
console.error('Error deleting task:', error);
this.showNotification('Error deleting task', 'error');
}
}
async updateTaskStatus(taskId, status) {
try {
const response = await fetch('api/tasks.php?action=update_status', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ 
task_id: taskId,
status: status 
})
});
const data = await response.json();
if (data.success) {
this.showNotification('Task status updated', 'success');
this.loadTasks();
}
} catch (error) {
console.error('Error updating task status:', error);
this.showNotification('Error updating task', 'error');
}
}
filterTasks() {
const status = document.getElementById('statusFilter')?.value || '';
const category = document.getElementById('categoryFilter')?.value || '';
fetch(`api/tasks.php?action=filter&status=${status}&category=${category}`)
.then(response => response.json())
.then(data => {
if (data.success) {
this.renderTasks(data.tasks);
}
})
.catch(error => console.error('Error filtering tasks:', error));
}
searchTasks(query) {
if (query.length < 2) {
this.loadTasks();
return;
}
fetch(`api/tasks.php?action=search&q=${encodeURIComponent(query)}`)
.then(response => response.json())
.then(data => {
if (data.success) {
this.renderTasks(data.tasks);
}
})
.catch(error => console.error('Error searching tasks:', error));
}
renderTasks(tasks) {
const container = document.getElementById('tasksList');
if (!container) return;
if (tasks.length === 0) {
container.innerHTML = `
<div class="empty-state">
<i class="fas fa-tasks" style="font-size: 48px; color: #cbd5e1;"></i>
<h3>No tasks found</h3>
<p>Click the "Add Task" button to create your first task</p>
</div>
`;
return;
}
let html = '';
tasks.forEach(task => {
html += `
<div class="task-card priority-${task.priority.toLowerCase()}">
<div class="task-header">
<h3 class="task-title">${this.escapeHtml(task.title)}</h3>
<span class="task-category">${this.escapeHtml(task.category)}</span>
</div>
<p class="task-description">${this.escapeHtml(task.description || 'No description')}</p>
<div class="task-meta">
<span><i class="fas fa-flag"></i> ${task.priority}</span>
<span><i class="fas fa-clock"></i> Due: ${task.due_date || 'No deadline'}</span>
<span><i class="fas fa-tag"></i> ${task.status}</span>
${this.isOverdue(task.due_date, task.status) ? 
'<span class="badge badge-danger">Overdue</span>' : ''}
</div>
<div class="task-actions">
<select onchange="taskManager.updateTaskStatus(${task.id}, this.value)" 
class="status-select">
<option value="Pending" ${task.status === 'Pending' ? 'selected' : ''}>Pending</option>
<option value="In Progress" ${task.status === 'In Progress' ? 'selected' : ''}>In Progress</option>
<option value="Completed" ${task.status === 'Completed' ? 'selected' : ''}>Completed</option>
<option value="Cancelled" ${task.status === 'Cancelled' ? 'selected' : ''}>Cancelled</option>
</select>
<button onclick="taskManager.editTask(${task.id})" class="btn btn-primary btn-sm">
<i class="fas fa-edit"></i> Edit
</button>
<button onclick="taskManager.deleteTask(${task.id})" class="btn btn-danger btn-sm">
<i class="fas fa-trash"></i> Delete
</button>
</div>
</div>
`;
});
container.innerHTML = html;
}
updateStats(stats) {
document.getElementById('totalTasks')?.textContent = stats.total || 0;
document.getElementById('pendingTasks')?.textContent = stats.pending || 0;
document.getElementById('completedTasks')?.textContent = stats.completed || 0;
document.getElementById('overdueTasks')?.textContent = stats.overdue || 0;
}
openModal(modalId) {
document.getElementById(modalId)?.classList.add('show');
}
closeModal(modalId) {
document.getElementById(modalId)?.classList.remove('show');
}
showNotification(message, type = 'info') {
const notification = document.createElement('div');
notification.className = `notification notification-${type}`;
notification.textContent = message;
document.body.appendChild(notification);
setTimeout(() => {
notification.classList.add('show');
}, 100);
setTimeout(() => {
notification.classList.remove('show');
setTimeout(() => {
notification.remove();
}, 300);
}, 3000);
}
escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
isOverdue(dueDate, status) {
if (!dueDate || status === 'Completed' || status === 'Cancelled') {
return false;
}
const today = new Date().toISOString().split('T')[0];
return dueDate < today;
}
async editTask(taskId) {
try {
const response = await fetch(`api/tasks.php?action=get&id=${taskId}`);
const data = await response.json();
if (data.success) {
const task = data.task;
// Populate edit form
document.getElementById('edit_task_id').value = task.id;
document.getElementById('edit_title').value = task.title;
document.getElementById('edit_description').value = task.description || '';
document.getElementById('edit_category').value = task.category;
document.getElementById('edit_priority').value = task.priority;
document.getElementById('edit_status').value = task.status;
document.getElementById('edit_due_date').value = task.due_date;
this.openModal('editTaskModal');
}
} catch (error) {
console.error('Error fetching task:', error);
this.showNotification('Error loading task details', 'error');
}
}
}
// Initialize task manager
const taskManager = new TaskManager();
// Export for global use
window.taskManager = taskManager;

6. API Endpoint (api/tasks.php)

<?php
require_once '../includes/config.php';
require_once '../includes/functions.php';
require_once '../includes/auth.php';
header('Content-Type: application/json');
// Check authentication
if (!isLoggedIn()) {
echo json_encode(['success' => false, 'message' => 'Unauthorized']);
exit;
}
$action = $_GET['action'] ?? '';
switch ($action) {
case 'get_all':
$tasks = getUserTasks($_SESSION['user_id']);
$stats = getTaskStats($_SESSION['user_id']);
echo json_encode([
'success' => true,
'tasks' => $tasks,
'stats' => $stats
]);
break;
case 'get':
$task_id = $_GET['id'] ?? 0;
$sql = "SELECT * FROM tasks WHERE id = ? AND user_id = ?";
$stmt = $db->prepare($sql);
$stmt->bind_param("ii", $task_id, $_SESSION['user_id']);
$stmt->execute();
$result = $stmt->get_result();
if ($task = $result->fetch_assoc()) {
echo json_encode(['success' => true, 'task' => $task]);
} else {
echo json_encode(['success' => false, 'message' => 'Task not found']);
}
break;
case 'add':
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$title = sanitize($_POST['title']);
$description = sanitize($_POST['description']);
$category = sanitize($_POST['category']);
$priority = sanitize($_POST['priority']);
$due_date = sanitize($_POST['due_date']);
if (addTask($_SESSION['user_id'], $title, $description, $category, $priority, $due_date)) {
echo json_encode(['success' => true, 'message' => 'Task added successfully']);
} else {
echo json_encode(['success' => false, 'message' => 'Error adding task']);
}
}
break;
case 'update':
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$task_id = (int)$_POST['task_id'];
$title = sanitize($_POST['title']);
$description = sanitize($_POST['description']);
$category = sanitize($_POST['category']);
$priority = sanitize($_POST['priority']);
$status = sanitize($_POST['status']);
$due_date = sanitize($_POST['due_date']);
if (updateTask($task_id, $_SESSION['user_id'], $title, $description, $category, $priority, $status, $due_date)) {
echo json_encode(['success' => true, 'message' => 'Task updated successfully']);
} else {
echo json_encode(['success' => false, 'message' => 'Error updating task']);
}
}
break;
case 'update_status':
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$data = json_decode(file_get_contents('php://input'), true);
$task_id = (int)$data['task_id'];
$status = sanitize($data['status']);
$sql = "UPDATE tasks SET status = ? WHERE id = ? AND user_id = ?";
$stmt = $db->prepare($sql);
$stmt->bind_param("sii", $status, $task_id, $_SESSION['user_id']);
if ($stmt->execute()) {
echo json_encode(['success' => true, 'message' => 'Status updated']);
} else {
echo json_encode(['success' => false, 'message' => 'Error updating status']);
}
}
break;
case 'delete':
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$data = json_decode(file_get_contents('php://input'), true);
$task_id = (int)$data['task_id'];
if (deleteTask($task_id, $_SESSION['user_id'])) {
echo json_encode(['success' => true, 'message' => 'Task deleted successfully']);
} else {
echo json_encode(['success' => false, 'message' => 'Error deleting task']);
}
}
break;
case 'filter':
$status = $_GET['status'] ?? null;
$category = $_GET['category'] ?? null;
$tasks = getUserTasks($_SESSION['user_id'], $status, $category);
echo json_encode(['success' => true, 'tasks' => $tasks]);
break;
case 'search':
$query = $_GET['q'] ?? '';
$sql = "SELECT * FROM tasks WHERE user_id = ? AND (title LIKE ? OR description LIKE ?)";
$search = "%$query%";
$stmt = $db->prepare($sql);
$stmt->bind_param("iss", $_SESSION['user_id'], $search, $search);
$stmt->execute();
$result = $stmt->get_result();
$tasks = [];
while ($row = $result->fetch_assoc()) {
$tasks[] = $row;
}
echo json_encode(['success' => true, 'tasks' => $tasks]);
break;
default:
echo json_encode(['success' => false, 'message' => 'Invalid action']);
}
?>

7. Main Dashboard (dashboard.php)

<?php
require_once 'includes/config.php';
require_once 'includes/functions.php';
require_once 'includes/auth.php';
// Check if user is logged in
if (!isLoggedIn()) {
header('Location: index.php');
exit;
}
$user = getUserById($_SESSION['user_id']);
$tasks = getUserTasks($_SESSION['user_id']);
$stats = getTaskStats($_SESSION['user_id']);
$categories = getCategories();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dashboard - <?php echo APP_NAME; ?></title>
<link rel="stylesheet" href="assets/css/style.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
</head>
<body>
<div class="dashboard">
<!-- Navigation -->
<nav class="navbar">
<div class="navbar-content">
<a href="dashboard.php" class="logo"><?php echo APP_NAME; ?></a>
<div class="nav-links">
<a href="dashboard.php"><i class="fas fa-home"></i> Dashboard</a>
<a href="#tasks"><i class="fas fa-tasks"></i> Tasks</a>
<a href="#profile"><i class="fas fa-user"></i> Profile</a>
<div class="user-info">
<div class="user-avatar">
<?php echo strtoupper(substr($user['full_name'] ?? $user['username'], 0, 1)); ?>
</div>
<span><?php echo htmlspecialchars($user['full_name'] ?? $user['username']); ?></span>
<a href="logout.php" class="btn btn-danger btn-sm"><i class="fas fa-sign-out-alt"></i> Logout</a>
</div>
</div>
</div>
</nav>
<!-- Main Content -->
<div class="container">
<!-- Welcome Section -->
<div class="welcome-section">
<h1>Welcome back, <?php echo htmlspecialchars($user['full_name'] ?? $user['username']); ?>!</h1>
<p>Here's your task summary for today</p>
</div>
<!-- Statistics Cards -->
<div class="stats-grid">
<div class="stat-card">
<h3><i class="fas fa-tasks"></i> Total Tasks</h3>
<div class="stat-number" id="totalTasks"><?php echo $stats['total']; ?></div>
</div>
<div class="stat-card">
<h3><i class="fas fa-hourglass-half"></i> Pending</h3>
<div class="stat-number" id="pendingTasks"><?php echo $stats['pending']; ?></div>
</div>
<div class="stat-card">
<h3><i class="fas fa-spinner"></i> In Progress</h3>
<div class="stat-number" id="inProgressTasks"><?php echo $stats['in_progress']; ?></div>
</div>
<div class="stat-card">
<h3><i class="fas fa-check-circle"></i> Completed</h3>
<div class="stat-number" id="completedTasks"><?php echo $stats['completed']; ?></div>
</div>
<div class="stat-card">
<h3><i class="fas fa-exclamation-triangle"></i> Overdue</h3>
<div class="stat-number" id="overdueTasks"><?php echo $stats['overdue']; ?></div>
</div>
</div>
<!-- Tasks Section -->
<div class="tasks-container">
<div class="tasks-header">
<h2><i class="fas fa-list"></i> My Tasks</h2>
<div class="task-filters">
<input type="text" id="searchTask" placeholder="Search tasks..." class="form-control" style="width: 200px;">
<select id="statusFilter" class="filter-select">
<option value="">All Status</option>
<option value="Pending">Pending</option>
<option value="In Progress">In Progress</option>
<option value="Completed">Completed</option>
<option value="Cancelled">Cancelled</option>
</select>
<select id="categoryFilter" class="filter-select">
<option value="">All Categories</option>
<?php foreach ($categories as $category): ?>
<option value="<?php echo $category['name']; ?>">
<?php echo $category['name']; ?>
</option>
<?php endforeach; ?>
</select>
<button class="btn btn-primary" onclick="taskManager.openModal('addTaskModal')">
<i class="fas fa-plus"></i> Add Task
</button>
</div>
</div>
<!-- Tasks List -->
<div id="tasksList">
<!-- Tasks will be loaded here via JavaScript -->
</div>
</div>
</div>
</div>
<!-- Add Task Modal -->
<div id="addTaskModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h3>Add New Task</h3>
<span class="close-btn" onclick="taskManager.closeModal('addTaskModal')">&times;</span>
</div>
<form id="addTaskForm">
<div class="form-group">
<label for="title">Task Title *</label>
<input type="text" id="title" name="title" class="form-control" required>
</div>
<div class="form-group">
<label for="description">Description</label>
<textarea id="description" name="description" class="form-control" rows="3"></textarea>
</div>
<div class="form-group">
<label for="category">Category</label>
<select id="category" name="category" class="form-control">
<?php foreach ($categories as $category): ?>
<option value="<?php echo $category['name']; ?>">
<?php echo $category['name']; ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label for="priority">Priority</label>
<select id="priority" name="priority" class="form-control">
<option value="Low">Low</option>
<option value="Medium" selected>Medium</option>
<option value="High">High</option>
<option value="Urgent">Urgent</option>
</select>
</div>
<div class="form-group">
<label for="due_date">Due Date</label>
<input type="date" id="due_date" name="due_date" class="form-control">
</div>
<button type="submit" class="btn btn-primary btn-block">Add Task</button>
</form>
</div>
</div>
<!-- Edit Task Modal -->
<div id="editTaskModal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h3>Edit Task</h3>
<span class="close-btn" onclick="taskManager.closeModal('editTaskModal')">&times;</span>
</div>
<form id="editTaskForm">
<input type="hidden" id="edit_task_id" name="task_id">
<div class="form-group">
<label for="edit_title">Task Title *</label>
<input type="text" id="edit_title" name="title" class="form-control" required>
</div>
<div class="form-group">
<label for="edit_description">Description</label>
<textarea id="edit_description" name="description" class="form-control" rows="3"></textarea>
</div>
<div class="form-group">
<label for="edit_category">Category</label>
<select id="edit_category" name="category" class="form-control">
<?php foreach ($categories as $category): ?>
<option value="<?php echo $category['name']; ?>">
<?php echo $category['name']; ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="form-group">
<label for="edit_priority">Priority</label>
<select id="edit_priority" name="priority" class="form-control">
<option value="Low">Low</option>
<option value="Medium">Medium</option>
<option value="High">High</option>
<option value="Urgent">Urgent</option>
</select>
</div>
<div class="form-group">
<label for="edit_status">Status</label>
<select id="edit_status" name="status" class="form-control">
<option value="Pending">Pending</option>
<option value="In Progress">In Progress</option>
<option value="Completed">Completed</option>
<option value="Cancelled">Cancelled</option>
</select>
</div>
<div class="form-group">
<label for="edit_due_date">Due Date</label>
<input type="date" id="edit_due_date" name="due_date" class="form-control">
</div>
<button type="button" onclick="taskManager.updateTask(document.getElementById('edit_task_id').value)" 
class="btn btn-primary btn-block">Update Task</button>
</form>
</div>
</div>
<script src="assets/js/tasks.js"></script>
</body>
</html>

Step-by-Step Guide to Use the Project

1. Prerequisites Installation

# Install XAMPP/WAMP/LAMP stack
# Download and install from:
# Windows: https://www.apachefriends.org/download.html
# Linux: sudo apt-get install lamp-server^
# Mac: https://www.mamp.info/en/downloads/

2. Project Setup

Step 1: Create Project Directory

# Navigate to web server root
cd /opt/lampp/htdocs/  # Linux/XAMPP
# or
cd C:\xampp\htdocs\    # Windows
# Create project folder
mkdir todo-app
cd todo-app

Step 2: Set Up Database

# 1. Open phpMyAdmin (http://localhost/phpmyadmin)
# 2. Click on "Import" tab
# 3. Browse and select 'sql/database.sql' file
# 4. Click "Go" to import database

Step 3: Configure Database Connection

// Edit includes/config.php
define('DB_HOST', 'localhost');
define('DB_USER', 'root');        // Your MySQL username
define('DB_PASS', '');             // Your MySQL password
define('DB_NAME', 'todo_app');

Step 4: Set Permissions (Linux/Mac)

# Set proper permissions for Linux/Mac
chmod -R 755 /opt/lampp/htdocs/todo-app/
chmod 777 /opt/lampp/htdocs/todo-app/assets/  # If using file uploads

3. Running the Application

Step 1: Start Servers

# Start Apache and MySQL
# Using XAMPP Control Panel (Windows)
# or terminal (Linux):
sudo /opt/lampp/lampp start

Step 2: Access Application

# Open web browser and navigate to:
http://localhost/todo-app/
# Default admin credentials:
Username: admin
Password: admin123

4. Features and Functionality

Client Side Features:

  1. User Registration & Login
  • Create new account
  • Secure password hashing
  • Session management
  1. Task Management
  • Create new tasks with title, description, category, priority
  • Set due dates for tasks
  • Edit existing tasks
  • Delete tasks
  • Mark tasks as complete/in progress
  1. Task Organization
  • Filter tasks by status and category
  • Search tasks by title or description
  • View tasks by priority levels
  • See overdue tasks highlighted
  1. Dashboard Statistics
  • View total tasks count
  • See pending tasks
  • Track in-progress tasks
  • Monitor completed tasks
  • Check overdue tasks
  1. Responsive Design
  • Mobile-friendly interface
  • Works on all devices
  • Touch-friendly controls

Admin Side Features:

  1. User Management
  • View all registered users
  • Add/Edit/Delete users
  • Change user roles
  • Activate/Deactivate accounts
  1. Task Overview
  • View all tasks from all users
  • Monitor system usage
  • Generate reports
  1. Category Management
  • Add/Edit/Delete categories
  • Set category descriptions
  • Organize tasks better
  1. System Settings
  • Configure application settings
  • View system logs
  • Database backup options

5. Testing the Application

# Test user registration
1. Go to http://localhost/todo-app/register.php
2. Fill registration form
3. Submit and verify email
# Test task creation
1. Login with your credentials
2. Click "Add Task" button
3. Fill task details
4. Submit and verify in dashboard
# Test admin features
1. Login as admin (admin/admin123)
2. Navigate to admin/dashboard.php
3. Test user and task management

6. Troubleshooting Common Issues

Issue 1: Database Connection Error

// Check in config.php
error_reporting(E_ALL);
ini_set('display_errors', 1);
// Verify database credentials
$conn = new mysqli('localhost', 'root', '', 'todo_app');
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}

Issue 2: 404 Errors

# Check file permissions
# Ensure .htaccess file exists in root directory
# Verify Apache mod_rewrite is enabled
sudo a2enmod rewrite
sudo service apache2 restart

Issue 3: Session Issues

// In includes/session.php
session_start();
if (!isset($_SESSION['created'])) {
session_regenerate_id(true);
$_SESSION['created'] = time();
}

7. Security Best Practices

  1. Password Hashing
// In register.php
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
  1. SQL Injection Prevention
// Always use prepared statements
$stmt = $conn->prepare("SELECT * FROM users WHERE email = ?");
$stmt->bind_param("s", $email);
  1. XSS Prevention
// Always escape output
echo htmlspecialchars($user_input, ENT_QUOTES, 'UTF-8');
  1. CSRF Protection
// Generate CSRF token
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
// Verify in forms
if (!hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
die('Invalid CSRF token');
}

8. Deployment to Production

# 1. Update database configuration
define('DB_HOST', 'production_host');
define('DB_USER', 'production_user');
define('DB_PASS', 'secure_password');
# 2. Disable error reporting
error_reporting(0);
ini_set('display_errors', 0);
# 3. Enable HTTPS
# Configure SSL certificate
# Force HTTPS redirect in .htaccess
# 4. Set proper file permissions
chmod 644 *.php
chmod 755 includes/ assets/
chmod 600 includes/config.php

9. Backup and Maintenance

# Database backup
mysqldump -u root -p todo_app > backup_$(date +%Y%m%d).sql
# Automated backup script
#!/bin/bash
BACKUP_DIR="/path/to/backup"
mysqldump -u root -p'password' todo_app > $BACKUP_DIR/backup_$(date +%Y%m%d).sql
find $BACKUP_DIR -type f -mtime +30 -delete

Project Summary

This comprehensive To-Do List application provides:

Complete CRUD Operations for tasks
User Authentication System with registration/login
Admin Panel for system management
Database Integration with MySQL
Responsive Design for all devices
Real-time Updates with AJAX
Task Filtering and Search functionality
Priority-based Task Organization
Due Date Tracking with overdue alerts
Statistics Dashboard for progress tracking
Secure Implementation with best practices
Easy Deployment and configuration

The application is production-ready and can be extended with additional features like email notifications, file attachments, task sharing, and more based on specific requirements.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper