diff --git a/app/task/[id].tsx b/app/task/[id].tsx
index 245900c..a540f1a 100644
--- a/app/task/[id].tsx
+++ b/app/task/[id].tsx
@@ -54,6 +54,7 @@ type TaskData = {
priority: number;
dueDate: Date | null;
listId: string;
+ parentId: string | null;
recurrence: string | null;
};
@@ -400,67 +401,71 @@ export default function TaskDetailScreen() {
>
)}
- {/* Subtasks */}
-
- {t('task.subtasks')}
-
- {subtasks.map((sub) => (
- editingSubtaskId === sub.id ? undefined : handleToggleSubtask(sub.id)}
- onLongPress={() => handleEditSubtask(sub)}
- className={`flex-row items-center border-b py-2.5 ${isDark ? 'border-[#3A3A3A]' : 'border-[#E5E7EB]'}`}
- >
-
- {sub.completed && ✓}
-
- {editingSubtaskId === sub.id ? (
+ {/* Subtasks — only for root tasks (not subtasks themselves) */}
+ {!task?.parentId && (
+ <>
+
+ {t('task.subtasks')}
+
+ {subtasks.map((sub) => (
+ editingSubtaskId === sub.id ? undefined : handleToggleSubtask(sub.id)}
+ onLongPress={() => handleEditSubtask(sub)}
+ className={`flex-row items-center border-b py-2.5 ${isDark ? 'border-[#3A3A3A]' : 'border-[#E5E7EB]'}`}
+ >
+
+ {sub.completed && ✓}
+
+ {editingSubtaskId === sub.id ? (
+
+ ) : (
+
+ {sub.title}
+
+ )}
+ handleDeleteSubtask(sub.id)}
+ className="ml-2 p-1.5"
+ hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
+ >
+
+
+
+ ))}
+
+ {/* Add subtask */}
+
+
- ) : (
-
- {sub.title}
-
- )}
- handleDeleteSubtask(sub.id)}
- className="ml-2 p-1.5"
- hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
- >
-
-
-
- ))}
-
- {/* Add subtask */}
-
-
-
-
+
+ >
+ )}
diff --git a/web/src/app/api/tasks/route.ts b/web/src/app/api/tasks/route.ts
index aeaf4a2..ad9c9e7 100644
--- a/web/src/app/api/tasks/route.ts
+++ b/web/src/app/api/tasks/route.ts
@@ -25,16 +25,20 @@ export async function POST(request: Request) {
return NextResponse.json({ error: 'List not found' }, { status: 404 });
}
- // If parentId, verify parent task belongs to user
+ // If parentId, verify parent task belongs to user and is not itself a subtask
if (body.data.parentId) {
const [parent] = await db
- .select({ id: slTasks.id })
+ .select({ id: slTasks.id, parentId: slTasks.parentId })
.from(slTasks)
.where(and(eq(slTasks.id, body.data.parentId), eq(slTasks.userId, auth.userId)));
if (!parent) {
return NextResponse.json({ error: 'Parent task not found' }, { status: 404 });
}
+
+ if (parent.parentId) {
+ return NextResponse.json({ error: 'Cannot create sub-subtasks (max 2 levels)' }, { status: 400 });
+ }
}
const [task] = await db
diff --git a/web/src/components/TaskItem.tsx b/web/src/components/TaskItem.tsx
index 16627e8..1d0d7a8 100644
--- a/web/src/components/TaskItem.tsx
+++ b/web/src/components/TaskItem.tsx
@@ -261,12 +261,14 @@ export function TaskItem({ task, subtasks = [], depth = 0 }: TaskItemProps) {
>
{t("task.edit")}
-
+ {depth < 1 && (
+
+ )}