diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 590275bc..ec9c5f7d 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -189,6 +189,12 @@ "warning": "Warning: Don't use send invite if it's invalid email. use add to Split Pro instead. Your account will be blocked if this feature is misused" }, "split_type_section": { + "direction": { + "no_money_flow": "No one else owes anything", + "owes_payer": "{{debtor}} owes {{payer}}", + "owes_you": "{{debtor}} owes you", + "you_owe": "You owe {{payer}}" + }, "split_equally": "split equally", "split_unequally": "split unequally", "types": { @@ -213,6 +219,9 @@ "title": "Share", "total_shares": "Total shares" } + }, + "validation": { + "invalid_split": "Adjust who owes this expense before saving." } }, "sponsor_us": "Sponsor us", diff --git a/src/components/AddExpense/AddExpensePage.tsx b/src/components/AddExpense/AddExpensePage.tsx index 90a206e4..778972ff 100644 --- a/src/components/AddExpense/AddExpensePage.tsx +++ b/src/components/AddExpense/AddExpensePage.tsx @@ -1,3 +1,4 @@ +import { SplitType } from '@prisma/client'; import { HeartHandshakeIcon, Landmark, RefreshCcwDot, X } from 'lucide-react'; import { useTranslation } from 'next-i18next'; import Link from 'next/link'; @@ -55,8 +56,73 @@ export const AddOrEditExpensePage: React.FC<{ const cronExpression = useAddExpenseStore((s) => s.cronExpression); const multipleTransactions = useAddExpenseStore((s) => s.multipleTransactions); - const { t, displayName, generateSplitDescription, getCurrencyHelpersCached } = - useTranslationWithUtils(); + const { t, displayName, getCurrencyHelpersCached } = useTranslationWithUtils(); + const splitValidationMessage = t( + 'expense_details.add_expense_details.split_type_section.validation.invalid_split', + ); + const splitDescription = React.useMemo(() => { + const splitEquallyText = t( + 'expense_details.add_expense_details.split_type_section.split_equally', + ); + + if (SplitType.EQUAL !== splitType) { + return t('expense_details.add_expense_details.split_type_section.split_unequally'); + } + + if (!paidBy || !currentUser) { + return splitEquallyText; + } + + const selectedParticipants = participants.filter((participant) => { + const share = splitShares[participant.id]?.[SplitType.EQUAL]; + return share === undefined || 0n !== share; + }); + + if (0 === selectedParticipants.length) { + return splitValidationMessage; + } + + const splitParticipant = selectedParticipants[0]; + if (1 === selectedParticipants.length && splitParticipant) { + if (splitParticipant.id === paidBy.id) { + return t('expense_details.add_expense_details.split_type_section.direction.no_money_flow'); + } + + const debtor = isNegative ? paidBy : splitParticipant; + const payer = isNegative ? splitParticipant : paidBy; + const debtorName = displayName(debtor, currentUser.id); + const payerName = displayName(payer, currentUser.id); + + if (payer.id === currentUser.id) { + return t('expense_details.add_expense_details.split_type_section.direction.owes_you', { + debtor: debtorName, + }); + } + + if (debtor.id === currentUser.id) { + return t('expense_details.add_expense_details.split_type_section.direction.you_owe', { + payer: payerName, + }); + } + + return t('expense_details.add_expense_details.split_type_section.direction.owes_payer', { + debtor: debtorName, + payer: payerName, + }); + } + + return `${splitEquallyText} (${selectedParticipants.length})`; + }, [ + currentUser, + displayName, + isNegative, + paidBy, + participants, + splitShares, + splitType, + splitValidationMessage, + t, + ]); const { setCurrency, @@ -111,6 +177,7 @@ export const AddOrEditExpensePage: React.FC<{ } if (!isExpenseSettled) { + toast.error(splitValidationMessage); setSplitScreenOpen(true); return; } @@ -220,6 +287,7 @@ export const AddOrEditExpensePage: React.FC<{ multipleTransactions, setSingleTransaction, update, + splitValidationMessage, ]); const handleDescriptionChange = useCallback( @@ -345,16 +413,13 @@ export const AddOrEditExpensePage: React.FC<{

{t('ui.and')}

+ {!isExpenseSettled ? ( +

{splitValidationMessage}

+ ) : null}
= (props) => { > {fmtSummartyText(amount, totalShares, toUIString)}

+ {!canSplitScreenClosed ? ( +

+ {t('expense_details.add_expense_details.split_type_section.validation.invalid_split')} +

+ ) : null} {isBoolean && (