11import React , { useState , useEffect , useContext } from 'react' ;
2- import { View , StyleSheet , FlatList , Alert } from 'react-native' ;
2+ import { View , StyleSheet , FlatList , Alert , ScrollView } from 'react-native' ;
33import { Button , Text , Card , ActivityIndicator , Appbar , FAB , Title , Paragraph } from 'react-native-paper' ;
44import { AuthContext } from '../context/AuthContext' ;
5- import { getGroupMembers , getGroupExpenses } from '../api/groups' ;
5+ import { getGroupMembers , getGroupExpenses , getOptimizedSettlements } from '../api/groups' ;
66
77const GroupDetailsScreen = ( { route, navigation } ) => {
88 const { groupId, groupName } = route . params ;
9- const { token } = useContext ( AuthContext ) ;
9+ const { token, user } = useContext ( AuthContext ) ;
1010 const [ members , setMembers ] = useState ( [ ] ) ;
1111 const [ expenses , setExpenses ] = useState ( [ ] ) ;
12+ const [ settlements , setSettlements ] = useState ( [ ] ) ;
1213 const [ isLoading , setIsLoading ] = useState ( true ) ;
1314
1415 const fetchData = async ( ) => {
1516 try {
1617 setIsLoading ( true ) ;
17- // Fetch members and expenses in parallel
18- const [ membersResponse , expensesResponse ] = await Promise . all ( [
18+ // Fetch members, expenses, and settlements in parallel
19+ const [ membersResponse , expensesResponse , settlementsResponse ] = await Promise . all ( [
1920 getGroupMembers ( token , groupId ) ,
2021 getGroupExpenses ( token , groupId ) ,
22+ getOptimizedSettlements ( token , groupId ) ,
2123 ] ) ;
2224 setMembers ( membersResponse . data ) ;
2325 setExpenses ( expensesResponse . data . expenses ) ;
26+ setSettlements ( settlementsResponse . data . optimizedSettlements || [ ] ) ;
2427 } catch ( error ) {
2528 console . error ( 'Failed to fetch group details:' , error ) ;
2629 Alert . alert ( 'Error' , 'Failed to fetch group details.' ) ;
@@ -36,22 +39,66 @@ const GroupDetailsScreen = ({ route, navigation }) => {
3639 }
3740 } , [ token , groupId ] ) ;
3841
39- const renderExpense = ( { item } ) => (
40- < Card style = { styles . card } >
41- < Card . Content >
42- < Title > { item . description } </ Title >
43- < Paragraph > Amount: ${ item . amount . toFixed ( 2 ) } </ Paragraph >
44- { /* The API doesn't provide the payer's name directly in the expense object */ }
45- { /* We would need to match the createdBy id with the members list */ }
46- { /* <Paragraph>Paid by: {getMemberName(item.createdBy)}</Paragraph> */ }
47- </ Card . Content >
48- </ Card >
49- ) ;
42+ const getMemberName = ( userId ) => {
43+ const member = members . find ( m => m . userId === userId ) ;
44+ return member ? member . user . name : 'Unknown' ;
45+ } ;
46+
47+ const renderExpense = ( { item } ) => {
48+ const userSplit = item . splits . find ( s => s . userId === user . _id ) ;
49+ const userShare = userSplit ? userSplit . amount : 0 ;
50+ const paidByMe = item . createdBy === user . _id ;
51+ const net = paidByMe ? item . amount - userShare : - userShare ;
52+
53+ let balanceText ;
54+ let balanceColor = 'black' ;
55+
56+ if ( net > 0 ) {
57+ balanceText = `You are owed $${ net . toFixed ( 2 ) } ` ;
58+ balanceColor = 'green' ;
59+ } else if ( net < 0 ) {
60+ balanceText = `You borrowed $${ Math . abs ( net ) . toFixed ( 2 ) } ` ;
61+ balanceColor = 'red' ;
62+ } else {
63+ balanceText = "You are settled for this expense." ;
64+ }
65+
66+ return (
67+ < Card style = { styles . card } >
68+ < Card . Content >
69+ < Title > { item . description } </ Title >
70+ < Paragraph > Amount: ${ item . amount . toFixed ( 2 ) } </ Paragraph >
71+ < Paragraph > Paid by: { getMemberName ( item . createdBy ) } </ Paragraph >
72+ < Paragraph style = { { color : balanceColor } } > { balanceText } </ Paragraph >
73+ </ Card . Content >
74+ </ Card >
75+ ) ;
76+ } ;
5077
5178 const renderMember = ( { item } ) => (
5279 < Paragraph style = { styles . memberText } > • { item . user . name } </ Paragraph >
5380 ) ;
5481
82+ const renderSettlementSummary = ( ) => {
83+ const userSettlements = settlements . filter ( s => s . fromUserId === user . _id ) ;
84+ const totalOwed = userSettlements . reduce ( ( sum , s ) => sum + s . amount , 0 ) ;
85+
86+ if ( userSettlements . length === 0 ) {
87+ return < Paragraph > You are all settled up in this group!</ Paragraph > ;
88+ }
89+
90+ return (
91+ < >
92+ < Title > You owe ${ totalOwed . toFixed ( 2 ) } overall</ Title >
93+ { userSettlements . map ( ( s , index ) => (
94+ < Paragraph key = { index } >
95+ - You owe { getMemberName ( s . toUserId ) } ${ s . amount . toFixed ( 2 ) }
96+ </ Paragraph >
97+ ) ) }
98+ </ >
99+ ) ;
100+ } ;
101+
55102 if ( isLoading ) {
56103 return (
57104 < View style = { styles . loaderContainer } >
@@ -67,7 +114,14 @@ const GroupDetailsScreen = ({ route, navigation }) => {
67114 < Appbar . Content title = { groupName } />
68115 </ Appbar . Header >
69116
70- < View style = { styles . contentContainer } >
117+ < ScrollView style = { styles . contentContainer } >
118+ < Card style = { styles . card } >
119+ < Card . Content >
120+ < Title > Settlement Summary</ Title >
121+ { renderSettlementSummary ( ) }
122+ </ Card . Content >
123+ </ Card >
124+
71125 < Card style = { styles . card } >
72126 < Card . Content >
73127 < Title > Members</ Title >
@@ -87,7 +141,7 @@ const GroupDetailsScreen = ({ route, navigation }) => {
87141 ListEmptyComponent = { < Text > No expenses recorded yet.</ Text > }
88142 contentContainerStyle = { { paddingBottom : 80 } } // To avoid FAB overlap
89143 />
90- </ View >
144+ </ ScrollView >
91145
92146 < FAB
93147 style = { styles . fab }
0 commit comments