diff --git a/src/app/groups/[groupId]/balances/page.tsx b/src/app/groups/[groupId]/balances/page.tsx index 24818c9..33ba653 100644 --- a/src/app/groups/[groupId]/balances/page.tsx +++ b/src/app/groups/[groupId]/balances/page.tsx @@ -1,4 +1,5 @@ import { BalancesList } from '@/app/groups/[groupId]/balances-list' +import { ReimbursementList } from '@/app/groups/[groupId]/reimbursement-list' import { Card, CardContent, @@ -7,7 +8,7 @@ import { CardTitle, } from '@/components/ui/card' import { getGroup, getGroupExpenses } from '@/lib/api' -import { getBalances } from '@/lib/balances' +import { getBalances, getSuggestedReimbursements } from '@/lib/balances' import { notFound } from 'next/navigation' export default async function GroupPage({ @@ -20,22 +21,42 @@ export default async function GroupPage({ const expenses = await getGroupExpenses(groupId) const balances = getBalances(expenses) + const reimbursements = getSuggestedReimbursements(balances) + console.log(reimbursements) return ( - - - Balances - - This is the amount that each participant paid or was paid for. - - - - - - + <> + + + Balances + + This is the amount that each participant paid or was paid for. + + + + + + + + + Suggested reimbursements + + Here are suggestions for optimized reimbursements between + participants. + + + + + + + ) } diff --git a/src/app/groups/[groupId]/reimbursement-list.tsx b/src/app/groups/[groupId]/reimbursement-list.tsx new file mode 100644 index 0000000..9d3ec49 --- /dev/null +++ b/src/app/groups/[groupId]/reimbursement-list.tsx @@ -0,0 +1,31 @@ +import { Reimbursement } from '@/lib/balances' +import { Participant } from '@prisma/client' + +type Props = { + reimbursements: Reimbursement[] + participants: Participant[] + currency: string +} + +export function ReimbursementList({ + reimbursements, + participants, + currency, +}: Props) { + const getParticipant = (id: string) => participants.find((p) => p.id === id) + return ( +
+ {reimbursements.map((reimbursement, index) => ( +
+
+ {getParticipant(reimbursement.from)?.name} owes{' '} + {getParticipant(reimbursement.to)?.name} +
+
+ {currency} {reimbursement.amount.toFixed(2)} +
+
+ ))} +
+ ) +} diff --git a/src/components/group-form.tsx b/src/components/group-form.tsx index 1057f27..39edd72 100644 --- a/src/components/group-form.tsx +++ b/src/components/group-form.tsx @@ -56,9 +56,8 @@ export function GroupForm({ group, onSubmit }: Props) { onSubmit={form.handleSubmit(async (values) => { await onSubmit(values) })} - className="space-y-8" > - + Group information @@ -99,7 +98,7 @@ export function GroupForm({ group, onSubmit }: Props) { /> - We’ll used it to display amounts. + We’ll use it to display amounts. @@ -107,7 +106,7 @@ export function GroupForm({ group, onSubmit }: Props) { /> - + Participants diff --git a/src/lib/balances.ts b/src/lib/balances.ts index fa00640..54dea00 100644 --- a/src/lib/balances.ts +++ b/src/lib/balances.ts @@ -6,6 +6,12 @@ export type Balances = Record< { paid: number; paidFor: number; total: number } > +export type Reimbursement = { + from: Participant['id'] + to: Participant['id'] + amount: number +} + export function getBalances( expenses: NonNullable>>, ): Balances { @@ -40,3 +46,37 @@ function divide(total: number, count: number, isLast: boolean): number { return total - divide(total, count, false) * (count - 1) } + +export function getSuggestedReimbursements( + balances: Balances, +): Reimbursement[] { + const balancesArray = Object.entries(balances).map( + ([participantId, { total }]) => ({ participantId, total }), + ) + balancesArray.sort((b1, b2) => b2.total - b1.total) + console.log(balancesArray) + const reimbursements: Reimbursement[] = [] + while (balancesArray.length > 1) { + const first = balancesArray[0] + const last = balancesArray[balancesArray.length - 1] + const amount = Math.round(first.total * 100 + last.total * 100) / 100 + if (first.total > -last.total) { + reimbursements.push({ + from: last.participantId, + to: first.participantId, + amount: -last.total, + }) + first.total = amount + balancesArray.pop() + } else { + reimbursements.push({ + from: last.participantId, + to: first.participantId, + amount: first.total, + }) + last.total = amount + balancesArray.shift() + } + } + return reimbursements +}