Building a Sample Application with AI
Building a complete application is the best way to understand the AI-assisted development process. This sample project will demonstrate how all the concepts come together in a real-world scenario.
In this section, we'll build a complete sample application using the vibe coding approach with AI tools. This hands-on example will demonstrate how to apply all the concepts we've covered so far in a practical context.
Choosing Our Sample Application
Personal Task Manager
For our sample application, we'll build a Personal Task Manager with the following features:
Task Management
- Create, edit, and delete tasks
- Organize with priority levels
- Set and track due dates
Organization
- Filter tasks by status
- Sort by different criteria
- Track completion status
Data Features
- Visualize task statistics
- Store data locally
- Data persistence
Why this project is ideal for demonstrating vibe coding:
- It's useful for everyday life
- It has clear functionality that's easy to understand
- It's complex enough to showcase various programming concepts
- It's simple enough to build without extensive development experience
- It can be built entirely with frontend technologies (HTML, CSS, JavaScript)
Project Setup
Let's start by setting up our project structure:
Directory Structure
task-manager/
├── index.html
├── css/
│ └── style.css
├── js/
│ ├── app.js
│ ├── taskManager.js
│ └── statistics.js
└── assets/
└── icons/
Pro tip: This structure separates our concerns and makes the code more maintainable. Organizing your code this way makes it easier to work with AI tools because you can focus on specific components one at a time.
Initial HTML Structure
Our index.html file will serve as the entry point for our application:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Personal Task Manager</title>
<link rel="stylesheet" href="css/style.css">
<!-- Add a modern font from Google Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
<!-- Add Font Awesome for icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
</head>
<body>
<div class="app-container">
<header class="app-header">
<h1>Personal Task Manager</h1>
<div class="theme-toggle">
<i class="fas fa-moon"></i>
</div>
</header>
<main class="app-main">
<section class="task-form-container">
<h2>Add New Task</h2>
<form id="task-form">
<div class="form-group">
<label for="task-title">Task Title</label>
<input type="text" id="task-title" placeholder="Enter task title" required>
</div>
<div class="form-group">
<label for="task-description">Description (Optional)</label>
<textarea id="task-description" placeholder="Enter task description"></textarea>
</div>
<div class="form-row">
<div class="form-group">
<label for="task-priority">Priority</label>
<select id="task-priority">
<option value="low">Low</option>
<option value="medium" selected>Medium</option>
<option value="high">High</option>
</select>
</div>
<div class="form-group">
<label for="task-due-date">Due Date</label>
<input type="date" id="task-due-date">
</div>
</div>
<button type="submit" class="btn btn-primary">Add Task</button>
</form>
</section>
<section class="task-list-container">
<div class="task-list-header">
<h2>My Tasks</h2>
<div class="task-filters">
<select id="filter-status">
<option value="all">All Tasks</option>
<option value="pending">Pending</option>
<option value="completed">Completed</option>
</select>
<select id="sort-by">
<option value="date-added">Date Added</option>
<option value="due-date">Due Date</option>
<option value="priority">Priority</option>
</select>
</div>
</div>
<ul id="task-list" class="task-list">
<!-- Tasks will be added here dynamically -->
</ul>
</section>
<section class="statistics-container">
<h2>Task Statistics</h2>
<div class="statistics-grid">
<div class="stat-card">
<h3>Total Tasks</h3>
<p id="stat-total">0</p>
</div>
<div class="stat-card">
<h3>Completed</h3>
<p id="stat-completed">0</p>
</div>
<div class="stat-card">
<h3>Pending</h3>
<p id="stat-pending">0</p>
</div>
<div class="stat-card">
<h3>Completion Rate</h3>
<p id="stat-rate">0%</p>
</div>
</div>
<div class="chart-container">
<canvas id="tasks-chart"></canvas>
</div>
</section>
</main>
<footer class="app-footer">
<p>Personal Task Manager - Created with AI assistance</p>
</footer>
</div>
<!-- Task template for JavaScript use -->
<template id="task-item-template">
<li class="task-item" data-id="">
<div class="task-content">
<h3 class="task-title"></h3>
<p class="task-description"></p>
<div class="task-meta">
<span class="task-priority"></span>
<span class="task-due-date"></span>
</div>
</div>
<div class="task-actions">
<button class="btn-complete" title="Mark as complete">
<i class="fas fa-check"></i>
</button>
<button class="btn-edit" title="Edit task">
<i class="fas fa-edit"></i>
</button>
<button class="btn-delete" title="Delete task">
<i class="fas fa-trash"></i>
</button>
</div>
</li>
</template>
<!-- Modal for editing tasks -->
<div id="edit-modal" class="modal">
<div class="modal-content">
<span class="close-modal">×</span>
<h2>Edit Task</h2>
<form id="edit-task-form">
<input type="hidden" id="edit-task-id">
<div class="form-group">
<label for="edit-task-title">Task Title</label>
<input type="text" id="edit-task-title" required>
</div>
<div class="form-group">
<label for="edit-task-description">Description</label>
<textarea id="edit-task-description"></textarea>
</div>
<div class="form-row">
<div class="form-group">
<label for="edit-task-priority">Priority</label>
<select id="edit-task-priority">
<option value="low">Low</option>
<option value="medium">Medium</option>
<option value="high">High</option>
</select>
</div>
<div class="form-group">
<label for="edit-task-due-date">Due Date</label>
<input type="date" id="edit-task-due-date">
</div>
</div>
<button type="submit" class="btn btn-primary">Save Changes</button>
</form>
</div>
</div>
<!-- Chart.js for statistics visualization -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<!-- Our application scripts -->
<script src="js/taskManager.js"></script>
<script src="js/statistics.js"></script>
<script src="js/app.js"></script>
</body>
</html>
CSS Styling
Now let's create our style.css file with a clean, modern design:
/* Base styles and variables */
:root {
--primary-color: #4a6fa5;
--primary-dark: #3a5a80;
--secondary-color: #6b8cae;
--accent-color: #e67e22;
--light-color: #f5f7fa;
--dark-color: #2c3e50;
--gray-light: #ecf0f1;
--gray-medium: #bdc3c7;
--gray-dark: #7f8c8d;
--success-color: #2ecc71;
--warning-color: #f39c12;
--danger-color: #e74c3c;
--border-radius: 8px;
--box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
--transition: all 0.3s ease;
/* Priority colors */
--priority-low: #3498db;
--priority-medium: #f39c12;
--priority-high: #e74c3c;
/* Dark theme variables (will be applied with .dark-theme class) */
--dark-bg: #1a1a2e;
--dark-surface: #16213e;
--dark-text: #e1e1e1;
}
/* Dark theme styles */
.dark-theme {
--primary-color: #5b8bd0;
--primary-dark: #4a6fa5;
--light-color: #16213e;
--dark-color: #e1e1e1;
--gray-light: #2c3e50;
--gray-medium: #34495e;
--gray-dark: #ecf0f1;
background-color: var(--dark-bg);
color: var(--dark-text);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Roboto', sans-serif;
line-height: 1.6;
color: var(--dark-color);
background-color: var(--light-color);
transition: var(--transition);
}
.app-container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
JavaScript Implementation
Let's implement the core functionality with JavaScript. First, the task manager module:
/**
* Task Manager Module
* Handles task data operations and storage
*/
class TaskManager {
constructor() {
this.tasks = JSON.parse(localStorage.getItem('tasks')) || [];
}
/**
* Get all tasks
* @returns {Array} Array of task objects
*/
getAllTasks() {
return this.tasks;
}
/**
* Get a task by ID
* @param {string} id - Task ID
* @returns {Object|null} Task object or null if not found
*/
getTaskById(id) {
return this.tasks.find(task => task.id === id) || null;
}
/**
* Add a new task
* @param {Object} taskData - Task data
* @returns {Object} The new task object
*/
addTask(taskData) {
const newTask = {
id: Date.now().toString(),
title: taskData.title,
description: taskData.description || '',
priority: taskData.priority || 'medium',
dueDate: taskData.dueDate || '',
completed: false,
dateAdded: new Date().toISOString()
};
this.tasks.push(newTask);
this.saveTasks();
return newTask;
}
/**
* Update an existing task
* @param {string} id - Task ID
* @param {Object} updatedData - Updated task data
* @returns {Object|null} Updated task object or null if not found
*/
updateTask(id, updatedData) {
const taskIndex = this.tasks.findIndex(task => task.id === id);
if (taskIndex === -1) return null;
this.tasks[taskIndex] = {
...this.tasks[taskIndex],
...updatedData
};
this.saveTasks();
return this.tasks[taskIndex];
}
/**
* Delete a task
* @param {string} id - Task ID
* @returns {boolean} True if deleted, false if not found
*/
deleteTask(id) {
const initialLength = this.tasks.length;
this.tasks = this.tasks.filter(task => task.id !== id);
if (this.tasks.length !== initialLength) {
this.saveTasks();
return true;
}
return false;
}
/**
* Toggle task completion status
* @param {string} id - Task ID
* @returns {Object|null} Updated task object or null if not found
*/
toggleTaskCompletion(id) {
const task = this.getTaskById(id);
if (!task) return null;
task.completed = !task.completed;
this.saveTasks();
return task;
}
/**
* Filter tasks by status
* @param {string} status - Status to filter by ('all', 'completed', 'pending')
* @returns {Array} Filtered array of tasks
*/
filterTasksByStatus(status) {
if (status === 'all') return this.tasks;
return this.tasks.filter(task => {
if (status === 'completed') return task.completed;
if (status === 'pending') return !task.completed;
return true;
});
}
/**
* Sort tasks by specified criteria
* @param {Array} tasks - Array of tasks to sort
* @param {string} sortBy - Sort criteria ('date-added', 'due-date', 'priority')
* @returns {Array} Sorted array of tasks
*/
sortTasks(tasks, sortBy) {
const sortedTasks = [...tasks];
switch (sortBy) {
case 'due-date':
return sortedTasks.sort((a, b) => {
if (!a.dueDate) return 1;
if (!b.dueDate) return -1;
return new Date(a.dueDate) - new Date(b.dueDate);
});
case 'priority':
const priorityOrder = { high: 0, medium: 1, low: 2 };
return sortedTasks.sort((a, b) => {
return priorityOrder[a.priority] - priorityOrder[b.priority];
});
case 'date-added':
default:
return sortedTasks.sort((a, b) => {
return new Date(b.dateAdded) - new Date(a.dateAdded);
});
}
}
/**
* Save tasks to localStorage
*/
saveTasks() {
localStorage.setItem('tasks', JSON.stringify(this.tasks));
}
}
// Create a global instance of the TaskManager
const taskManager = new TaskManager();
Next, let's implement the statistics module:
/**
* Statistics Manager Module
* Handles task statistics and visualization
*/
class StatisticsManager {
constructor(taskManager) {
this.taskManager = taskManager;
this.chart = null;
this.initializeChart();
}
/**
* Initialize the statistics chart
*/
initializeChart() {
const ctx = document.getElementById('tasks-chart').getContext('2d');
// Set chart options based on current theme
const isDarkTheme = document.body.classList.contains('dark-theme');
const textColor = isDarkTheme ? '#e1e1e1' : '#2c3e50';
this.chart = new Chart(ctx, {
type: 'doughnut',
data: {
labels: ['Completed', 'Pending'],
datasets: [{
data: [0, 0],
backgroundColor: [
'rgba(46, 204, 113, 0.8)',
'rgba(231, 76, 60, 0.8)'
],
borderColor: [
'rgba(46, 204, 113, 1)',
'rgba(231, 76, 60, 1)'
],
borderWidth: 1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'bottom',
labels: {
color: textColor
}
},
title: {
display: true,
text: 'Task Completion Status',
color: textColor,
font: {
size: 16
}
}
}
}
});
}
/**
* Update chart theme based on current app theme
*/
updateChartTheme() {
const isDarkTheme = document.body.classList.contains('dark-theme');
const textColor = isDarkTheme ? '#e1e1e1' : '#2c3e50';
if (this.chart) {
this.chart.options.plugins.legend.labels.color = textColor;
this.chart.options.plugins.title.color = textColor;
this.chart.update();
}
}
/**
* Update statistics display
*/
updateStatistics() {
const tasks = this.taskManager.getAllTasks();
const completedTasks = tasks.filter(task => task.completed);
const pendingTasks = tasks.filter(task => !task.completed);
// Update stat counters
document.getElementById('stat-total').textContent = tasks.length;
document.getElementById('stat-completed').textContent = completedTasks.length;
document.getElementById('stat-pending').textContent = pendingTasks.length;
// Calculate completion rate
const completionRate = tasks.length > 0
? Math.round((completedTasks.length / tasks.length) * 100)
: 0;
document.getElementById('stat-rate').textContent = `${completionRate}%`;
// Update chart data
this.chart.data.datasets[0].data = [completedTasks.length, pendingTasks.length];
this.chart.update();
// Additional statistics by priority
const priorityStats = {
high: tasks.filter(task => task.priority === 'high').length,
medium: tasks.filter(task => task.priority === 'medium').length,
low: tasks.filter(task => task.priority === 'low').length
};
// You could add more statistics here
}
}
Finally, let's implement the main application module:
/**
* Main Application Module
* Handles UI interactions and connects the TaskManager with the DOM
*/
document.addEventListener('DOMContentLoaded', function() {
// Initialize the statistics manager
const statisticsManager = new StatisticsManager(taskManager);
// DOM Elements
const taskForm = document.getElementById('task-form');
const taskList = document.getElementById('task-list');
const filterStatus = document.getElementById('filter-status');
const sortBy = document.getElementById('sort-by');
const editModal = document.getElementById('edit-modal');
const editForm = document.getElementById('edit-task-form');
const closeModal = document.querySelector('.close-modal');
const themeToggle = document.querySelector('.theme-toggle');
// Current filter and sort settings
let currentFilter = 'all';
let currentSort = 'date-added';
// Initialize the UI
renderTasks();
statisticsManager.updateStatistics();
// Check for saved theme preference
if (localStorage.getItem('darkTheme') === 'true') {
document.body.classList.add('dark-theme');
themeToggle.innerHTML = '';
statisticsManager.updateChartTheme();
}
// Event Listeners
taskForm.addEventListener('submit', handleAddTask);
taskList.addEventListener('click', handleTaskActions);
filterStatus.addEventListener('change', handleFilterChange);
sortBy.addEventListener('change', handleSortChange);
editForm.addEventListener('submit', handleEditTask);
closeModal.addEventListener('click', () => editModal.classList.remove('show'));
themeToggle.addEventListener('click', toggleTheme);
// Close modal when clicking outside of it
window.addEventListener('click', function(event) {
if (event.target === editModal) {
editModal.classList.remove('show');
}
});
/**
* Handle adding a new task
* @param {Event} event - The form submit event
*/
function handleAddTask(event) {
event.preventDefault();
const taskData = {
title: document.getElementById('task-title').value,
description: document.getElementById('task-description').value,
priority: document.getElementById('task-priority').value,
dueDate: document.getElementById('task-due-date').value
};
taskManager.addTask(taskData);
taskForm.reset();
renderTasks();
statisticsManager.updateStatistics();
}
/**
* Handle task actions (complete, edit, delete)
* @param {Event} event - The click event
*/
function handleTaskActions(event) {
const taskItem = event.target.closest('.task-item');
if (!taskItem) return;
const taskId = taskItem.dataset.id;
if (event.target.closest('.btn-complete')) {
taskManager.toggleTaskCompletion(taskId);
renderTasks();
statisticsManager.updateStatistics();
} else if (event.target.closest('.btn-edit')) {
openEditModal(taskId);
} else if (event.target.closest('.btn-delete')) {
if (confirm('Are you sure you want to delete this task?')) {
taskManager.deleteTask(taskId);
renderTasks();
statisticsManager.updateStatistics();
}
}
}
/**
* Handle filter change
* @param {Event} event - The change event
*/
function handleFilterChange(event) {
currentFilter = event.target.value;
renderTasks();
}
/**
* Handle sort change
* @param {Event} event - The change event
*/
function handleSortChange(event) {
currentSort = event.target.value;
renderTasks();
}
/**
* Open the edit modal for a task
* @param {string} taskId - The task ID
*/
function openEditModal(taskId) {
const task = taskManager.getTaskById(taskId);
if (!task) return;
document.getElementById('edit-task-id').value = task.id;
document.getElementById('edit-task-title').value = task.title;
document.getElementById('edit-task-description').value = task.description;
document.getElementById('edit-task-priority').value = task.priority;
document.getElementById('edit-task-due-date').value = task.dueDate;
editModal.classList.add('show');
}
/**
* Handle editing a task
* @param {Event} event - The form submit event
*/
function handleEditTask(event) {
event.preventDefault();
const taskId = document.getElementById('edit-task-id').value;
const updatedData = {
title: document.getElementById('edit-task-title').value,
description: document.getElementById('edit-task-description').value,
priority: document.getElementById('edit-task-priority').value,
dueDate: document.getElementById('edit-task-due-date').value
};
taskManager.updateTask(taskId, updatedData);
editModal.classList.remove('show');
renderTasks();
}
/**
* Toggle between light and dark themes
*/
function toggleTheme() {
const isDarkTheme = document.body.classList.toggle('dark-theme');
themeToggle.innerHTML = isDarkTheme ?
'' :
'';
localStorage.setItem('darkTheme', isDarkTheme);
statisticsManager.updateChartTheme();
}
/**
* Render tasks based on current filter and sort settings
*/
function renderTasks() {
// Get filtered tasks
let filteredTasks = taskManager.filterTasksByStatus(currentFilter);
// Sort tasks
filteredTasks = taskManager.sortTasks(filteredTasks, currentSort);
// Clear the task list
taskList.innerHTML = '';
// Get the task template
const template = document.getElementById('task-item-template');
// Render each task
filteredTasks.forEach(task => {
const taskElement = document.importNode(template.content, true).querySelector('.task-item');
// Set task data
taskElement.dataset.id = task.id;
if (task.completed) {
taskElement.classList.add('completed');
}
// Set task content
taskElement.querySelector('.task-title').textContent = task.title;
taskElement.querySelector('.task-description').textContent = task.description || 'No description';
// Set priority
const priorityElement = taskElement.querySelector('.task-priority');
priorityElement.textContent = task.priority.charAt(0).toUpperCase() + task.priority.slice(1);
priorityElement.classList.add(`priority-${task.priority}`);
// Set due date
const dueDateElement = taskElement.querySelector('.task-due-date');
if (task.dueDate) {
const dueDate = new Date(task.dueDate);
dueDateElement.textContent = `Due: ${dueDate.toLocaleDateString()}`;
// Highlight overdue tasks
const today = new Date();
today.setHours(0, 0, 0, 0);
if (dueDate < today && !task.completed) {
dueDateElement.style.color = 'var(--danger-color)';
dueDateElement.style.fontWeight = 'bold';
}
} else {
dueDateElement.textContent = 'No due date';
}
// Set complete button text
const completeButton = taskElement.querySelector('.btn-complete');
completeButton.title = task.completed ? 'Mark as incomplete' : 'Mark as complete';
// Add to the list
taskList.appendChild(taskElement);
});
// Show message if no tasks
if (filteredTasks.length === 0) {
const emptyMessage = document.createElement('li');
emptyMessage.className = 'empty-message';
emptyMessage.textContent = 'No tasks found. Add a new task to get started!';
taskList.appendChild(emptyMessage);
}
}
});
Testing the Application
Now that we've built our application, let's test it to ensure everything works as expected:
-
Create the project structure:
- Create the directories and files as outlined above
- Add the HTML, CSS, and JavaScript code to the respective files
-
Open the application:
- Open the
index.htmlfile in a web browser
- Open the
-
Test core functionality:
- Add a new task with all fields filled
- Add a task with only required fields
- Mark a task as complete
- Edit a task
- Delete a task
- Filter tasks by status
- Sort tasks by different criteria
-
Test edge cases:
- Add a task with a very long title or description
- Try to add a task without a title
- Test the application on different screen sizes
-
Test theme switching:
- Toggle between light and dark themes
- Verify that the theme preference is saved
Debugging Common Issues
During testing, you might encounter some issues. Here are common problems and their solutions:
Issue 1: Tasks not saving after page refresh
Solution:
Check the localStorage implementation in the TaskManager class. Make sure the saveTasks method is being called after any task modifications.
Issue 2: Chart not updating when tasks change
Solution:
Ensure that statisticsManager.updateStatistics() is called after any operation that changes tasks (add, edit, delete, toggle completion).
Issue 3: Modal not closing properly
Solution:
Verify the event listeners for closing the modal, especially the click event on the close button and the window click event for clicking outside the modal.
Issue 4: Responsive design issues on mobile
Solution:
Test the application on different screen sizes and adjust the CSS media queries as needed. Pay special attention to the form layout and task list on small screens.
Enhancing the Application
Once the basic functionality is working, you can enhance the application with additional features:
Feature 1: Task Categories
Add the ability to categorize tasks (work, personal, shopping, etc.) and filter by category.
Feature 2: Recurring Tasks
Implement recurring tasks that automatically reset after completion (daily, weekly, monthly).
Feature 3: Task Search
Add a search function to find tasks by title or description.
Feature 4: Data Export/Import
Allow users to export their tasks as JSON and import them back, enabling data backup and transfer.
Feature 5: Task Notes
Add the ability to attach notes or comments to tasks for additional context.
Conclusion
In this section, we've built a complete task manager application using the vibe coding approach with AI tools. We've covered:
- Setting up the project structure
- Creating a responsive user interface with HTML and CSS
- Implementing core functionality with JavaScript
- Testing and debugging the application
- Suggesting enhancements for further development
This practical example demonstrates how you can use AI tools to build useful applications without extensive programming knowledge. By understanding the concepts covered in previous sections and following the AI coding workflow, you can create similar applications for your own needs.