mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-22 23:16:46 +00:00
fix(costs): tell the phone when the settlement did not load
The mobile Costs tab swallowed a failed settlement request, and the final budget card then filled every traveler's row from the empty fallback: "0.00" for everyone, which reads as "the trip cost nobody anything". The desktop panel already keeps a settlementError flag and prints its notice instead of a number; the mobile tab now does the same in the final budget card.
This commit is contained in:
@@ -58,8 +58,13 @@ export default function MCostsTab({ planner, shell }: MTabScreenProps) {
|
||||
const ctx: CostsCtx = useMemo(() => ({ me, tripCurrency, convert }), [me, tripCurrency, convert])
|
||||
|
||||
const [settlement, setSettlement] = useState<CostsSettlementResponse | null>(null)
|
||||
// A failed settlement read leaves `settlement` null, and the final budget would
|
||||
// read that as "the trip cost nobody anything", a claim we cannot make.
|
||||
const [settlementError, setSettlementError] = useState(false)
|
||||
const loadSettlement = useCallback(() => {
|
||||
budgetApi.settlement(tripId, base).then(setSettlement).catch(() => {})
|
||||
budgetApi.settlement(tripId, base)
|
||||
.then(s => { setSettlement(s); setSettlementError(false) })
|
||||
.catch(() => setSettlementError(true))
|
||||
}, [tripId, base])
|
||||
|
||||
// Mirrors CostsPanel.tsx: items reload on trip change, settlement reloads on
|
||||
@@ -312,7 +317,9 @@ export default function MCostsTab({ planner, shell }: MTabScreenProps) {
|
||||
{/* Final budget — what the trip costs each traveler; the arithmetic opens on tap */}
|
||||
<div className="mt-2 rounded-2xl border border-[color:var(--m-rowbr)] bg-m-card p-[13px]">
|
||||
<div className="font-geist text-[0.625rem] font-bold uppercase tracking-[.09em] text-m-faint">{t('costs.finalBudget')}</div>
|
||||
{tripMembers.map(p => {
|
||||
{settlementError ? (
|
||||
<p className="py-[14px] text-center font-geist text-[0.71875rem] text-m-muted">{t('common.unknownError')}</p>
|
||||
) : tripMembers.map(p => {
|
||||
const row = finalBudgetFor(settlement?.finalBudgets || [], p)
|
||||
const open = expandedFinalId === p.id
|
||||
return (
|
||||
|
||||
@@ -14,7 +14,7 @@ import { resetAllStores, seedStore } from '../../../helpers/store'
|
||||
import { server } from '../../../helpers/msw/server'
|
||||
import { fireEvent, render, screen, waitFor, within } from '../../../helpers/render'
|
||||
|
||||
// FE-MOB-COSTT-001 to FE-MOB-COSTT-043
|
||||
// FE-MOB-COSTT-001 to FE-MOB-COSTT-044
|
||||
|
||||
// The add/edit expense sheet is the shared desktop-sized form; the panel only
|
||||
// owns when it opens and what happens on save, so it is stubbed here.
|
||||
@@ -646,6 +646,16 @@ describe('MCostsTab', () => {
|
||||
expect(within(card).queryByText('+$58.00')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('FE-MOB-COSTT-044: a settlement read that fails says so instead of costing everyone nothing', async () => {
|
||||
serveSettlement(SETTLEMENT, true)
|
||||
render(<MCostsTab planner={planner()} shell={buildShell()} />)
|
||||
|
||||
const card = up(await screen.findByText('costs.finalBudget'), 1)
|
||||
await waitFor(() => expect(within(card).getByText('common.unknownError')).toBeInTheDocument())
|
||||
expect(within(card).queryByText('$0.00')).not.toBeInTheDocument()
|
||||
expect(within(card).queryByRole('button')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('FE-MOB-COSTT-040: the expense filters apply to payments the same way they do on desktop', async () => {
|
||||
const mine = { id: 503, from_user_id: 1, to_user_id: 2, amount: 8, currency: 'USD', created_at: '2026-05-02T09:00:00Z' }
|
||||
const others = { id: 504, from_user_id: 2, to_user_id: 3, amount: 9, currency: 'USD', created_at: '2026-05-02T09:00:00Z' }
|
||||
|
||||
Reference in New Issue
Block a user