From ae613f8707cbe5586565817623c69dea8e52bd0f Mon Sep 17 00:00:00 2001 From: Fhj0607 Date: Fri, 1 May 2026 12:36:58 +0200 Subject: [PATCH] redesigned completion and reopening subjects/assignments/tasks and how it is rendered --- app/(tabs)/subjects.tsx | 197 ++++++++++++++--------- app/assignment/viewDetailsAssignment.tsx | 78 ++++++--- app/subject/viewDetailsSubject.tsx | 76 +++++++-- app/task/viewDetailsTask.tsx | 12 -- 4 files changed, 239 insertions(+), 124 deletions(-) diff --git a/app/(tabs)/subjects.tsx b/app/(tabs)/subjects.tsx index f3cd33c..4998f62 100644 --- a/app/(tabs)/subjects.tsx +++ b/app/(tabs)/subjects.tsx @@ -1,18 +1,26 @@ -import { SUBJECT_COLORS } from '@/lib/subjectColors'; +import { SUBJECT_COLORS, type SubjectColor } from '@/lib/subjectColors'; import { supabase } from '@/lib/supabase'; import { Subject } from '@/lib/types'; import { Session } from '@supabase/supabase-js'; import { router, Stack, useFocusEffect } from 'expo-router'; import { useCallback, useEffect, useState } from 'react'; -import { ActivityIndicator, Alert, Pressable, ScrollView, Text, View } from 'react-native'; - -import type { SubjectColor } from '@/lib/subjectColors'; +import { + ActivityIndicator, + Alert, + Pressable, + ScrollView, + Text, + View, +} from 'react-native'; export default function Subjects() { const [subjects, SetSubjects] = useState([]); const [session, SetSession] = useState(null); const [isLoading, SetIsLoading] = useState(true); + const activeSubjects = subjects.filter((subject) => subject.isActive); + const inactiveSubjects = subjects.filter((subject) => !subject.isActive); + useEffect(() => { supabase.auth.getSession().then(({ data }) => { SetSession(data.session ?? null); @@ -59,6 +67,73 @@ export default function Subjects() { }, [session]) ); + const RenderSubjectCard = (subject: Subject) => { + const colorKey: SubjectColor = subject.color ?? 'slate'; + const colorSet = SUBJECT_COLORS[colorKey]; + const firstLetter = subject.title?.trim().charAt(0).toUpperCase() || '?'; + + return ( + + router.push({ + pathname: '/subject/viewDetailsSubject', + params: { sId: subject.sId }, + }) + } + > + + + + {firstLetter} + + + + + + {subject.title} + + + + {subject.description || 'No description added.'} + + + + + + + {subject.isActive ? 'Active' : 'Inactive'} + + + + + + ); + }; + return ( - - Subjects - - Pick a subject to manage assignments and tasks. - - - {isLoading ? ( @@ -111,74 +179,55 @@ export default function Subjects() { ) : ( - {subjects.map((subject) => { - const colorKey: SubjectColor = subject.color ?? 'slate'; - const colorSet = SUBJECT_COLORS[colorKey]; + + + Active Subjects + - const firstLetter = - subject.title?.trim().charAt(0).toUpperCase() || '?'; + + + {activeSubjects.length} + + + - return ( - - router.push({ - pathname: '/subject/viewDetailsSubject', - params: { sId: subject.sId }, - }) - } - > - - - - {firstLetter} - - + {activeSubjects.length === 0 ? ( + + + No active subjects + + + Subjects with ongoing work will show up here. + + + ) : ( + activeSubjects.map(RenderSubjectCard) + )} - - - {subject.title} - + + + Inactive Subjects + - - {subject.description || 'No description added.'} - - + + + {inactiveSubjects.length} + + + - - - - {subject.isActive ? 'Active' : 'Inactive'} - - - - - - ); - })} + {inactiveSubjects.length === 0 ? ( + + + No inactive subjects + + + Completed or paused subjects will show up here. + + + ) : ( + inactiveSubjects.map(RenderSubjectCard) + )} )} diff --git a/app/assignment/viewDetailsAssignment.tsx b/app/assignment/viewDetailsAssignment.tsx index 6e24c45..ccb0def 100644 --- a/app/assignment/viewDetailsAssignment.tsx +++ b/app/assignment/viewDetailsAssignment.tsx @@ -167,6 +167,32 @@ export default function ViewDetailsAssignment() { ) } + const ToggleTaskCompletion = async (task: Task) => { + const nextIsCompleted = !task.isCompleted; + + const { error } = await supabase + .from("tasks") + .update({ + isCompleted: nextIsCompleted, + lastChanged: new Date().toISOString(), + }) + .eq("tId", task.tId); + + if (error) { + Alert.alert("Task could not be updated, please try again"); + return; + } + + try { + await CheckAssignmentCompletion(task.aId); + } catch { + Alert.alert("Failed to update assignment completion state"); + } + + await GetTasks(task.aId); + await GetAssignment(task.aId); + } + const colorSet = getSubjectColorSet(subjectMeta.color); const completedTasks = tasks.filter((task) => task.isCompleted).length; @@ -234,7 +260,7 @@ export default function ViewDetailsAssignment() { item.tId} showsVerticalScrollIndicator={false} stickySectionHeadersEnabled={false} @@ -248,18 +274,6 @@ export default function ViewDetailsAssignment() { }} > - - {assignment.isCompleted && ( - - )} - - {assignment.title} @@ -399,18 +413,6 @@ export default function ViewDetailsAssignment() { } > - - {item.isCompleted && ( - - )} - - + ToggleTaskCompletion(item)} + > + + {item.isCompleted ? 'Reopen' : 'Complete'} + + + @@ -459,6 +474,19 @@ export default function ViewDetailsAssignment() { ); }} + ListEmptyComponent={ + + + No tasks needed yet + + + Add tasks if this assignment needs smaller steps. + + + } renderSectionFooter={({ section }) => section.data.length === 0 ? ( diff --git a/app/subject/viewDetailsSubject.tsx b/app/subject/viewDetailsSubject.tsx index 5f55aec..08001ff 100644 --- a/app/subject/viewDetailsSubject.tsx +++ b/app/subject/viewDetailsSubject.tsx @@ -78,6 +78,32 @@ export default function ViewDetailsSubject() { SetAssignments(data ?? []); }; + const ToggleAssignmentCompletion = async (assignment: Assignment) => { + const nextIsCompleted = !assignment.isCompleted; + + const { error } = await supabase + .from('assignments') + .update({ + isCompleted: nextIsCompleted, + lastChanged: new Date().toISOString(), + }) + .eq('aId', assignment.aId); + + if (error) { + Alert.alert('Assignment could not be updated, please try again'); + return; + } + + try { + await CheckSubjectCompletion(assignment.sId); + } catch { + Alert.alert('Failed to update subject status'); + } + + await GetAssignments(assignment.sId); + await GetSubject(assignment.sId); + }; + useFocusEffect( useCallback(() => { if (!session || !sId) { @@ -267,7 +293,7 @@ export default function ViewDetailsSubject() { paddingTop: 20, paddingBottom: 32, }} - sections={assignmentSections} + sections={totalAssignments === 0 ? [] : assignmentSections} keyExtractor={(item) => item.aId} showsVerticalScrollIndicator={false} stickySectionHeadersEnabled={false} @@ -396,7 +422,7 @@ export default function ViewDetailsSubject() { } > - Create Assignment + Add Assignment @@ -422,15 +448,16 @@ export default function ViewDetailsSubject() { borderColor: colorSet.strong, }} > - - router.push({ - pathname: '/assignment/viewDetailsAssignment', - params: { aId: item.aId }, - }) - } - > - + + + router.push({ + pathname: '/assignment/viewDetailsAssignment', + params: { aId: item.aId }, + }) + } + > - - + + {isOwner && ( + ToggleAssignmentCompletion(item)} + > + + {item.isCompleted ? 'Reopen' : 'Complete'} + + + @@ -485,6 +525,16 @@ export default function ViewDetailsSubject() { ); }} + ListEmptyComponent={ + + + No assignments yet + + + Add one when this subject has work to track. + + + } renderSectionFooter={({ section }) => section.data.length === 0 ? ( diff --git a/app/task/viewDetailsTask.tsx b/app/task/viewDetailsTask.tsx index 9c44b18..e9da2d0 100644 --- a/app/task/viewDetailsTask.tsx +++ b/app/task/viewDetailsTask.tsx @@ -212,18 +212,6 @@ export default function ViewDetailsTask() { }} > - - {task.isCompleted && ( - - )} - -