feat: divisão por porcentagem e indicador de orçamento no modal de lançamento

Toggle compacto R$/% no card 'Dividir lançamento' usando ToggleGroup do shadcn.
No modo %, cada input exibe o valor convertido em R$ logo abaixo (mesmo padrão
do InlinePeriodPicker). Helpers amountToPercent/percentToAmount reutilizam
safeToNumber, normalizeDecimalInput e formatDecimalForDbRequired.

Indicador de orçamento ao lado do nome da categoria selecionada: mostra
'R$ gasto de R$ orçado (%)' com cores semânticas (verde/âmbar/vermelho).
Busca assíncrona via getCategoryBudgetSummaryAction com cache por instância
(useRef<Map>) e cancelamento de race condition. Suprimido quando o input divide
a linha com o campo de tipo de transação (caso pré-lançamentos).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Felipe Coutinho
2026-05-10 13:51:44 +00:00
parent c9239c4f3c
commit c4c52c02ab
3 changed files with 272 additions and 37 deletions

View File

@@ -3,6 +3,10 @@
import { and, eq } from "drizzle-orm";
import { z } from "zod";
import { budgets, categories } from "@/db/schema";
import {
type CategoryBudgetSummary,
fetchCategoryBudgetSummary,
} from "@/features/budgets/queries";
import {
handleActionError,
revalidateForEntity,
@@ -204,6 +208,34 @@ export async function deleteBudgetAction(
}
}
const getCategoryBudgetSummarySchema = z.object({
categoryId: uuidSchema("Category"),
period: periodSchema,
});
type GetCategoryBudgetSummaryInput = z.input<
typeof getCategoryBudgetSummarySchema
>;
export async function getCategoryBudgetSummaryAction(
input: GetCategoryBudgetSummaryInput,
): Promise<ActionResult<CategoryBudgetSummary | null>> {
try {
const user = await getUser();
const data = getCategoryBudgetSummarySchema.parse(input);
const summary = await fetchCategoryBudgetSummary(
user.id,
data.categoryId,
data.period,
);
return { success: true, message: "ok", data: summary };
} catch (error) {
return handleActionError(
error,
) as ActionResult<CategoryBudgetSummary | null>;
}
}
const duplicatePreviousMonthSchema = z.object({
period: periodSchema,
});