feat(preferencias): permite ocultar resumo do lancamento

This commit is contained in:
Felipe Coutinho
2026-05-31 15:18:07 -03:00
parent cdcc677787
commit 41eecc2538
11 changed files with 3037 additions and 10 deletions

View File

@@ -0,0 +1 @@
ALTER TABLE "preferencias_usuario" ADD COLUMN "mostrar_resumo_lancamento" boolean DEFAULT true NOT NULL;

File diff suppressed because it is too large Load Diff

View File

@@ -204,6 +204,13 @@
"when": 1777648189399, "when": 1777648189399,
"tag": "0029_friendly_spitfire", "tag": "0029_friendly_spitfire",
"breakpoints": true "breakpoints": true
},
{
"idx": 30,
"version": "7",
"when": 1780150535055,
"tag": "0030_complete_umar",
"breakpoints": true
} }
] ]
} }

View File

@@ -82,6 +82,9 @@ export default async function Page() {
userPreferences?.transactionsColumnOrder ?? null userPreferences?.transactionsColumnOrder ?? null
} }
attachmentMaxSizeMb={userPreferences?.attachmentMaxSizeMb ?? 50} attachmentMaxSizeMb={userPreferences?.attachmentMaxSizeMb ?? 50}
showTransactionSummary={
userPreferences?.showTransactionSummary ?? true
}
/> />
</div> </div>
</Card> </Card>

View File

@@ -154,6 +154,9 @@ export const userPreferences = pgTable("preferencias_usuario", {
string[] | null string[] | null
>(), >(),
attachmentMaxSizeMb: integer("attachment_max_size_mb").notNull().default(50), attachmentMaxSizeMb: integer("attachment_max_size_mb").notNull().default(50),
showTransactionSummary: boolean("mostrar_resumo_lancamento")
.notNull()
.default(true),
dashboardWidgets: jsonb("dashboard_widgets").$type<{ dashboardWidgets: jsonb("dashboard_widgets").$type<{
order: string[]; order: string[];
hidden: string[]; hidden: string[];
@@ -495,7 +498,7 @@ export const inboxItems = pgTable(
withTimezone: true, withTimezone: true,
}).notNull(), }).notNull(),
// Dados parseados (editáveis pelo usuário antes de processar) // Dados parseados (editáveis pelo usuário antes de lançar)
parsedName: text("parsed_name"), // Nome do estabelecimento parsedName: text("parsed_name"), // Nome do estabelecimento
parsedAmount: numeric("parsed_amount", { precision: 12, scale: 2 }), parsedAmount: numeric("parsed_amount", { precision: 12, scale: 2 }),

View File

@@ -68,6 +68,7 @@ const updatePreferencesSchema = z.object({
statementNoteAsColumn: z.boolean(), statementNoteAsColumn: z.boolean(),
transactionsColumnOrder: z.array(z.string()).nullable(), transactionsColumnOrder: z.array(z.string()).nullable(),
attachmentMaxSizeMb: z.number().int().min(1).max(100), attachmentMaxSizeMb: z.number().int().min(1).max(100),
showTransactionSummary: z.boolean(),
}); });
type ResettableUser = { type ResettableUser = {
@@ -582,6 +583,7 @@ export async function updatePreferencesAction(
statementNoteAsColumn: validated.statementNoteAsColumn, statementNoteAsColumn: validated.statementNoteAsColumn,
transactionsColumnOrder: validated.transactionsColumnOrder, transactionsColumnOrder: validated.transactionsColumnOrder,
attachmentMaxSizeMb: validated.attachmentMaxSizeMb, attachmentMaxSizeMb: validated.attachmentMaxSizeMb,
showTransactionSummary: validated.showTransactionSummary,
updatedAt: new Date(), updatedAt: new Date(),
}) })
.where(eq(schema.userPreferences.userId, session.user.id)); .where(eq(schema.userPreferences.userId, session.user.id));
@@ -592,6 +594,7 @@ export async function updatePreferencesAction(
statementNoteAsColumn: validated.statementNoteAsColumn, statementNoteAsColumn: validated.statementNoteAsColumn,
transactionsColumnOrder: validated.transactionsColumnOrder, transactionsColumnOrder: validated.transactionsColumnOrder,
attachmentMaxSizeMb: validated.attachmentMaxSizeMb, attachmentMaxSizeMb: validated.attachmentMaxSizeMb,
showTransactionSummary: validated.showTransactionSummary,
}); });
} }

