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:

  1. Create the project structure:

    • Create the directories and files as outlined above
    • Add the HTML, CSS, and JavaScript code to the respective files
  2. Open the application:

    • Open the index.html file in a web browser
  3. 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
  4. 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
  5. 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:

  1. Setting up the project structure
  2. Creating a responsive user interface with HTML and CSS
  3. Implementing core functionality with JavaScript
  4. Testing and debugging the application
  5. 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.