From e8f7f45f11cf12cb6ee70314c5f2825b501ea771 Mon Sep 17 00:00:00 2001 From: Fhj0607 Date: Mon, 27 Apr 2026 16:30:43 +0200 Subject: [PATCH] createSubject and editSubject removed, upsertSubject added. Tasks and assignments removed from top-level tabs and now only accessible through Subjects -> Assignments -> Tasks. More styling in general. --- app/(tabs)/_layout.tsx | 11 +- app/(tabs)/subjects.tsx | 383 +++++---------- app/assignment/_layout.tsx | 1 + app/{(tabs) => assignment}/assignments.tsx | 0 app/assignment/viewDetailsAssignment.tsx | 342 +++++++++---- app/createUser.tsx | 159 ++++-- app/login.tsx | 142 ++++-- app/subject/_layout.tsx | 3 +- app/subject/createSubject.tsx | 195 -------- app/subject/editSubject.tsx | 125 ----- app/subject/upsertSubject.tsx | 351 +++++++++++++ app/subject/viewDetailsSubject.tsx | 544 +++++++++++++++------ app/task/_layout.tsx | 1 + app/task/editTask.tsx | 220 +++++++-- app/{(tabs) => task}/tasks.tsx | 0 app/task/viewDetailsTask.tsx | 224 ++++++--- lib/subjectColors.ts | 53 ++ lib/types.ts | 3 + 18 files changed, 1729 insertions(+), 1028 deletions(-) rename app/{(tabs) => assignment}/assignments.tsx (100%) delete mode 100644 app/subject/createSubject.tsx delete mode 100644 app/subject/editSubject.tsx create mode 100644 app/subject/upsertSubject.tsx rename app/{(tabs) => task}/tasks.tsx (100%) create mode 100644 lib/subjectColors.ts diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx index 5876d15..d9a261e 100644 --- a/app/(tabs)/_layout.tsx +++ b/app/(tabs)/_layout.tsx @@ -56,14 +56,15 @@ export default function TabLayout() { } if (!session) { - return ; + return ; } return ( - - - - + + diff --git a/app/(tabs)/subjects.tsx b/app/(tabs)/subjects.tsx index f5f5f79..86564b8 100644 --- a/app/(tabs)/subjects.tsx +++ b/app/(tabs)/subjects.tsx @@ -1,40 +1,21 @@ -import { defaultStyles } from '@/constants/defaultStyles'; +import { SUBJECT_COLORS } from '@/lib/subjectColors'; import { supabase } from '@/lib/supabase'; -import type { Assignment, Subject } from '@/lib/types'; -import { Ionicons } from '@expo/vector-icons'; +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 { - Alert, - Pressable, - SectionList, - Text, - View, -} from 'react-native'; +import { Alert, Pressable, ScrollView, Text, View } from 'react-native'; + +import type { SubjectColor } from '@/lib/subjectColors'; export default function Subjects() { const [subjects, SetSubjects] = useState([]); - const [assignmentsBySubject, SetAssignmentsBySubject] = useState>({}); const [session, SetSession] = useState(null); - const subjectSections = [ - { - title: 'Active Subjects', - data: subjects.filter((subject) => subject.isActive), - emptyMessage: 'No active subjects', - }, - { - title: 'Inactive Subjects', - data: subjects.filter((subject) => !subject.isActive), - emptyMessage: 'No inactive subjects', - }, - ]; - useEffect(() => { - supabase.auth - .getSession() - .then(({ data }) => SetSession(data.session ?? null)); + supabase.auth.getSession().then(({ data }) => { + SetSession(data.session ?? null); + }); const { data: sub } = supabase.auth.onAuthStateChange( (_event, newSession) => { @@ -46,47 +27,20 @@ export default function Subjects() { }, []); const GetSubjects = async () => { - const { data: subjectsData, error: subjectsError } = await supabase + if (!session?.user.id) return; + + const { data, error } = await supabase .from('subjects') .select('*') + .eq('uId', session.user.id) .order('lastChanged', { ascending: false }); - if (subjectsError) { + if (error) { Alert.alert('Subjects could not be fetched, please try again'); return; } - const subjectRows = subjectsData ?? []; - SetSubjects(subjectsData ?? []); - - if (subjectRows.length === 0) { - SetAssignmentsBySubject({}); - return; - } - - const sIds = subjectRows.map((subject) => subject.sId); - - const { data: assignmentsData, error: assignmentsError } = await supabase - .from('assignments') - .select('*') - .in('sId', sIds); - - if (assignmentsError) { - Alert.alert('Subject assignments could not be fetched, please try again'); - SetAssignmentsBySubject({}); - return; - } - - const groupedAssignments: Record = {}; - - for (const assignment of assignmentsData ?? []) { - if (!groupedAssignments[assignment.sId]) { - groupedAssignments[assignment.sId] = []; - } - groupedAssignments[assignment.sId].push(assignment); - } - - SetAssignmentsBySubject(groupedAssignments); + SetSubjects((data as Subject[]) ?? []); }; useFocusEffect( @@ -97,232 +51,131 @@ export default function Subjects() { }, [session]) ); - const DeleteSubject = async (sId: string) => { - Alert.alert( - 'Delete Subject', - 'Are you sure you want to delete this subject?', - [ - { - text: 'Cancel', - style: 'cancel', - }, - { - text: 'Delete', - style: 'destructive', - onPress: async () => { - const { error } = await supabase - .from('subjects') - .delete() - .eq('sId', sId); - - if (error) { - Alert.alert('Subject could not be deleted, please try again'); - return; - } - - Alert.alert('Subject deleted successfully!'); - GetSubjects(); - }, - }, - ] - ); - }; - return ( ( - - - - - - await supabase.auth.signOut()} - > - - Logout - - - + await supabase.auth.signOut()} + > + + Logout + + ), }} /> - + - - Subjects - + Subjects - Organize your study work by subject, then break it into assignments - and tasks. + Pick a subject to manage assignments and tasks. + {subjects.length === 0 ? ( + + + No subjects yet + + + Create your first subject to get started. + + + ) : ( + + {subjects.map((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'} + + + + + + ); + })} + + )} + router.push('/subject/createSubject')} + className="mt-2 h-14 items-center justify-center rounded-2xl bg-accent" + onPress={() => router.push('/subject/upsertSubject')} > Create Subject - - item.sId} - showsVerticalScrollIndicator={false} - stickySectionHeadersEnabled={false} - contentContainerStyle={{ - paddingBottom: 32, - }} - renderSectionHeader={({ section: { title, data } }) => ( - - - {title} - - - - - {data.length} - - - - )} - renderItem={({ item }) => { - const isOwner = session?.user.id === item.uId; - - const subjectAssignments = assignmentsBySubject[item.sId] ?? []; - const progress = subjectAssignments.length === 0 ? 0 : Math.round((subjectAssignments.filter(assignment => assignment.isCompleted).length / subjectAssignments.length) * 100); - - return ( - - - router.push({ - pathname: '/subject/viewDetailsSubject', - params: { sId: item.sId }, - }) - } - > - - - {item.isActive && ( - - ✓ - - )} - - - - - {item.title} - - - {item.description ? ( - - {item.description} - - ) : null} - - - - {item.isActive ? 'Active' : 'Inactive'} - - - - {progress}% - - - - - - - - - - {isOwner && ( - - - router.push({ - pathname: '/subject/editSubject', - params: { sId: item.sId }, - }) - } - > - - Edit - - - - DeleteSubject(item.sId)} - > - - Delete - - - - )} - - ); - }} - renderSectionFooter={({ section }) => - section.data.length === 0 ? ( - - - {section.emptyMessage} - - - Subjects you create will show up here. - - - ) : ( - - ) - } - /> - + ); } \ No newline at end of file diff --git a/app/assignment/_layout.tsx b/app/assignment/_layout.tsx index 380a294..87149d5 100644 --- a/app/assignment/_layout.tsx +++ b/app/assignment/_layout.tsx @@ -3,6 +3,7 @@ import { Stack } from "expo-router"; export default function AssignmentLayout() { return ( + diff --git a/app/(tabs)/assignments.tsx b/app/assignment/assignments.tsx similarity index 100% rename from app/(tabs)/assignments.tsx rename to app/assignment/assignments.tsx diff --git a/app/assignment/viewDetailsAssignment.tsx b/app/assignment/viewDetailsAssignment.tsx index e7dc364..158c5c0 100644 --- a/app/assignment/viewDetailsAssignment.tsx +++ b/app/assignment/viewDetailsAssignment.tsx @@ -1,11 +1,10 @@ -import { defaultStyles } from '@/constants/defaultStyles'; import { CheckAssignmentCompletion, CheckSubjectCompletion } from '@/lib/progress'; import { supabase } from '@/lib/supabase'; import type { Assignment, Task } from '@/lib/types'; import { Session } from '@supabase/supabase-js'; import { router, Stack, useFocusEffect, useLocalSearchParams } from 'expo-router'; import { useCallback, useEffect, useState } from 'react'; -import { Alert, Button, Pressable, SectionList, Text, View } from "react-native"; +import { Alert, Pressable, SectionList, Text, View } from "react-native"; export default function ViewDetailsAssignment() { const { aId } = useLocalSearchParams<{ aId: string }>(); @@ -134,115 +133,270 @@ export default function ViewDetailsAssignment() { ) } - const progress = tasks.length === 0 ? 0 : Math.round((tasks.filter(task => task.isCompleted).length / tasks.length) * 100); + const progress = + tasks.length === 0 + ? 0 + : Math.round( + (tasks.filter((task) => task.isCompleted).length / tasks.length) * 100 + ); + + if (!assignment) { + return ( + + + + + + Assignment not found + + + The assignment could not be loaded. + + + router.back()} + > + Go back + + + + ); + } return ( - + { - return ( - -