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 +}