Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions todo_app_backend/task_app/migrations/0004_task_priority.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Generated by Django 5.2.8 on 2026-04-16 10:37

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("task_app", "0003_alter_task_status"),
]

operations = [
migrations.AddField(
model_name="task",
name="priority",
field=models.CharField(
choices=[("High", "High"), ("Medium", "Medium"), ("Low", "Low")],
default="Medium",
max_length=20,
),
),
]
6 changes: 6 additions & 0 deletions todo_app_backend/task_app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,14 @@ class Task(models.Model):
('In-Progress','In-progress'),
('Completed','Completed')
)
PRIORITY = (
('High','High'),
('Medium','Medium'),
('Low','Low')
)
task_name = models.CharField(max_length=100)
description = models.TextField()
status = models.CharField(max_length=20, choices=STATUS, default='In-Progress')
priority = models.CharField(max_length=20, choices=PRIORITY, default='Medium')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
2 changes: 1 addition & 1 deletion todo_app_backend/todo_app/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
import dj_database_url

DATABASES = {
"default": dj_database_url.config(default=os.environ.get("DATABASE_URL"))
"default": dj_database_url.config(default=os.environ.get("DATABASE_URL", "sqlite:///db.sqlite3"))
}


Expand Down
70 changes: 55 additions & 15 deletions todo_app_frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,19 @@ function App() {
const [error, setError] = useState(null);
const [searchTerm, setSearchTerm] = useState('');
const [filterStatus, setFilterStatus] = useState('All');
const [filterPriority, setFilterPriority] = useState('All');
const [sortBy, setSortBy] = useState('None');
const [showAddForm, setShowAddForm] = useState(false);
const [editingTask, setEditingTask] = useState(null);
const [newTask, setNewTask] = useState({
task_name: '',
description: ''
description: '',
priority: 'Medium'
});

// API Base URL - Update this to your Django backend URL
// const API_BASE_URL = 'http://localhost:8000/api'; // Change this to your backend URL
const API_BASE_URL = 'https://todo-app-fullstack-nrfk.onrender.com/api';
const API_BASE_URL = 'http://localhost:8000/api'; // Change this to your backend URL
// const API_BASE_URL = 'https://todo-app-fullstack-nrfk.onrender.com/api';

// Fetch tasks from API
const fetchTasks = async () => {
Expand Down Expand Up @@ -47,12 +50,15 @@ function App() {
const response = await fetch(`${API_BASE_URL}/tasks/`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newTask)
body: JSON.stringify({
...newTask,
priority: newTask.priority || 'Medium'
})
});
if (!response.ok) throw new Error('Failed to create task');

await fetchTasks(); // Refresh tasks list
setNewTask({ task_name: '', description: '' });
setNewTask({ task_name: '', description: '', priority: 'Medium' });
setShowAddForm(false);
} catch (err) {
setError(err.message);
Expand All @@ -72,7 +78,8 @@ function App() {
body: JSON.stringify({
task_name: editingTask.task_name,
description: editingTask.description,
status: editingTask.status
status: editingTask.status,
priority: editingTask.priority || 'Medium'
})
});
if (!response.ok) throw new Error('Failed to update task');
Expand Down Expand Up @@ -122,10 +129,11 @@ function App() {
}
};

