From 1d9a1b96dd67156145105aac44e6c4753f74382c Mon Sep 17 00:00:00 2001 From: Ayush Rodrigues Date: Fri, 17 Apr 2026 14:48:44 +0100 Subject: [PATCH] Replace info box with toggle for granted balance in balance edit sheet Swap the static "both will be updated" info box with a ConfigRow + Switch toggle so users can control whether adding to balance also updates the granted balance. Remove the overly restrictive included_grant validation on the server and clean up dead params in computeGrantedBalanceInput. Made-with: Cursor --- .../balances/handlers/handleUpdateBalance.ts | 14 ------- .../update-balance-prepaid-granted.test.ts | 2 - .../computeGrantedBalanceInput.ts | 2 - .../components/sheets/BalanceEditSheet.tsx | 41 +++++++++++++++---- .../sheets/balanceEditFormSchema.ts | 1 + .../components/sheets/useBalanceEditForm.ts | 1 + 6 files changed, 34 insertions(+), 27 deletions(-) diff --git a/server/src/internal/balances/handlers/handleUpdateBalance.ts b/server/src/internal/balances/handlers/handleUpdateBalance.ts index e1e7bb00d..0d3f1c892 100644 --- a/server/src/internal/balances/handlers/handleUpdateBalance.ts +++ b/server/src/internal/balances/handlers/handleUpdateBalance.ts @@ -1,12 +1,8 @@ import { - ErrCode, findFeatureById, notNullish, - nullish, - RecaseError, UpdateBalanceParamsV0Schema, } from "@autumn/shared"; -import { StatusCodes } from "http-status-codes"; import { createRoute } from "@/honoMiddlewares/routeHandler"; import { runUpdateBalanceV2 } from "@/internal/balances/updateBalance/runUpdateBalanceV2"; import { runUpdateUsage } from "@/internal/balances/updateBalance/runUpdateUsage"; @@ -32,15 +28,6 @@ export const handleUpdateBalance = createRoute({ const targetBalance = params.remaining ?? params.current_balance; - if (notNullish(params.included_grant) && nullish(targetBalance)) { - throw new RecaseError({ - message: - "'remaining' is required when updating granted balance", - code: ErrCode.InvalidRequest, - statusCode: StatusCodes.BAD_REQUEST, - }); - } - let fullCustomer = await getOrSetCachedFullCustomer({ ctx, customerId: params.customer_id, @@ -59,7 +46,6 @@ export const handleUpdateBalance = createRoute({ } if (notNullish(params.included_grant)) { - ctx.logger.info( `updating granted balance for feature ${params.feature_id} to ${params.included_grant}`, ); diff --git a/server/tests/integration/balances/update/balance/update-balance-prepaid-granted.test.ts b/server/tests/integration/balances/update/balance/update-balance-prepaid-granted.test.ts index b8baf3cda..b5e3eea56 100644 --- a/server/tests/integration/balances/update/balance/update-balance-prepaid-granted.test.ts +++ b/server/tests/integration/balances/update/balance/update-balance-prepaid-granted.test.ts @@ -96,8 +96,6 @@ test.concurrent(`${chalk.yellowBright("update-prepaid-granted1: granted_balance const grantedBalanceInput = computeGrantedBalanceInput({ newGPB, - defaultGPB, - defaultBalance, prepaidAllowance, }); diff --git a/shared/utils/cusEntUtils/balanceUtils/computeGrantedBalanceInput.ts b/shared/utils/cusEntUtils/balanceUtils/computeGrantedBalanceInput.ts index d749493be..5045246a0 100644 --- a/shared/utils/cusEntUtils/balanceUtils/computeGrantedBalanceInput.ts +++ b/shared/utils/cusEntUtils/balanceUtils/computeGrantedBalanceInput.ts @@ -11,8 +11,6 @@ export function computeGrantedBalanceInput({ prepaidAllowance, }: { newGPB: number; - defaultGPB: number; - defaultBalance: number; prepaidAllowance: number; }): number { return newGPB - prepaidAllowance; diff --git a/vite/src/views/customers2/components/sheets/BalanceEditSheet.tsx b/vite/src/views/customers2/components/sheets/BalanceEditSheet.tsx index b15c11025..ec765d824 100644 --- a/vite/src/views/customers2/components/sheets/BalanceEditSheet.tsx +++ b/vite/src/views/customers2/components/sheets/BalanceEditSheet.tsx @@ -13,7 +13,9 @@ import { ClockCountdownIcon } from "@phosphor-icons/react"; import { useStore } from "@tanstack/react-form"; import { useState } from "react"; import { toast } from "sonner"; +import { ConfigRow } from "@/components/forms/shared/ConfigRow"; import { DateInputUnix } from "@/components/general/DateInputUnix"; +import { Switch } from "@/components/ui/switch"; import { Button } from "@/components/v2/buttons/Button"; import { CopyButton } from "@/components/v2/buttons/CopyButton"; import { GroupedTabButton } from "@/components/v2/buttons/GroupedTabButton"; @@ -26,7 +28,6 @@ import { useAxiosInstance } from "@/services/useAxiosInstance"; import { formatUnixToDateTime } from "@/utils/formatUtils/formatDateUtils"; import { getBackendErr, notNullish } from "@/utils/genUtils"; import { useCusQuery } from "@/views/customers/customer/hooks/useCusQuery"; -import { InfoBox } from "@/views/onboarding2/integrate/components/InfoBox"; import { useCustomerContext } from "../../customer/CustomerContext"; import { BalanceEditPreviews } from "./BalanceEditPreviews"; import { GrantedBalancePopover } from "./GrantedBalancePopover"; @@ -463,6 +464,11 @@ function SetBalanceFields({ /* ─── Add Balance Mode ─── */ function AddBalanceFields({ form }: { form: BalanceEditFormInstance }) { + const updateGrantedBalance = useStore( + form.store, + (s) => s.values.updateGrantedBalance, + ); + return (
@@ -482,9 +488,18 @@ function AddBalanceFields({ form }: { form: BalanceEditFormInstance }) { /> )} - - Current and total granted balance will both be updated. - + + form.setFieldValue("updateGrantedBalance", !!checked) + } + /> + } + />
); } @@ -551,9 +566,6 @@ function SubmitButton({ if (values.mode === "set") { const grantedBalanceInput = computeGrantedBalanceInput({ newGPB: values.grantedAndPurchasedBalance ?? 0, - defaultGPB: - form.options.defaultValues?.grantedAndPurchasedBalance ?? 0, - defaultBalance: form.options.defaultValues?.balance ?? 0, prepaidAllowance: form.prepaidAllowance, }); @@ -566,18 +578,29 @@ function SubmitButton({ feature_id: featureId, current_balance: targetBalance, included_grant: grantedBalanceInput ?? undefined, - granted_balance: grantedBalanceInput ?? undefined, customer_entitlement_id: selectedCusEnt.id, entity_id: entityId ?? undefined, next_reset_at: values.nextResetAt ?? undefined, }), ); } else { + const addAmount = parseFloat(String(values.addValue)); + const defaultGPB = + form.options.defaultValues?.grantedAndPurchasedBalance ?? 0; + const newGPB = defaultGPB + addAmount; + const grantedBalanceInput = values.updateGrantedBalance + ? computeGrantedBalanceInput({ + newGPB, + prepaidAllowance: form.prepaidAllowance, + }) + : undefined; + promises.push( axiosInstance.post("/v1/balances/update", { customer_id: customer.id || customer.internal_id, feature_id: featureId, - add_to_balance: parseFloat(String(values.addValue)), + add_to_balance: addAmount, + included_grant: grantedBalanceInput, customer_entitlement_id: selectedCusEnt.id, entity_id: entityId ?? undefined, }), diff --git a/vite/src/views/customers2/components/sheets/balanceEditFormSchema.ts b/vite/src/views/customers2/components/sheets/balanceEditFormSchema.ts index 046fe06b9..2a4b91d33 100644 --- a/vite/src/views/customers2/components/sheets/balanceEditFormSchema.ts +++ b/vite/src/views/customers2/components/sheets/balanceEditFormSchema.ts @@ -7,6 +7,7 @@ export const BalanceEditFormSchema = z grantedAndPurchasedBalance: z.number().nullable(), nextResetAt: z.number().nullable(), addValue: z.number().nullable(), + updateGrantedBalance: z.boolean(), }) .check((ctx) => { const { mode, balance, addValue } = ctx.value; diff --git a/vite/src/views/customers2/components/sheets/useBalanceEditForm.ts b/vite/src/views/customers2/components/sheets/useBalanceEditForm.ts index ee9b719c9..17c3825a8 100644 --- a/vite/src/views/customers2/components/sheets/useBalanceEditForm.ts +++ b/vite/src/views/customers2/components/sheets/useBalanceEditForm.ts @@ -50,6 +50,7 @@ export function useBalanceEditForm({ grantedAndPurchasedBalance: grantedAndPurchasedBalance ?? null, nextResetAt: selectedCusEnt.next_reset_at ?? null, addValue: null, + updateGrantedBalance: true, } as BalanceEditForm, validators: { onChange: BalanceEditFormSchema,