View File

@@ -42,6 +42,7 @@ interface PreferencesFormProps {
statementNoteAsColumn: boolean; statementNoteAsColumn: boolean;
transactionsColumnOrder: string[] | null; transactionsColumnOrder: string[] | null;
attachmentMaxSizeMb: number; attachmentMaxSizeMb: number;
showTransactionSummary: boolean;
} }
function SortableColumnItem({ id }: { id: string }) { function SortableColumnItem({ id }: { id: string }) {
@@ -85,6 +86,7 @@ export function PreferencesForm({
statementNoteAsColumn: initialExtratoNoteAsColumn, statementNoteAsColumn: initialExtratoNoteAsColumn,
transactionsColumnOrder: initialColumnOrder, transactionsColumnOrder: initialColumnOrder,
attachmentMaxSizeMb: initialAttachmentMaxSizeMb, attachmentMaxSizeMb: initialAttachmentMaxSizeMb,
showTransactionSummary: initialShowTransactionSummary,
}: PreferencesFormProps) { }: PreferencesFormProps) {
const router = useRouter(); const router = useRouter();
const [isPending, startTransition] = useTransition(); const [isPending, startTransition] = useTransition();
@@ -104,6 +106,9 @@ export function PreferencesForm({
? initialAttachmentMaxSizeMb ? initialAttachmentMaxSizeMb
: 50) as AttachmentSizeOption, : 50) as AttachmentSizeOption,
); );
const [showTransactionSummary, setShowTransactionSummary] = useState(
initialShowTransactionSummary,
);
const sensors = useSensors( const sensors = useSensors(
useSensor(PointerSensor, { activationConstraint: { distance: 8 } }), useSensor(PointerSensor, { activationConstraint: { distance: 8 } }),
@@ -129,6 +134,7 @@ export function PreferencesForm({
statementNoteAsColumn, statementNoteAsColumn,
transactionsColumnOrder: columnOrder, transactionsColumnOrder: columnOrder,
attachmentMaxSizeMb, attachmentMaxSizeMb,
showTransactionSummary,
}); });
if (result.success) { if (result.success) {
@@ -172,6 +178,26 @@ export function PreferencesForm({
<Separator /> <Separator />
<section className="flex items-center justify-between max-w-md">
<div className="space-y-2">
<Label htmlFor="show-transaction-summary" className="text-sm">
Resumo da operação
</Label>
<p className="text-sm text-muted-foreground">
Exibe um resumo dos dados preenchidos no final do modal de
lançamento.
</p>
</div>
<Switch
id="show-transaction-summary"
checked={showTransactionSummary}
onCheckedChange={setShowTransactionSummary}
disabled={isPending}
/>
</section>
<Separator />
<section className="space-y-2 max-w-md"> <section className="space-y-2 max-w-md">
<Label className="text-sm">Ordem das colunas</Label> <Label className="text-sm">Ordem das colunas</Label>
<p className="text-sm text-muted-foreground"> <p className="text-sm text-muted-foreground">

View File

@@ -6,6 +6,7 @@ interface UserPreferences {
statementNoteAsColumn: boolean; statementNoteAsColumn: boolean;
transactionsColumnOrder: string[] | null; transactionsColumnOrder: string[] | null;
attachmentMaxSizeMb: number; attachmentMaxSizeMb: number;
showTransactionSummary: boolean;
} }
interface ApiToken { interface ApiToken {
@@ -34,6 +35,7 @@ export async function fetchUserPreferences(
statementNoteAsColumn: schema.userPreferences.statementNoteAsColumn, statementNoteAsColumn: schema.userPreferences.statementNoteAsColumn,
transactionsColumnOrder: schema.userPreferences.transactionsColumnOrder, transactionsColumnOrder: schema.userPreferences.transactionsColumnOrder,
attachmentMaxSizeMb: schema.userPreferences.attachmentMaxSizeMb, attachmentMaxSizeMb: schema.userPreferences.attachmentMaxSizeMb,
showTransactionSummary: schema.userPreferences.showTransactionSummary,
}) })
.from(schema.userPreferences) .from(schema.userPreferences)
.where(eq(schema.userPreferences.userId, userId)) .where(eq(schema.userPreferences.userId, userId))

View File

@@ -17,6 +17,7 @@ import {
buildTransactionInitialState, buildTransactionInitialState,
deriveCreditCardPeriod, deriveCreditCardPeriod,
} from "@/features/transactions/lib/form-helpers"; } from "@/features/transactions/lib/form-helpers";
import { useAppPreferences } from "@/shared/components/providers/app-preferences-provider";
import { Button } from "@/shared/components/ui/button"; import { Button } from "@/shared/components/ui/button";
import { import {
Collapsible, Collapsible,
@@ -104,6 +105,7 @@ export function TransactionDialog({
const [pendingUploadFiles, setPendingUploadFiles] = useState<File[]>([]); const [pendingUploadFiles, setPendingUploadFiles] = useState<File[]>([]);
const [extrasOpen, setExtrasOpen] = useState(false); const [extrasOpen, setExtrasOpen] = useState(false);
const scrollContainerRef = useRef<HTMLDivElement>(null); const scrollContainerRef = useRef<HTMLDivElement>(null);
const { showTransactionSummary } = useAppPreferences();
useEffect(() => { useEffect(() => {
if (dialogOpen) { if (dialogOpen) {
@@ -730,6 +732,7 @@ export function TransactionDialog({
</Collapsible> </Collapsible>
)} )}
{showTransactionSummary ? (
<div className="mt-3"> <div className="mt-3">
<TransactionSummaryCard <TransactionSummaryCard
formState={formState} formState={formState}
@@ -739,6 +742,7 @@ export function TransactionDialog({
categoryOptions={categoryOptions} categoryOptions={categoryOptions}
/> />
</div> </div>
) : null}
</div> </div>
{errorMessage ? ( {errorMessage ? (

View File

@@ -0,0 +1,31 @@
"use client";
import { createContext, useContext } from "react";
import type { AppPreferences } from "@/shared/lib/preferences/queries";
const DEFAULT_APP_PREFERENCES: AppPreferences = {
showTransactionSummary: true,
};
const AppPreferencesContext = createContext<AppPreferences>(
DEFAULT_APP_PREFERENCES,
);
type AppPreferencesProviderProps = AppPreferences & {
children: React.ReactNode;
};
export function AppPreferencesProvider({
children,
...preferences
}: AppPreferencesProviderProps) {
return (
<AppPreferencesContext.Provider value={preferences}>
{children}
</AppPreferencesContext.Provider>
);
}
export function useAppPreferences() {
return useContext(AppPreferencesContext);
}

View File

@@ -0,0 +1,24 @@
import { eq } from "drizzle-orm";
import { db, schema } from "@/shared/lib/db";
export type AppPreferences = {
showTransactionSummary: boolean;
};
const DEFAULT_APP_PREFERENCES: AppPreferences = {
showTransactionSummary: true,
};
export async function fetchAppPreferences(
userId: string,
): Promise<AppPreferences> {
const [preferences] = await db
.select({
showTransactionSummary: schema.userPreferences.showTransactionSummary,
})
.from(schema.userPreferences)
.where(eq(schema.userPreferences.userId, userId))
.limit(1);
return preferences ?? DEFAULT_APP_PREFERENCES;
}