Skip to content

Commit

Permalink
feat: add auto-balancing for the amount edit (#173)
Browse files Browse the repository at this point in the history
* feat: add auto-balancing for the amount edit

this implementation allocates the rest of the total
to participants, whose rows have yet not been edited.

* fix: reset already edited on total amount change
  • Loading branch information
mpjk authored Aug 2, 2024
1 parent 002e867 commit c392c06
Showing 1 changed file with 75 additions and 3 deletions.
78 changes: 75 additions & 3 deletions src/components/expense-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import { zodResolver } from '@hookform/resolvers/zod'
import { Save } from 'lucide-react'
import Link from 'next/link'
import { useSearchParams } from 'next/navigation'
import { useState } from 'react'
import { useEffect, useState } from 'react'
import { useForm } from 'react-hook-form'
import { match } from 'ts-pattern'
import { DeletePopup } from './delete-popup'
Expand Down Expand Up @@ -245,9 +245,78 @@ export function ExpenseForm({
}

const [isIncome, setIsIncome] = useState(Number(form.getValues().amount) < 0)
const [manuallyEditedParticipants, setManuallyEditedParticipants] = useState<
Set<string>
>(new Set())

const sExpense = isIncome ? 'income' : 'expense'
const sPaid = isIncome ? 'received' : 'paid'

useEffect(() => {
setManuallyEditedParticipants(new Set())
const newPaidFor = defaultSplittingOptions.paidFor.map((participant) => ({
...participant,
shares: String(participant.shares) as unknown as number,
}))
form.setValue('paidFor', newPaidFor, { shouldValidate: true })
}, [form.watch('splitMode'), form.watch('amount')])

useEffect(() => {
const totalAmount = Number(form.getValues().amount) || 0
const paidFor = form.getValues().paidFor
const splitMode = form.getValues().splitMode

let newPaidFor = [...paidFor]

if (
splitMode === 'EVENLY' ||
splitMode === 'BY_SHARES' ||
splitMode === 'BY_PERCENTAGE'
) {
return
} else {
// Only auto-balance for split mode 'Unevenly - By amount'
const editedParticipants = Array.from(manuallyEditedParticipants)
let remainingAmount = totalAmount
let remainingParticipants = newPaidFor.length - editedParticipants.length

newPaidFor = newPaidFor.map((participant) => {
if (editedParticipants.includes(participant.participant)) {
const participantShare = Number(participant.shares) || 0
if (splitMode === 'BY_AMOUNT') {
remainingAmount -= participantShare
}
return participant
}
return participant
})

if (remainingParticipants > 0) {
let amountPerRemaining = 0
if (splitMode === 'BY_AMOUNT') {
amountPerRemaining = remainingAmount / remainingParticipants
}

newPaidFor = newPaidFor.map((participant) => {
if (!editedParticipants.includes(participant.participant)) {
return {
...participant,
shares: String(
Number(amountPerRemaining.toFixed(2)),
) as unknown as number,
}
}
return participant
})
}
form.setValue('paidFor', newPaidFor, { shouldValidate: true })
}
}, [
manuallyEditedParticipants,
form.watch('amount'),
form.watch('splitMode'),
])

return (
<Form {...form}>
<form onSubmit={form.handleSubmit(submit)}>
Expand Down Expand Up @@ -570,7 +639,7 @@ export function ExpenseForm({
participant === id,
)?.shares
}
onChange={(event) =>
onChange={(event) => {
field.onChange(
field.value.map((p) =>
p.participant === id
Expand All @@ -584,7 +653,10 @@ export function ExpenseForm({
: p,
),
)
}
setManuallyEditedParticipants(
(prev) => new Set(prev).add(id),
)
}}
inputMode={
form.getValues().splitMode ===
'BY_AMOUNT'
Expand Down

0 comments on commit c392c06

Please sign in to comment.