const getPriorityColor = (status) => {
switch(status) {
case 'In-Progress': return 'bg-orange-100 text-orange-800 border-orange-200';
case 'Completed': return 'bg-green-100 text-green-800 border-green-200';
const getPriorityColor = (priority) => {
switch(priority) {
case 'High': return 'bg-red-100 text-red-800 border-red-200';
case 'Medium': return 'bg-yellow-100 text-yellow-800 border-yellow-200';
case 'Low': return 'bg-green-100 text-green-800 border-green-200';
default: return 'bg-gray-100 text-gray-800 border-gray-200';
}
};
Expand All @@ -142,7 +150,16 @@ function App() {
const matchesSearch = task.task_name.toLowerCase().includes(searchTerm.toLowerCase()) ||
task.description.toLowerCase().includes(searchTerm.toLowerCase());
const matchesStatus = filterStatus === 'All' || task.status === filterStatus;
return matchesSearch && matchesStatus;
const matchesPriority = filterPriority === 'All' || task.priority === filterPriority;
return matchesSearch && matchesStatus && matchesPriority;
});

const sortedTasks = [...filteredTasks].sort((a, b) => {
if (sortBy === 'Priority') {
const priorityOrder = { 'High': 0, 'Medium': 1, 'Low': 2 };
return priorityOrder[a.priority] - priorityOrder[b.priority];
}
return 0;
});

const taskStats = {
Expand Down Expand Up @@ -276,6 +293,24 @@ function App() {
<option value="In-Progress">In Progress</option>
<option value="Completed">Completed</option>
</select>
<select
value={filterPriority}
onChange={(e) => setFilterPriority(e.target.value)}
className="px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent text-sm sm:text-base"
>
<option value="All">All Priority</option>
<option value="High">High</option>
<option value="Medium">Medium</option>
<option value="Low">Low</option>
</select>
<select
value={sortBy}
onChange={(e) => setSortBy(e.target.value)}
className="px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-transparent text-sm sm:text-base"
>
<option value="None">Sort by</option>
<option value="Priority">Priority</option>
</select>
</div>
</div>
</div>
Expand All @@ -302,7 +337,7 @@ function App() {
</div>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 gap-4">
{filteredTasks.map(task => (
{sortedTasks.map(task => (
<div key={task.id} className="bg-white rounded-lg shadow-sm border border-gray-200 p-4 hover:shadow-md transition-shadow">
<div className="flex items-start justify-between mb-3">
<div className="flex items-center flex-1">
Expand Down Expand Up @@ -347,9 +382,14 @@ function App() {
</p>

<div className="flex items-center justify-between mb-3">
<span className={`px-2 py-1 rounded-full text-xs border ${getPriorityColor(task.status)}`}>
{task.status}
</span>
<div className="flex space-x-2">
<span className={`px-2 py-1 rounded-full text-xs border ${getStatusColor(task.status) === 'text-green-600' ? 'bg-green-100 text-green-800 border-green-200' : 'bg-orange-100 text-orange-800 border-orange-200'}`}>
{task.status}
</span>
<span className={`px-2 py-1 rounded-full text-xs border ${getPriorityColor(task.priority)}`}>
{task.priority}
</span>
</div>

{task.updated_at && (
<div className="flex items-center text-xs text-gray-500">
Expand Down
21 changes: 20 additions & 1 deletion todo_app_frontend/src/components/TaskForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const TaskForm: React.FC<TaskFormProps> = ({ onSubmit }) => {
const [taskName, setTaskName] = useState('');
const [description, setDescription] = useState('');
const [status, setStatus] = useState<'In-Progress' | 'Completed'>('In-Progress');
const [priority, setPriority] = useState<'High' | 'Medium' | 'Low'>('Medium');
const [isSubmitting, setIsSubmitting] = useState(false);

const handleSubmit = async (e: React.FormEvent) => {
Expand All @@ -21,12 +22,14 @@ const TaskForm: React.FC<TaskFormProps> = ({ onSubmit }) => {
task_name: taskName.trim(),
description: description.trim(),
status,
priority,
});

if (success) {
setTaskName('');
setDescription('');
setStatus('In-Progress');
setPriority('Medium');
}
setIsSubmitting(false);
};
Expand All @@ -39,7 +42,7 @@ const TaskForm: React.FC<TaskFormProps> = ({ onSubmit }) => {
</h2>

<form onSubmit={handleSubmit} className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Task Name
Expand Down Expand Up @@ -68,6 +71,22 @@ const TaskForm: React.FC<TaskFormProps> = ({ onSubmit }) => {
<option value="Completed">Completed</option>
</select>
</div>

<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Priority
</label>
<select
value={priority}
onChange={(e) => setPriority(e.target.value as 'High' | 'Medium' | 'Low')}
className="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 transition-colors"
disabled={isSubmitting}
>
<option value="High">High</option>
<option value="Medium">Medium</option>
<option value="Low">Low</option>
</select>
</div>
</div>

<div>
Expand Down
37 changes: 34 additions & 3 deletions todo_app_frontend/src/components/TaskItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const TaskItem: React.FC<TaskItemProps> = ({ task, onUpdate, onDelete }) => {
const [editName, setEditName] = useState(task.task_name);
const [editDescription, setEditDescription] = useState(task.description);
const [editStatus, setEditStatus] = useState(task.status);
const [editPriority, setEditPriority] = useState(task.priority);
const [isUpdating, setIsUpdating] = useState(false);
const [isDeleting, setIsDeleting] = useState(false);

Expand All @@ -21,13 +22,15 @@ const TaskItem: React.FC<TaskItemProps> = ({ task, onUpdate, onDelete }) => {
setEditName(task.task_name);
setEditDescription(task.description);
setEditStatus(task.status);
setEditPriority(task.priority);
};

const handleCancel = () => {
setIsEditing(false);
setEditName(task.task_name);
setEditDescription(task.description);
setEditStatus(task.status);
setEditPriority(task.priority);
};

const handleSave = async () => {
Expand All @@ -38,6 +41,7 @@ const TaskItem: React.FC<TaskItemProps> = ({ task, onUpdate, onDelete }) => {
task_name: editName.trim(),
description: editDescription.trim(),
status: editStatus,
priority: editPriority,
});

if (success) {
Expand All @@ -54,6 +58,18 @@ const TaskItem: React.FC<TaskItemProps> = ({ task, onUpdate, onDelete }) => {
};

const statusColor = task.status === 'Completed' ? 'bg-green-100 text-green-800' : 'bg-blue-100 text-blue-800';
const getPriorityColor = (priority: string) => {
switch (priority) {
case 'High':
return 'bg-red-100 text-red-800';
case 'Medium':
return 'bg-yellow-100 text-yellow-800';
case 'Low':
return 'bg-green-100 text-green-800';
default:
return 'bg-gray-100 text-gray-800';
}
};

const formatDate = (dateString: string) => {
return new Date(dateString).toLocaleDateString('en-US', {
Expand Down Expand Up @@ -94,14 +110,29 @@ const TaskItem: React.FC<TaskItemProps> = ({ task, onUpdate, onDelete }) => {
<option value="In-Progress">In-Progress</option>
<option value="Completed">Completed</option>
</select>
<select
value={editPriority}
onChange={(e) => setEditPriority(e.target.value as 'High' | 'Medium' | 'Low')}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
disabled={isUpdating}
>
<option value="High">High</option>
<option value="Medium">Medium</option>
<option value="Low">Low</option>
</select>
</div>
) : (
<div>
<h3 className="text-lg font-medium text-gray-900 truncate">{task.task_name}</h3>
<p className="text-gray-600 mt-2 text-sm leading-relaxed">{task.description}</p>
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium mt-2 ${statusColor}`}>
{task.status}
</span>
<div className="flex flex-wrap gap-2 mt-2">
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${statusColor}`}>
{task.status}
</span>
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${getPriorityColor(task.priority)}`}>
{task.priority}
</span>
</div>
<div className="mt-3 text-xs text-gray-500 space-y-1">
<div>Created: {formatDate(task.created_at)}</div>
<div>Updated: {formatDate(task.updated_at)}</div>
Expand Down
3 changes: 3 additions & 0 deletions todo_app_frontend/src/types/Task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export interface Task {
task_name: string;
description: string;
status: 'In-Progress' | 'Completed';
priority: 'High' | 'Medium' | 'Low';
created_at: string;
updated_at: string;
}
Expand All @@ -11,10 +12,12 @@ export interface CreateTaskRequest {
task_name: string;
description: string;
status: 'In-Progress' | 'Completed';
priority: 'High' | 'Medium' | 'Low';
}

export interface UpdateTaskRequest {
task_name: string;
description: string;
status: 'In-Progress' | 'Completed';
priority: 'High' | 'Medium' | 'Low';
}