diff --git a/frontend/apps/mobile/app/(app)/(tabs)/event/[id].tsx b/frontend/apps/mobile/app/(app)/(tabs)/event/[id].tsx index 97774def..95aa807f 100644 --- a/frontend/apps/mobile/app/(app)/(tabs)/event/[id].tsx +++ b/frontend/apps/mobile/app/(app)/(tabs)/event/[id].tsx @@ -10,9 +10,7 @@ import { import { useState } from "react"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { useLocalSearchParams, useRouter } from "expo-router"; -import { - useGetEventOccurrencesById, -} from "@skillspark/api-client"; +import { useGetEventOccurrencesById } from "@skillspark/api-client"; import type { EventOccurrence } from "@skillspark/api-client"; import MaterialIcons from "@expo/vector-icons/MaterialIcons"; import { AppColors } from "@/constants/theme"; @@ -35,267 +33,171 @@ function EventOccurrenceDetail({ occurrence }: { occurrence: EventOccurrence }) const address = formatAddress(occurrence); return ( - + - - - {occurrence.event.presigned_url ? ( - - ) : ( - - )} - router.navigate('/')} - activeOpacity={0.7} - style={{ - position: "absolute", - top: 16, - left: 16, - zIndex: 10, - flexDirection: "row", - alignItems: "center", - backgroundColor: "#fff", - borderRadius: 999, - paddingHorizontal: 16, - paddingVertical: 10, - shadowColor: "#000", - shadowOpacity: 0.15, - shadowRadius: 8, - elevation: 10, - }} - > - - Back - - - - - - + {/* Hero image */} + + {occurrence.event.presigned_url ? ( + + ) : ( + + )} + router.navigate('/')} + activeOpacity={0.7} + className="absolute top-4 left-4 z-10 flex-row items-center bg-white rounded-full px-4 py-2.5 elevation-10" + style={{ shadowColor: "#000", shadowOpacity: 0.15, shadowRadius: 8 }} + > + + Back + - - {occurrence.event.title} - + + {/* Content card */} - + {/* Drag handle */} + + + + - {address} + {occurrence.event.title} - - - { - if (!descriptionExpanded) { - setDescriptionTruncated(e.nativeEvent.lines.length >= 5); - } - }} - style={{ - fontSize: 14, - color: AppColors.secondaryText, - lineHeight: 22, - marginBottom: descriptionTruncated ? 4 : 18, - }} - > - {occurrence.event.description} - - {descriptionTruncated && ( - setDescriptionExpanded((prev) => !prev)} style={{ marginBottom: 14 }}> - - {descriptionExpanded ? "See less" : "See more"} + + + + {address} - - )} - - - {occurrence.event.category?.map((cat) => ( - - {cat} - - ))} - - - {occurrence.price} THB - /Session + - - - - - { + if (!descriptionExpanded) { + setDescriptionTruncated(e.nativeEvent.lines.length >= 5); + } }} + className={`text-sm leading-[22px] ${descriptionTruncated ? "mb-1" : "mb-[18px]"}`} + style={{ color: AppColors.secondaryText }} > - {duration} + {occurrence.event.description} - - - 8 min - - + {descriptionTruncated && ( + setDescriptionExpanded((prev) => !prev)} className="mb-3.5"> + + {descriptionExpanded ? "See less" : "See more"} + + + )} + + + {occurrence.event.category?.map((cat) => ( + + {cat} + + ))} + + + {occurrence.price} THB + /Session + - - - - - - - Home - - - - - + + {/* Divider */} + + + {/* Bottom section */} + + + + {duration} + + + + 8 min + + - - - Location + + + + + + + + Home + + + + + + + + + Location + + {}} + activeOpacity={0.7} + className="rounded-2xl px-[26px] py-3.5" + style={{ backgroundColor: AppColors.primaryText }} + > + Register + - {}} - activeOpacity={0.7} - style={{ - backgroundColor: AppColors.primaryText, - borderRadius: 16, - paddingHorizontal: 26, - paddingVertical: 14, - }} - > - Register - - - + ); @@ -307,7 +209,7 @@ export default function EventOccurrenceScreen() { if (isLoading) { return ( - + ); @@ -315,8 +217,8 @@ export default function EventOccurrenceScreen() { if (error || !response || response.status !== 200) { return ( - - + + Event not found @@ -324,4 +226,4 @@ export default function EventOccurrenceScreen() { } return ; -} +} \ No newline at end of file diff --git a/frontend/apps/mobile/app/(app)/(tabs)/family/manage.tsx b/frontend/apps/mobile/app/(app)/(tabs)/family/manage.tsx index b816b7fc..eb13dee9 100644 --- a/frontend/apps/mobile/app/(app)/(tabs)/family/manage.tsx +++ b/frontend/apps/mobile/app/(app)/(tabs)/family/manage.tsx @@ -17,9 +17,8 @@ import { IconSymbol } from '@/components/ui/icon-symbol'; import { useQueryClient } from '@tanstack/react-query'; import { useCreateChild, useUpdateChild, useDeleteChild, getGetChildrenByGuardianIdQueryKey } from '@skillspark/api-client'; import { ChildProfileForm, MONTHS } from '@/components/ChildProfileForm'; - -// TODO: Replace with authenticated user's guardian ID -const GUARDIAN_ID = '88888888-8888-8888-8888-888888888888'; +import { useAuthContext } from '@/hooks/use-auth-context'; +import { ErrorScreen } from '@/components/ErrorScreen'; export default function ManageChildScreen() { const router = useRouter(); @@ -27,10 +26,10 @@ export default function ManageChildScreen() { const colorScheme = useColorScheme(); const insets = useSafeAreaInsets(); const theme = Colors[colorScheme ?? 'light']; + const { guardianId } = useAuthContext(); const isEditing = !!params.id; - // Initial State Setup const [firstName, setFirstName] = useState( params.name ? (params.name as string).split(' ')[0] : '' ); @@ -38,7 +37,6 @@ export default function ManageChildScreen() { params.name ? (params.name as string).split(' ').slice(1).join(' ') : '' ); - // Convert numeric month (1-12) to String Name if editing const initialMonthStr = params.birth_month ? MONTHS[parseInt(params.birth_month as string) - 1] : ''; @@ -55,10 +53,8 @@ export default function ManageChildScreen() { const [interests, setInterests] = useState(initialInterests); const [searchQuery, setSearchQuery] = useState(''); - const [showMonthDrop, setShowMonthDrop] = useState(false); const [showYearDrop, setShowYearDrop] = useState(false); - const [isSubmitting, setIsSubmitting] = useState(false); const queryClient = useQueryClient(); @@ -66,6 +62,10 @@ export default function ManageChildScreen() { const updateChildMutation = useUpdateChild(); const deleteChildMutation = useDeleteChild(); + if (!guardianId) { + return ; + } + const handleSave = async () => { if (!firstName || !birthYear || !birthMonth || !schoolId) { Alert.alert('Error', 'Please fill in all required fields (Name, Birth Date, School ID)'); @@ -78,7 +78,7 @@ export default function ManageChildScreen() { name, birth_year: parseInt(birthYear, 10), birth_month: MONTHS.indexOf(birthMonth) + 1, - guardian_id: GUARDIAN_ID, + guardian_id: guardianId, school_id: schoolId, interests, }; @@ -87,7 +87,7 @@ export default function ManageChildScreen() { } else { await createChildMutation.mutateAsync({ data: childData }); } - await queryClient.invalidateQueries({ queryKey: getGetChildrenByGuardianIdQueryKey(GUARDIAN_ID) }); + await queryClient.invalidateQueries({ queryKey: getGetChildrenByGuardianIdQueryKey(guardianId) }); router.back(); } catch (error) { console.error(error); @@ -109,14 +109,14 @@ export default function ManageChildScreen() { setIsSubmitting(true); try { await deleteChildMutation.mutateAsync({ id: params.id as string }); - await queryClient.invalidateQueries({ queryKey: getGetChildrenByGuardianIdQueryKey(GUARDIAN_ID) }); + await queryClient.invalidateQueries({ queryKey: getGetChildrenByGuardianIdQueryKey(guardianId) }); router.back(); } catch { Alert.alert('Error', 'Failed to delete.'); setIsSubmitting(false); } - } - } + }, + }, ] ); }; @@ -126,10 +126,13 @@ export default function ManageChildScreen() { - + router.back()} className="w-8 h-8 justify-center items-start"> @@ -167,8 +170,8 @@ export default function ManageChildScreen() { setShowYearDrop={setShowYearDrop} /> @@ -176,9 +179,8 @@ export default function ManageChildScreen() { {isSubmitting ? 'Saving...' : 'Save Changes'} - ); -} +} \ No newline at end of file diff --git a/frontend/apps/mobile/app/(app)/(tabs)/profile.tsx b/frontend/apps/mobile/app/(app)/(tabs)/profile.tsx index 91dba861..e4c3e8f8 100644 --- a/frontend/apps/mobile/app/(app)/(tabs)/profile.tsx +++ b/frontend/apps/mobile/app/(app)/(tabs)/profile.tsx @@ -83,7 +83,7 @@ export default function ProfileScreen() { /> )) ) : ( - No children found + No children found )} diff --git a/frontend/apps/mobile/components/ChildProfileForm.tsx b/frontend/apps/mobile/components/ChildProfileForm.tsx index 51e05d5f..bc9664ba 100644 --- a/frontend/apps/mobile/components/ChildProfileForm.tsx +++ b/frontend/apps/mobile/components/ChildProfileForm.tsx @@ -83,8 +83,9 @@ export function ChildProfileForm({ placeholder="Last Name" placeholderTextColor={AppColors.placeholderText} /> - - + + {/* Month picker */} + - {showMonthDrop && ( - + {MONTHS.map(m => ( )} - + {/* Year picker */} + - {showYearDrop && ( - + {YEARS.map(y => ( true} onMoveShouldSetResponder={() => true}> - + {filteredOptions.map(item => ( ); -} +} \ No newline at end of file diff --git a/frontend/apps/mobile/components/EventCard.tsx b/frontend/apps/mobile/components/EventCard.tsx index 27905f6c..802ddb97 100644 --- a/frontend/apps/mobile/components/EventCard.tsx +++ b/frontend/apps/mobile/components/EventCard.tsx @@ -28,14 +28,13 @@ export function EventCard({ pin }: EventCardProps) { return ( @@ -43,7 +42,7 @@ export function EventCard({ pin }: EventCardProps) { className="mr-[15px] h-[90px] w-[90px] items-center justify-center rounded-[10px]" style={{ backgroundColor: placeholderColor }} > - + @@ -52,7 +51,6 @@ export function EventCard({ pin }: EventCardProps) { {pin.members} members - {[1, 2, 3, 4, 5].map((star) => ( ))} - - + {pin.description} - + ); -} +} \ No newline at end of file diff --git a/frontend/apps/mobile/components/SchoolPicker.tsx b/frontend/apps/mobile/components/SchoolPicker.tsx index 6310f3fe..836be3ac 100644 --- a/frontend/apps/mobile/components/SchoolPicker.tsx +++ b/frontend/apps/mobile/components/SchoolPicker.tsx @@ -18,11 +18,10 @@ export function SchoolPicker({ value, onChange }: SchoolPickerProps) { const { data, isLoading, isError } = useGetAllSchools(); const schools = Array.isArray(data?.data) ? data.data : []; const selectedSchool = schools.find((s: School) => s.id === value); - const placeholderLabel = isLoading ? 'Loading schools...' : isError ? 'Failed to load schools' : 'Select School'; return ( - + - {showDrop && ( - + {schools.map(school => ( ); -} +} \ No newline at end of file