mirror of
https://github.com/spliit-app/spliit.git
synced 2025-12-06 01:19:29 +01:00
feat: add auto-balancing for the amount edit (#173)
* 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
This commit is contained in:
@@ -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'
|
||||
@@ -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)}>
|
||||
@@ -570,7 +639,7 @@ export function ExpenseForm({
|
||||
participant === id,
|
||||
)?.shares
|
||||
}
|
||||
onChange={(event) =>
|
||||
onChange={(event) => {
|
||||
field.onChange(
|
||||
field.value.map((p) =>
|
||||
p.participant === id
|
||||
@@ -584,7 +653,10 @@ export function ExpenseForm({
|
||||
: p,
|
||||
),
|
||||
)
|
||||
}
|
||||
setManuallyEditedParticipants(
|
||||
(prev) => new Set(prev).add(id),
|
||||
)
|
||||
}}
|
||||
inputMode={
|
||||
form.getValues().splitMode ===
|
||||
'BY_AMOUNT'
|
||||
|
||||
Reference in New Issue
Block a user