Learn how to build a robust task management app with Lovable. Follow our step-by-step guide with code snippets, tips, and best practices for effective workflow management.
Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
Step 1: Setting Up Your Lovable Project
lovable.json
in your project’s root directory with the following content:
{
"name": "task-management-app",
"version": "1.0.0",
"dependencies": {
"lovable-dom": "latest",
"lovable-storage": "latest"
},
"scripts": {
"start": "load index.html"
}
}
dependencies
field above simulates installing additional libraries that help manage DOM interactions and storage. Lovable will read this file and automatically load these dependencies in your project.
Step 2: Creating the Main HTML File
index.html
in the root directory. This file will serve as the main entry point of your app.index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Task Management App</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<h1>Task Management App</h1>
<div id="task-container">
<input type="text" id="new-task" placeholder="Enter new task..." />
<button id="add-task">Add Task</button>
<ul id="task-list"></ul>
</div>
<script src="app.js"></script>
</body>
</html>
Step 3: Adding the JavaScript Functionality
app.js
in the root directory. This file will handle the logic behind adding, displaying, and deleting tasks.app.js
:
// Check if the browser supports local storage (Lovable storage utility can also be used)
function loadTasks() {
let tasks = JSON.parse(localStorage.getItem('tasks')) || [];
return tasks;
}
function saveTasks(tasks) {
localStorage.setItem('tasks', JSON.stringify(tasks));
}
function renderTasks() {
const taskList = document.getElementById('task-list');
taskList.innerHTML = '';
const tasks = loadTasks();
tasks.forEach((task, index) => {
const li = document.createElement('li');
li.textContent = task;
// Create a delete button
const deleteBtn = document.createElement('button');
deleteBtn.textContent = 'Delete';
deleteBtn.onclick = function() {
deleteTask(index);
};
li.appendChild(deleteBtn);
taskList.appendChild(li);
});
}
function addTask() {
const taskInput = document.getElementById('new-task');
const taskText = taskInput.value.trim();
if (taskText === '') {
return; // Do not add empty tasks
}
const tasks = loadTasks();
tasks.push(taskText);
saveTasks(tasks);
taskInput.value = '';
renderTasks();
}
function deleteTask(index) {
let tasks = loadTasks();
tasks.splice(index, 1);
saveTasks(tasks);
renderTasks();
}
// Bind event listener to the Add Task button
document.getElementById('add-task').addEventListener('click', addTask);
// Render tasks on initial load
renderTasks();
renderTasks()
function updates the task list in the UI whenever tasks are added or deleted.addTask()
which in turn updates local storage and the view.
Step 4: Styling Your App with CSS
styles.css
in the root directory. This file will contain the app’s styling.styles.css
:
body {
font-family: Arial, sans-serif;
background: #f4f4f4;
margin: 0;
padding: 20px;
}
#task-container {
background: #fff;
padding: 20px;
border-radius: 5px;
max-width: 500px;
margin: auto;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
h1 {
text-align: center;
}
#new-task {
width: 70%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 3px;
}
#add-task {
padding: 10px;
border: none;
background: #28a745;
color: #fff;
border-radius: 3px;
cursor: pointer;
}
#task-list {
list-style: none;
padding: 0;
margin-top: 20px;
}
#task-list li {
padding: 10px;
background: #eee;
margin-bottom: 10px;
border-radius: 3px;
display: flex;
justify-content: space-between;
align-items: center;
}
#task-list li button {
background: #dc3545;
border: none;
color: #fff;
padding: 5px 10px;
border-radius: 3px;
cursor: pointer;
}
Step 5: Configuring Lovable to Run Your Application
lovable.json
file for dependency management and starting your app. The "scripts": { "start": "load index.html" }
entry tells Lovable to load the index.html
file when your project starts.
Step 6: Testing Your Task Management App
lovable.json
, index.html
, app.js
, and styles.css
files with the provided code, save all your changes.
Step 7: Making Additional Changes and Iterating
lovable.json
file accordingly. Lovable will pick up the changes on the next run.app.js
file by adding additional functions and corresponding UI elements in index.html
.const express = require('express');
const app = express();
app.use(express.json());
/\*
Data structure for tasks includes support for nested subtasks.
Each task has:
- id: unique identifier
- title: task title
- description: detailed description
- status: "pending" or "completed"
- createdAt: timestamp when task was created
- subtasks: array of similar task objects for nesting
\*/
let tasks = [];
app.post('/api/tasks', (req, res) => {
const { title, description, subtasks } = req.body;
const newTask = {
id: tasks.length + 1,
title,
description,
status: 'pending',
createdAt: new Date(),
subtasks: Array.isArray(subtasks)
? subtasks.map((sub, index) => ({
id: 1000 + index + 1,
title: sub.title,
description: sub.description,
status: 'pending',
createdAt: new Date()
}))
: []
};
tasks.push(newTask);
res.status(201).json(newTask);
});
app.get('/api/tasks', (req, res) => {
res.json(tasks);
});
app.put('/api/tasks/:id', (req, res) => {
const taskId = parseInt(req.params.id, 10);
const taskIndex = tasks.findIndex(task => task.id === taskId);
if (taskIndex === -1) {
return res.status(404).json({ error: 'Task not found' });
}
tasks[taskIndex] = { ...tasks[taskIndex], ...req.body };
res.json(tasks[taskIndex]);
});
app.delete('/api/tasks/:id', (req, res) => {
const taskId = parseInt(req.params.id, 10);
const originalLength = tasks.length;
tasks = tasks.filter(task => task.id !== taskId);
if (tasks.length === originalLength) {
return res.status(404).json({ error: 'Task not found' });
}
res.json({ message: 'Task deleted successfully' });
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server listening on port ${PORT}`));
const express = require('express');
const axios = require('axios');
const app = express();
app.use(express.json());
// In-memory task store for demonstration purposes
let tasks = [
{ id: 1, title: 'Design UI Mockup', description: 'Create initial UI designs', status: 'pending' }
];
// Route to sync additional task details from an external API (e.g., Lovable Enhancements API)
app.get('/api/tasks/:id/sync', async (req, res) => {
const taskId = parseInt(req.params.id, 10);
const task = tasks.find(t => t.id === taskId);
if (!task) {
return res.status(404).json({ error: 'Task not found' });
}
try {
// Request external API for enhanced task information based on title and description
const response = await axios.get('https://api.lovable.com/enhanceTask', {
params: {
title: task.title,
description: task.description
}
});
// Merge the retrieved details into the task object
task.enhancedDetails = response.data;
res.json(task);
} catch (error) {
res.status(500).json({ error: 'Failed to sync with external API', details: error.message });
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
const express = require('express');
const axios = require('axios');
const app = express();
app.use(express.json());
let tasks = [
{ id: 1, title: 'Plan Project', description: 'Plan tasks for the project', status: 'pending', subtasks: [
{ id: 11, title: 'Define scope', description: 'Outline project boundaries', status: 'pending', subtasks: [] },
{ id: 12, title: 'Assign team', description: 'Select team members', status: 'pending', subtasks: [] }
]},
{ id: 2, title: 'Design UI', description: 'Create initial UI mockups', status: 'pending', subtasks: [] }
];
function completeTaskRecursively(task) {
task.status = 'completed';
if (Array.isArray(task.subtasks)) {
task.subtasks.forEach(subtask => completeTaskRecursively(subtask));
}
}
function findTaskById(taskList, id) {
for (let task of taskList) {
if (task.id === id) {
return task;
}
if (task.subtasks && task.subtasks.length) {
const found = findTaskById(task.subtasks, id);
if (found) return found;
}
}
return null;
}
app.post('/api/tasks/:id/complete', async (req, res) => {
const taskId = parseInt(req.params.id, 10);
const task = findTaskById(tasks, taskId);
if (!task) {
return res.status(404).json({ error: 'Task not found' });
}
completeTaskRecursively(task);
try {
const response = await axios.post('https://api.lovable.com/taskCompletion', {
taskId: task.id,
completedAt: new Date(),
details: { title: task.title, description: task.description }
});
res.json({ message: 'Task and all subtasks marked as completed', lovableData: response.data });
} catch (error) {
res.status(500).json({ error: 'Error notifying Lovable API', details: error.message });
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
Understanding the Requirements
Setting Up Your Development Environment
Planning the App Structure
Leveraging AI Code Generators
Implementing the Core Features
from flask import Flask, request, jsonify
app = Flask(**name**)
tasks = []
@app.route('/add\_task', methods=['POST'])
def add\_task():
data = request.get\_json()
task = {
'id': len(tasks) + 1,
'description': data.get('description'),
'completed': False
}
tasks.append(task)
return jsonify(task), 201
if **name** == '**main**':
app.run(debug=True)
Organizing Your Code
Incorporating AI for Continuous Improvement
When it comes to serving you, we sweat the little things. That’s why our work makes a big impact.