diff --git a/app/assignment/viewDetailsAssignment.tsx b/app/assignment/viewDetailsAssignment.tsx
index 15c4ed9..e22cb61 100644
--- a/app/assignment/viewDetailsAssignment.tsx
+++ b/app/assignment/viewDetailsAssignment.tsx
@@ -41,7 +41,6 @@ export default function ViewDetailsAssignment() {
.single();
if (error || !data) {
- console.log('GetAssignment error:', error);
Alert.alert('Assignment could not be fetched, please try again');
return;
}
@@ -56,7 +55,6 @@ export default function ViewDetailsAssignment() {
.single();
if (subjectError || !subjectData) {
- console.log('GetSubjectMeta error:', subjectError);
setSubjectMeta({
title: 'Unknown Subject',
color: 'slate'
@@ -355,7 +353,7 @@ export default function ViewDetailsAssignment() {
className="mb-6 mt-5 h-14 items-center justify-center rounded-2xl bg-accent"
onPress={() =>
router.push({
- pathname: '/task/createTask',
+ pathname: '/task/upsertTask',
params: { aId: assignment.aId },
})
}
@@ -436,7 +434,7 @@ export default function ViewDetailsAssignment() {
className="mr-3 flex-1 items-center justify-center rounded-2xl border border-app-border bg-app-subtle py-3"
onPress={() =>
router.push({
- pathname: '/task/editTask',
+ pathname: '/task/upsertTask',
params: { tId: item.tId },
})
}
@@ -474,4 +472,4 @@ export default function ViewDetailsAssignment() {
/>
);
-}
\ No newline at end of file
+}
diff --git a/app/subject/viewDetailsSubject.tsx b/app/subject/viewDetailsSubject.tsx
index 7b5480f..8dee4f8 100644
--- a/app/subject/viewDetailsSubject.tsx
+++ b/app/subject/viewDetailsSubject.tsx
@@ -86,20 +86,6 @@ export default function ViewDetailsSubject() {
}, [session, sId])
);
- useEffect(() => {
- const test = async () => {
- try {
- const { data, error } = await supabase.from('subjects').select('*').limit(1);
- console.log('test data:', data);
- console.log('test error:', error);
- } catch (err) {
- console.log('test crashed:', err);
- }
- };
-
- test();
- }, []);
-
const DeleteSubject = async (subjectId: string) => {
Alert.alert(
'Delete Subject',
@@ -474,4 +460,4 @@ export default function ViewDetailsSubject() {
/>
);
-}
\ No newline at end of file
+}
diff --git a/app/task/_layout.tsx b/app/task/_layout.tsx
index 1125408..06a3159 100644
--- a/app/task/_layout.tsx
+++ b/app/task/_layout.tsx
@@ -3,9 +3,7 @@ import { Stack } from "expo-router";
export default function TaskLayout() {
return (
-
-
-
+
);
diff --git a/app/task/editTask.tsx b/app/task/editTask.tsx
deleted file mode 100644
index 45f7f11..0000000
--- a/app/task/editTask.tsx
+++ /dev/null
@@ -1,255 +0,0 @@
-import { CheckAssignmentCompletion } from '@/lib/progress';
-import { supabase } from '@/lib/supabase';
-import type { Task } from '@/lib/types';
-import { router, Stack, useFocusEffect, useLocalSearchParams } from 'expo-router';
-import { useCallback, useState } from 'react';
-import {
- ActivityIndicator,
- Alert,
- Keyboard,
- KeyboardAvoidingView,
- Platform,
- Pressable,
- ScrollView,
- Text,
- TextInput,
- TouchableWithoutFeedback,
- View,
-} from 'react-native';
-
-export default function EditTask() {
- const { tId } = useLocalSearchParams<{ tId: string }>();
- const [task, SetTask] = useState(null);
- const [isSaving, SetIsSaving] = useState(false);
-
-
- const GetTask = async (tId: string) => {
- const { data, error } = await supabase.from("tasks").select("*").eq("tId", tId).single();
-
- if (error) {
- Alert.alert("Task could not be fetched, please try again");
- return;
- }
-
- SetTask(data ?? null);
- }
-
- useFocusEffect(
- useCallback(() => {
- if (tId) {
- GetTask(tId);
- }
- }, [tId])
- );
-
- const EditTask = async () => {
- if (!task) return;
-
- if(task.title.trim() === '') {
- Alert.alert("Title is required!");
- return;
- }
-
- const { data, error: userError } = await supabase.auth.getUser();
-
- if(userError || !data.user) {
- router.replace("../createUser");
- return;
- }
-
- SetIsSaving(true);
-
- const { error: dbError } = await supabase.from("tasks").update({
- title: task.title,
- description: task.description,
- isCompleted: task.isCompleted,
- lastChanged: new Date().toISOString(),
- uId: data.user.id,
- aId: task.aId,
- }).eq("tId", tId);
-
- SetIsSaving(false);
-
- if (dbError) {
- Alert.alert("Task could not be edited, please try again");
- return;
- }
-
- if (task.aId) {
- try {
- await CheckAssignmentCompletion(task.aId);
- } catch {
- Alert.alert("Failed to update assignment completion state");
- }
- }
-
- Alert.alert("Task successfully edited!");
- router.back();
- };
-
- const inputClassName =
- 'rounded-2xl border border-app-border bg-app-subtle px-4 py-3 text-base text-text-main';
-
- const labelClassName = 'mb-2 text-sm font-semibold text-text-secondary';
-
-
- return (
- <>
-
-
- {!task ? (
-
-
-
- Task not found
-
-
- The task could not be loaded.
-
-
- router.back()}
- >
-
- Go back
-
-
-
-
- ) : (
-
-
-
-
-
- Edit Task
-
-
- Update the task details and completion state.
-
-
-
-
-
- Title
-
- SetTask((prev) => (prev ? { ...prev, title: text } : prev))
- }
- returnKeyType="next"
- />
-
-
-
- Description
-
- SetTask((prev) =>
- prev ? { ...prev, description: text } : prev
- )
- }
- multiline
- textAlignVertical="top"
- />
-
-
-
- SetTask((prev) =>
- prev ? { ...prev, isCompleted: !prev.isCompleted } : prev
- )
- }
- disabled={isSaving}
- className={`mb-6 flex-row items-center rounded-2xl border p-4 ${
- task.isCompleted
- ? 'border-accent bg-accent-soft'
- : 'border-app-border bg-app-subtle'
- }`}
- >
-
- {task.isCompleted && (
-
- ✓
-
- )}
-
-
-
-
- Mark as completed
-
-
- You can change this again later.
-
-
-
-
-
- {isSaving ? (
-
-
-
- Saving...
-
-
- ) : (
-
- Save Changes
-
- )}
-
-
- router.back()}
- disabled={isSaving}
- >
-
- Cancel
-
-
-
-
-
-
- )}
- >
- );
-}
\ No newline at end of file
diff --git a/app/task/tasks.tsx b/app/task/tasks.tsx
deleted file mode 100644
index 7e08755..0000000
--- a/app/task/tasks.tsx
+++ /dev/null
@@ -1,277 +0,0 @@
-import { defaultStyles } from '@/constants/defaultStyles';
-import { CheckAssignmentCompletion } from '@/lib/progress';
-import { supabase } from '@/lib/supabase';
-import type { Task } from '@/lib/types';
-import { Ionicons } from '@expo/vector-icons';
-import { Session } from '@supabase/supabase-js';
-import { router, Stack, useFocusEffect } from 'expo-router';
-import { useCallback, useEffect, useState } from 'react';
-import {
- Alert,
- Pressable,
- SectionList,
- Text,
- View,
-} from 'react-native';
-
-export default function Tasks() {
- const [tasks, SetTasks] = useState([]);
- const [session, SetSession] = useState(null);
-
- const taskSections = [
- {
- title: 'Upcoming Tasks',
- data: tasks.filter((task) => !task.isCompleted),
- emptyMessage: 'No upcoming tasks',
- },
- {
- title: 'Completed Tasks',
- data: tasks.filter((task) => task.isCompleted),
- emptyMessage: 'No completed tasks',
- },
- ];
-
- useEffect(() => {
- supabase.auth
- .getSession()
- .then(({ data }) => SetSession(data.session ?? null));
-
- const { data: sub } = supabase.auth.onAuthStateChange(
- (_event, newSession) => {
- SetSession(newSession);
- }
- );
-
- return () => sub.subscription.unsubscribe();
- }, []);
-
- const GetTasks = async () => {
- const { data, error } = await supabase.from('tasks').select('*');
-
- if (error) {
- Alert.alert('Tasks could not be fetched, please try again');
- return;
- }
-
- SetTasks(data ?? []);
- };
-
- useFocusEffect(
- useCallback(() => {
- if (session) {
- GetTasks();
- }
- }, [session])
- );
-
- const DeleteTask = async (tId: string, aId: string) => {
- Alert.alert(
- 'Delete Task',
- 'Are you sure you want to delete this task?',
- [
- {
- text: 'Cancel',
- style: 'cancel',
- },
- {
- text: 'Delete',
- style: 'destructive',
- onPress: async () => {
- const { error } = await supabase
- .from('tasks')
- .delete()
- .eq('tId', tId);
-
- if (error) {
- Alert.alert('Task could not be deleted, please try again');
- return;
- }
-
- Alert.alert('Task deleted successfully!');
-
- try {
- await CheckAssignmentCompletion(aId);
- } catch {
- Alert.alert("Failed to update assignment completion state");
- }
-
- GetTasks();
- },
- },
- ]
- );
- };
-
- return (
-
- (
-
-
-
-
-
- await supabase.auth.signOut()}
- >
-
- Logout
-
-
-
- ),
- }}
- />
-
-
-
-
- Tasks
-
-
- Break assignments into small steps and keep your progress clear.
-
-
-
- router.push('/task/createTask')}
- >
-
- Create Task
-
-
-
- item.tId}
- showsVerticalScrollIndicator={false}
- stickySectionHeadersEnabled={false}
- contentContainerStyle={{
- paddingBottom: 32,
- }}
- renderSectionHeader={({ section: { title, data } }) => (
-
-
- {title}
-
-
-
-
- {data.length}
-
-
-
- )}
- renderItem={({ item }) => {
- const isOwner = session?.user.id === item.uId;
-
- return (
-
-
- router.push({
- pathname: '/task/viewDetailsTask',
- params: { tId: item.tId },
- })
- }
- >
-
-
- {item.isCompleted && (
-
- ✓
-
- )}
-
-
-
-
- {item.title}
-
-
- {item.description ? (
-
- {item.description}
-
- ) : null}
-
-
-
- {item.isCompleted ? 'Completed' : 'In progress'}
-
-
-
-
-
-
- {isOwner && (
-
-
- router.push({
- pathname: '/task/editTask',
- params: { tId: item.tId },
- })
- }
- >
-
- Edit
-
-
-
- DeleteTask(item.tId, item.aId)}
- >
-
- Delete
-
-
-
- )}
-
- );
- }}
- renderSectionFooter={({ section }) =>
- section.data.length === 0 ? (
-
-
- {section.emptyMessage}
-
-
- Tasks for this assignment will show up here.
-
-
- ) : (
-
- )
- }
- />
-
-
- );
-}
\ No newline at end of file
diff --git a/app/task/createTask.tsx b/app/task/upsertTask.tsx
similarity index 66%
rename from app/task/createTask.tsx
rename to app/task/upsertTask.tsx
index 3b70e84..6d67b96 100644
--- a/app/task/createTask.tsx
+++ b/app/task/upsertTask.tsx
@@ -1,8 +1,9 @@
import { defaultStyles } from '@/constants/defaultStyles';
import { CheckAssignmentCompletion } from '@/lib/progress';
import { supabase } from '@/lib/supabase';
+import type { Task } from '@/lib/types';
import { router, Stack, useLocalSearchParams } from 'expo-router';
-import { useState } from 'react';
+import { useEffect, useState } from 'react';
import {
ActivityIndicator,
Alert,
@@ -17,15 +18,57 @@ import {
View,
} from 'react-native';
-export default function CreateTask() {
- const aId = (useLocalSearchParams().aId as string) ?? null;
+export default function UpsertTask() {
+ const { tId, aId: routeAId } = useLocalSearchParams<{
+ tId?: string;
+ aId?: string;
+ }>();
+
+ const isEditMode = Boolean(tId);
const [title, SetTitle] = useState('');
const [description, SetDescription] = useState('');
const [isCompleted, SetIsCompleted] = useState(false);
+ const [assignmentId, SetAssignmentId] = useState(routeAId ?? null);
+
+ const [isLoading, SetIsLoading] = useState(isEditMode);
const [isSaving, SetIsSaving] = useState(false);
- const CreateTask = async () => {
+ useEffect(() => {
+ if (!isEditMode || !tId) {
+ SetIsLoading(false);
+ return;
+ }
+
+ const loadTask = async () => {
+ SetIsLoading(true);
+
+ const { data, error } = await supabase
+ .from('tasks')
+ .select('*')
+ .eq('tId', tId)
+ .single();
+
+ SetIsLoading(false);
+
+ if (error || !data) {
+ Alert.alert('Task could not be loaded, please try again');
+ router.back();
+ return;
+ }
+
+ const task = data as Task;
+
+ SetTitle(task.title ?? '');
+ SetDescription(task.description ?? '');
+ SetIsCompleted(task.isCompleted ?? false);
+ SetAssignmentId(task.aId ?? routeAId ?? null);
+ };
+
+ loadTask();
+ }, [isEditMode, tId, routeAId]);
+
+ const handleSubmit = async () => {
if (title.trim() === '') {
Alert.alert('Title is required!');
return;
@@ -34,42 +77,55 @@ export default function CreateTask() {
const { data, error: userError } = await supabase.auth.getUser();
if (userError || !data.user) {
- router.replace('../createUser');
+ router.replace('/login');
+ return;
+ }
+
+ if (!assignmentId) {
+ Alert.alert('Missing assignment', 'This task is not linked to an assignment.');
return;
}
SetIsSaving(true);
- const { error: dbError } = await supabase.from('tasks').insert({
+ const payload = {
title: title.trim(),
description: description.trim(),
isCompleted,
lastChanged: new Date().toISOString(),
uId: data.user.id,
- aId,
- });
+ aId: assignmentId,
+ };
- if (dbError) {
+ const result =
+ isEditMode && tId
+ ? await supabase.from('tasks').update(payload).eq('tId', tId)
+ : await supabase.from('tasks').insert(payload);
+
+ if (result.error) {
SetIsSaving(false);
- Alert.alert('Task could not be created, please try again');
+ Alert.alert(
+ isEditMode
+ ? 'Task could not be updated, please try again'
+ : 'Task could not be created, please try again'
+ );
return;
}
- Alert.alert('Task successfully created!');
-
- if (aId) {
- try {
- await CheckAssignmentCompletion(aId);
- } catch {
- Alert.alert("Failed to update assignment completion state");
- }
+ try {
+ await CheckAssignmentCompletion(assignmentId);
+ } catch {
+ SetIsSaving(false);
+ Alert.alert('Failed to update assignment completion state');
+ return;
}
- SetTitle('');
- SetDescription('');
- SetIsCompleted(false);
SetIsSaving(false);
+ Alert.alert(
+ isEditMode ? 'Task successfully updated!' : 'Task successfully created!'
+ );
+
router.back();
};
@@ -78,11 +134,19 @@ export default function CreateTask() {
const labelClassName = 'mb-2 text-sm font-semibold text-text-secondary';
+ if (isLoading) {
+ return (
+
+
+
+ );
+ }
+
return (
<>
@@ -104,10 +168,12 @@ export default function CreateTask() {
>
- Create Task
+ {isEditMode ? 'Edit Task' : 'Create Task'}
- Add a small step to move this assignment forward.
+ {isEditMode
+ ? 'Update this task and keep your assignment moving forward.'
+ : 'Add a small step to move this assignment forward.'}
@@ -117,6 +183,7 @@ export default function CreateTask() {
{isSaving ? (
- Creating...
+ {isEditMode ? 'Saving...' : 'Creating...'}
) : (
- Create Task
+ {isEditMode ? 'Save Changes' : 'Create Task'}
)}
diff --git a/app/task/viewDetailsTask.tsx b/app/task/viewDetailsTask.tsx
index 9c44b18..605da33 100644
--- a/app/task/viewDetailsTask.tsx
+++ b/app/task/viewDetailsTask.tsx
@@ -37,7 +37,6 @@ export default function ViewDetailsTask() {
.single();
if (error || !data) {
- console.log('GetTask error:', error);
Alert.alert('Task could not be fetched, please try again');
return;
}
@@ -52,7 +51,6 @@ export default function ViewDetailsTask() {
.single();
if (assignmentError || !assignmentData) {
- console.log('GetTaskAssignment error:', assignmentError);
setContextMeta({
subjectTitle: 'Unknown Subject',
assignmentTitle: 'Unknown Assignment',
@@ -69,7 +67,6 @@ export default function ViewDetailsTask() {
.single();
if (subjectError || !subjectData) {
- console.log('GetTaskSubject error:', subjectError);
setContextMeta({
subjectTitle: 'Unknown Subject',
assignmentTitle: assignmentData.title ?? 'Unknown Assignment',
@@ -275,7 +272,7 @@ export default function ViewDetailsTask() {
className="mr-3 flex-1 items-center justify-center rounded-2xl border border-app-border bg-app-subtle py-3"
onPress={() =>
router.push({
- pathname: '/task/editTask',
+ pathname: '/task/upsertTask',
params: { tId: task.tId },
})
}
@@ -298,4 +295,4 @@ export default function ViewDetailsTask() {
);
-}
\ No newline at end of file
+}