mirror of
https://github.com/felipegcoutinho/openmonetis.git
synced 2026-07-09 03:16:01 +00:00
feat(lancamentos): oculta parcelas antecipadas por preferencia
This commit is contained in:
1
drizzle/0032_bumpy_spencer_smythe.sql
Normal file
1
drizzle/0032_bumpy_spencer_smythe.sql
Normal file
@@ -0,0 +1 @@
|
||||
ALTER TABLE "preferencias_usuario" ADD COLUMN "ocultar_parcelas_antecipadas" boolean DEFAULT false NOT NULL;
|
||||
2995
drizzle/meta/0032_snapshot.json
Normal file
2995
drizzle/meta/0032_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -218,6 +218,13 @@
|
||||
"when": 1782051007412,
|
||||
"tag": "0031_lame_cerise",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 32,
|
||||
"version": "7",
|
||||
"when": 1782569103402,
|
||||
"tag": "0032_bumpy_spencer_smythe",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -109,6 +109,8 @@ export default async function Page({ params, searchParams }: PageProps) {
|
||||
filters: searchFilters,
|
||||
slugMaps,
|
||||
accountId: account.id,
|
||||
hideAnticipatedInstallments:
|
||||
userPreferences?.hideAnticipatedInstallments ?? false,
|
||||
});
|
||||
|
||||
const transactionsPage = await fetchAccountTransactionsPage(
|
||||
|
||||
@@ -82,6 +82,8 @@ export default async function Page({ params, searchParams }: PageProps) {
|
||||
filters: searchFilters,
|
||||
slugMaps,
|
||||
cardId: card.id,
|
||||
hideAnticipatedInstallments:
|
||||
userPreferences?.hideAnticipatedInstallments ?? false,
|
||||
});
|
||||
|
||||
const transactionRows = await fetchCardTransactions(filters);
|
||||
|
||||
@@ -41,13 +41,17 @@ export default async function Page({ params, searchParams }: PageProps) {
|
||||
const periodoParam = getSingleParam(resolvedSearchParams, "periodo");
|
||||
const { period: selectedPeriod } = parsePeriodParam(periodoParam);
|
||||
|
||||
const [detail, filterSources, estabelecimentos, userPreferences] =
|
||||
await Promise.all([
|
||||
fetchCategoryDetails(userId, categoryId, selectedPeriod),
|
||||
fetchTransactionFilterSources(userId),
|
||||
fetchRecentEstablishments(userId),
|
||||
fetchUserPreferences(userId),
|
||||
]);
|
||||
const [filterSources, estabelecimentos, userPreferences] = await Promise.all([
|
||||
fetchTransactionFilterSources(userId),
|
||||
fetchRecentEstablishments(userId),
|
||||
fetchUserPreferences(userId),
|
||||
]);
|
||||
const detail = await fetchCategoryDetails(
|
||||
userId,
|
||||
categoryId,
|
||||
selectedPeriod,
|
||||
userPreferences?.hideAnticipatedInstallments ?? false,
|
||||
);
|
||||
|
||||
if (!detail) {
|
||||
notFound();
|
||||
|
||||
@@ -131,6 +131,7 @@ export default async function Page({ params, searchParams }: PageProps) {
|
||||
...EMPTY_FILTERS,
|
||||
searchFilter: allSearchFilters.searchFilter, // Permitir busca mesmo em modo read-only
|
||||
};
|
||||
const userPreferences = await fetchUserPreferences(userId);
|
||||
|
||||
let filterSources: Awaited<
|
||||
ReturnType<typeof fetchTransactionFilterSources>
|
||||
@@ -163,6 +164,8 @@ export default async function Page({ params, searchParams }: PageProps) {
|
||||
filters: searchFilters,
|
||||
slugMaps,
|
||||
payerId: pagador.id,
|
||||
hideAnticipatedInstallments:
|
||||
userPreferences?.hideAnticipatedInstallments ?? false,
|
||||
});
|
||||
|
||||
const sharesPromise = canEdit
|
||||
@@ -184,7 +187,6 @@ export default async function Page({ params, searchParams }: PageProps) {
|
||||
shareRows,
|
||||
currentUserShare,
|
||||
estabelecimentos,
|
||||
userPreferences,
|
||||
] = await Promise.all([
|
||||
fetchPayerTransactions(filters),
|
||||
fetchPayerMonthlyBreakdown({
|
||||
@@ -220,7 +222,6 @@ export default async function Page({ params, searchParams }: PageProps) {
|
||||
sharesPromise,
|
||||
currentUserSharePromise,
|
||||
fetchRecentEstablishments(userId),
|
||||
fetchUserPreferences(userId),
|
||||
]);
|
||||
|
||||
const mappedTransactions = mapTransactionsData(transactionRows);
|
||||
|
||||
@@ -85,6 +85,9 @@ export default async function Page() {
|
||||
showTransactionSummary={
|
||||
userPreferences?.showTransactionSummary ?? true
|
||||
}
|
||||
hideAnticipatedInstallments={
|
||||
userPreferences?.hideAnticipatedInstallments ?? false
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
@@ -53,6 +53,8 @@ export default async function Page({ searchParams }: PageProps) {
|
||||
period: selectedPeriod,
|
||||
filters: searchFilters,
|
||||
slugMaps,
|
||||
hideAnticipatedInstallments:
|
||||
userPreferences?.hideAnticipatedInstallments ?? false,
|
||||
});
|
||||
|
||||
const [transactionsPage, estabelecimentos] = await Promise.all([
|
||||
|
||||
@@ -157,6 +157,9 @@ export const userPreferences = pgTable("preferencias_usuario", {
|
||||
showTransactionSummary: boolean("mostrar_resumo_lancamento")
|
||||
.notNull()
|
||||
.default(true),
|
||||
hideAnticipatedInstallments: boolean("ocultar_parcelas_antecipadas")
|
||||
.notNull()
|
||||
.default(false),
|
||||
dashboardWidgets: jsonb("dashboard_widgets").$type<{
|
||||
order: string[];
|
||||
hidden: string[];
|
||||
|
||||
@@ -36,6 +36,7 @@ export async function fetchCategoryDetails(
|
||||
userId: string,
|
||||
categoryId: string,
|
||||
period: string,
|
||||
hideAnticipatedInstallments = false,
|
||||
): Promise<CategoryDetailData | null> {
|
||||
const category = await db.query.categories.findFirst({
|
||||
where: and(eq(categories.userId, userId), eq(categories.id, categoryId)),
|
||||
@@ -63,6 +64,14 @@ export async function fetchCategoryDetails(
|
||||
eq(transactions.transactionType, transactionType),
|
||||
eq(transactions.period, period),
|
||||
eq(transactions.payerId, adminPayerId),
|
||||
...(hideAnticipatedInstallments
|
||||
? [
|
||||
or(
|
||||
isNull(transactions.isAnticipated),
|
||||
eq(transactions.isAnticipated, false),
|
||||
),
|
||||
]
|
||||
: []),
|
||||
...(isInvoiceCategory ? [] : [sanitizedNote]),
|
||||
),
|
||||
with: {
|
||||
|
||||
@@ -69,6 +69,7 @@ const updatePreferencesSchema = z.object({
|
||||
transactionsColumnOrder: z.array(z.string()).nullable(),
|
||||
attachmentMaxSizeMb: z.number().int().min(1).max(100),
|
||||
showTransactionSummary: z.boolean(),
|
||||
hideAnticipatedInstallments: z.boolean(),
|
||||
});
|
||||
|
||||
type ResettableUser = {
|
||||
@@ -584,6 +585,7 @@ export async function updatePreferencesAction(
|
||||
transactionsColumnOrder: validated.transactionsColumnOrder,
|
||||
attachmentMaxSizeMb: validated.attachmentMaxSizeMb,
|
||||
showTransactionSummary: validated.showTransactionSummary,
|
||||
hideAnticipatedInstallments: validated.hideAnticipatedInstallments,
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(eq(schema.userPreferences.userId, session.user.id));
|
||||
@@ -595,6 +597,7 @@ export async function updatePreferencesAction(
|
||||
transactionsColumnOrder: validated.transactionsColumnOrder,
|
||||
attachmentMaxSizeMb: validated.attachmentMaxSizeMb,
|
||||
showTransactionSummary: validated.showTransactionSummary,
|
||||
hideAnticipatedInstallments: validated.hideAnticipatedInstallments,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ interface PreferencesFormProps {
|
||||
transactionsColumnOrder: string[] | null;
|
||||
attachmentMaxSizeMb: number;
|
||||
showTransactionSummary: boolean;
|
||||
hideAnticipatedInstallments: boolean;
|
||||
}
|
||||
|
||||
function SortableColumnItem({ id }: { id: string }) {
|
||||
@@ -87,6 +88,7 @@ export function PreferencesForm({
|
||||
transactionsColumnOrder: initialColumnOrder,
|
||||
attachmentMaxSizeMb: initialAttachmentMaxSizeMb,
|
||||
showTransactionSummary: initialShowTransactionSummary,
|
||||
hideAnticipatedInstallments: initialHideAnticipatedInstallments,
|
||||
}: PreferencesFormProps) {
|
||||
const router = useRouter();
|
||||
const [isPending, startTransition] = useTransition();
|
||||
@@ -109,6 +111,8 @@ export function PreferencesForm({
|
||||
const [showTransactionSummary, setShowTransactionSummary] = useState(
|
||||
initialShowTransactionSummary,
|
||||
);
|
||||
const [hideAnticipatedInstallments, setHideAnticipatedInstallments] =
|
||||
useState(initialHideAnticipatedInstallments);
|
||||
|
||||
const sensors = useSensors(
|
||||
useSensor(PointerSensor, { activationConstraint: { distance: 8 } }),
|
||||
@@ -135,6 +139,7 @@ export function PreferencesForm({
|
||||
transactionsColumnOrder: columnOrder,
|
||||
attachmentMaxSizeMb,
|
||||
showTransactionSummary,
|
||||
hideAnticipatedInstallments,
|
||||
});
|
||||
|
||||
if (result.success) {
|
||||
@@ -198,6 +203,26 @@ export function PreferencesForm({
|
||||
|
||||
<Separator />
|
||||
|
||||
<section className="flex items-center justify-between max-w-md gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="hide-anticipated-installments" className="text-sm">
|
||||
Ocultar parcelas antecipadas
|
||||
</Label>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Quando ativo, parcelas já antecipadas não aparecem na tabela de
|
||||
lançamentos.
|
||||
</p>
|
||||
</div>
|
||||
<Switch
|
||||
id="hide-anticipated-installments"
|
||||
checked={hideAnticipatedInstallments}
|
||||
onCheckedChange={setHideAnticipatedInstallments}
|
||||
disabled={isPending}
|
||||
/>
|
||||
</section>
|
||||
|
||||
<Separator />
|
||||
|
||||
<section className="space-y-2 max-w-md">
|
||||
<Label className="text-sm">Ordem das colunas</Label>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
|
||||
@@ -7,6 +7,7 @@ interface UserPreferences {
|
||||
transactionsColumnOrder: string[] | null;
|
||||
attachmentMaxSizeMb: number;
|
||||
showTransactionSummary: boolean;
|
||||
hideAnticipatedInstallments: boolean;
|
||||
}
|
||||
|
||||
interface ApiToken {
|
||||
@@ -36,6 +37,8 @@ export async function fetchUserPreferences(
|
||||
transactionsColumnOrder: schema.userPreferences.transactionsColumnOrder,
|
||||
attachmentMaxSizeMb: schema.userPreferences.attachmentMaxSizeMb,
|
||||
showTransactionSummary: schema.userPreferences.showTransactionSummary,
|
||||
hideAnticipatedInstallments:
|
||||
schema.userPreferences.hideAnticipatedInstallments,
|
||||
})
|
||||
.from(schema.userPreferences)
|
||||
.where(eq(schema.userPreferences.userId, userId))
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { z } from "zod";
|
||||
import { fetchAccountTransactions } from "@/features/accounts/statement-queries";
|
||||
import { fetchUserPreferences } from "@/features/settings/queries";
|
||||
import type { TransactionsExportContext } from "@/features/transactions/lib/export-types";
|
||||
import {
|
||||
buildSluggedFilters,
|
||||
@@ -60,7 +61,10 @@ export async function exportTransactionsDataAction(
|
||||
try {
|
||||
const userId = await getUserId();
|
||||
const validated = exportTransactionsSchema.parse(input);
|
||||
const filterSources = await fetchTransactionFilterSources(userId);
|
||||
const [filterSources, userPreferences] = await Promise.all([
|
||||
fetchTransactionFilterSources(userId),
|
||||
fetchUserPreferences(userId),
|
||||
]);
|
||||
const sluggedFilters = buildSluggedFilters(filterSources);
|
||||
const slugMaps = buildSlugMaps(sluggedFilters);
|
||||
|
||||
@@ -72,6 +76,8 @@ export async function exportTransactionsDataAction(
|
||||
accountId: validated.accountId ?? undefined,
|
||||
cardId: validated.cardId ?? undefined,
|
||||
payerId: validated.payerId ?? undefined,
|
||||
hideAnticipatedInstallments:
|
||||
userPreferences?.hideAnticipatedInstallments ?? false,
|
||||
});
|
||||
|
||||
const rows =
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
ilike,
|
||||
inArray,
|
||||
isNotNull,
|
||||
isNull,
|
||||
lte,
|
||||
or,
|
||||
sql,
|
||||
@@ -384,6 +385,7 @@ export const buildTransactionWhere = ({
|
||||
cardId,
|
||||
accountId,
|
||||
payerId,
|
||||
hideAnticipatedInstallments = false,
|
||||
}: {
|
||||
userId: string;
|
||||
period: string;
|
||||
@@ -392,6 +394,7 @@ export const buildTransactionWhere = ({
|
||||
cardId?: string;
|
||||
accountId?: string;
|
||||
payerId?: string;
|
||||
hideAnticipatedInstallments?: boolean;
|
||||
}): SQL[] => {
|
||||
const where: SQL[] = [eq(transactions.userId, userId)];
|
||||
|
||||
@@ -421,6 +424,15 @@ export const buildTransactionWhere = ({
|
||||
where.push(eq(transactions.payerId, payerId));
|
||||
}
|
||||
|
||||
if (hideAnticipatedInstallments) {
|
||||
where.push(
|
||||
or(
|
||||
isNull(transactions.isAnticipated),
|
||||
eq(transactions.isAnticipated, false),
|
||||
) as SQL,
|
||||
);
|
||||
}
|
||||
|
||||
if (cardId) {
|
||||
where.push(eq(transactions.cardId, cardId));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user