mirror of
https://github.com/felipegcoutinho/openmonetis.git
synced 2026-05-09 11:01:45 +00:00
Compare commits
7 Commits
51652da4f8
...
v2.5.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1df2ba787d | ||
|
|
e5d9b66cca | ||
|
|
37edb1b76d | ||
|
|
6288f5f8d4 | ||
|
|
57ac326c2a | ||
|
|
dccc18b1c1 | ||
|
|
0cb01a1d4c |
16
CHANGELOG.md
16
CHANGELOG.md
@@ -5,6 +5,22 @@ Todas as mudanças notáveis deste projeto serão documentadas neste arquivo.
|
||||
O formato é baseado em [Keep a Changelog](https://keepachangelog.com/pt-BR/1.1.0/),
|
||||
e este projeto adere ao [Versionamento Semântico](https://semver.org/lang/pt-BR/).
|
||||
|
||||
## [2.5.2] - 2026-05-04
|
||||
|
||||
Esta versão traz melhorias visuais e de usabilidade em contas, lançamentos, orçamentos, cartões e anotações: novos tipos de conta, ícones no seletor, feedback visual de limite excedido nas progress bars e refinamentos nos ícones de tarefas em anotações.
|
||||
|
||||
### Adicionado
|
||||
- Novos tipos de conta `"Dinheiro"` e `"Outros"` na lista padrão do diálogo de contas (issue #50).
|
||||
- Ícones por tipo de conta no seletor (Conta Corrente, Poupança, Carteira Digital, Investimento, Pré-Pago, Dinheiro, Outros).
|
||||
- Filtro automático: ao selecionar `"Dinheiro"` como forma de pagamento em lançamentos, o select de conta exibe apenas contas do tipo `"Dinheiro"`.
|
||||
- Sinal `+` no valor de transferências recebidas na tabela de lançamentos (mantém cor azul).
|
||||
|
||||
### Alterado
|
||||
- Forma de pagamento de novas transferências entre contas alterada de `"Pix"` para `"Transferência bancária"`.
|
||||
- Progress bar de orçamentos excedidos agora exibe indicador e fundo na cor `destructive`.
|
||||
- Progress bar de cartões com 100% do limite utilizado agora exibe indicador e fundo na cor `destructive`.
|
||||
- Ícone de tarefa não concluída no card e no modal de detalhes de anotações substituído por `RiSubtractLine` (locais sem interação de marcação).
|
||||
|
||||
## [2.5.1] - 2026-05-04
|
||||
|
||||
Versão de correção pontual focada na exibição do indicador de anexo nas tabelas de lançamentos da fatura do cartão. Em `/cards/[cardId]/invoice`, lançamentos com anexos não mostravam o ícone porque o fetcher dedicado da fatura não calculava o flag `hasAttachments`. A primeira tentativa de adicionar o EXISTS via `extras` na query relacional gerou SQL inválido (Drizzle re-aliasava `transactionAttachments.transactionId` para o alias da tabela externa). A correção definitiva troca o fetcher pela função compartilhada `fetchTransactionsWithRelations` de `features/transactions`, que já implementa o EXISTS corretamente via `select`.
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
> **⚠️ Não há versão online hospedada.** Você precisa clonar o repositório e rodar localmente ou no seu próprio servidor.
|
||||
|
||||
[](CHANGELOG.md)
|
||||
[](CHANGELOG.md)
|
||||
[](https://nextjs.org/)
|
||||
[](https://www.typescriptlang.org/)
|
||||
[](https://www.postgresql.org/)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "openmonetis",
|
||||
"version": "2.5.1",
|
||||
"version": "2.5.2",
|
||||
"private": true,
|
||||
"packageManager": "pnpm@10.33.0",
|
||||
"scripts": {
|
||||
|
||||
BIN
public/logos/dinheiro.png
Normal file
BIN
public/logos/dinheiro.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 72 KiB |
@@ -37,7 +37,9 @@ const DEFAULT_ACCOUNT_TYPES = [
|
||||
"Conta Poupança",
|
||||
"Carteira Digital",
|
||||
"Conta Investimento",
|
||||
"Dinheiro",
|
||||
"Pré-Pago | VR/VA",
|
||||
"Outros",
|
||||
] as const;
|
||||
|
||||
const DEFAULT_ACCOUNT_STATUS = ["Ativa", "Inativa"] as const;
|
||||
|
||||
@@ -12,7 +12,10 @@ import {
|
||||
SelectValue,
|
||||
} from "@/shared/components/ui/select";
|
||||
import { Textarea } from "@/shared/components/ui/textarea";
|
||||
import { StatusSelectContent } from "./account-select-items";
|
||||
import {
|
||||
AccountTypeSelectContent,
|
||||
StatusSelectContent,
|
||||
} from "./account-select-items";
|
||||
|
||||
import type { AccountFormValues } from "./types";
|
||||
|
||||
@@ -54,12 +57,16 @@ export function AccountFormFields({
|
||||
onValueChange={(value) => onChange("accountType", value)}
|
||||
>
|
||||
<SelectTrigger id="account-type" className="w-full">
|
||||
<SelectValue placeholder="Selecione o tipo" />
|
||||
<SelectValue placeholder="Selecione o tipo">
|
||||
{values.accountType && (
|
||||
<AccountTypeSelectContent label={values.accountType} />
|
||||
)}
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{accountTypes.map((type) => (
|
||||
<SelectItem key={type} value={type}>
|
||||
{type}
|
||||
<AccountTypeSelectContent label={type} />
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
|
||||
@@ -1,6 +1,18 @@
|
||||
"use client";
|
||||
|
||||
import StatusDot from "@/shared/components/status-dot";
|
||||
import { getAccountTypeIcon } from "@/shared/utils/icons";
|
||||
|
||||
export function AccountTypeSelectContent({ label }: { label: string }) {
|
||||
const icon = getAccountTypeIcon(label);
|
||||
|
||||
return (
|
||||
<span className="flex items-center gap-2">
|
||||
{icon}
|
||||
<span>{label}</span>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
export function StatusSelectContent({ label }: { label: string }) {
|
||||
const isActive = label === "Ativa";
|
||||
|
||||
@@ -90,6 +90,7 @@ export function BudgetCard({ budget, onEdit, onRemove }: BudgetCardProps) {
|
||||
<Progress
|
||||
value={usagePercent}
|
||||
className={cn("h-2.5", exceeded && "bg-destructive/20!")}
|
||||
indicatorClassName={cn(exceeded && "bg-destructive")}
|
||||
aria-label={`${usagePercent.toFixed(1)}% do orçamento utilizado`}
|
||||
/>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
|
||||
@@ -69,6 +69,7 @@ export function CardItem({
|
||||
|
||||
const usagePercent =
|
||||
limit > 0 ? Math.min(Math.max((used / limit) * 100, 0), 100) : 0;
|
||||
const exceeded = usagePercent >= 100;
|
||||
|
||||
const logoPath = resolveLogoSrc(logo);
|
||||
const brandAsset = resolveCardBrandAsset(brand);
|
||||
@@ -194,7 +195,8 @@ export function CardItem({
|
||||
<div className="flex flex-col gap-2">
|
||||
<Progress
|
||||
value={usagePercent}
|
||||
className="h-2.5"
|
||||
className={cn("h-2.5", exceeded && "bg-destructive/20!")}
|
||||
indicatorClassName={cn(exceeded && "bg-destructive")}
|
||||
aria-label={`${usagePercent.toFixed(0)}% do limite utilizado`}
|
||||
/>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
|
||||
@@ -13,7 +13,7 @@ const formatOccurrences = (value: number | null) => {
|
||||
return "Recorrência contínua";
|
||||
}
|
||||
|
||||
return `${value} recorrências`;
|
||||
return `${value} recorrências mensais`;
|
||||
};
|
||||
|
||||
export function RecurringExpensesWidget({
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
RiFileList2Line,
|
||||
RiInboxUnarchiveLine,
|
||||
RiPencilLine,
|
||||
RiSubtractLine,
|
||||
} from "@remixicon/react";
|
||||
import {
|
||||
buildNoteDisplayTitle,
|
||||
@@ -101,7 +102,7 @@ export function NoteCard({
|
||||
{task.completed ? (
|
||||
<RiCheckLine className="h-4 w-4 text-success" />
|
||||
) : (
|
||||
<div className="h-4 w-4 rounded-sm border border-input" />
|
||||
<RiSubtractLine className="h-4 w-4 text-muted-foreground" />
|
||||
)}
|
||||
</div>
|
||||
<span
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { RiCheckLine } from "@remixicon/react";
|
||||
import { RiCheckLine, RiSubtractLine } from "@remixicon/react";
|
||||
import {
|
||||
buildNoteDisplayTitle,
|
||||
formatNoteCreatedAtLong,
|
||||
@@ -69,7 +69,7 @@ export function NoteDetailsDialog({
|
||||
{task.completed ? (
|
||||
<RiCheckLine className="h-4 w-4 text-success" />
|
||||
) : (
|
||||
<div className="h-4 w-4 rounded-sm border border-input" />
|
||||
<RiSubtractLine className="h-4 w-4 text-muted-foreground" />
|
||||
)}
|
||||
</div>
|
||||
<span
|
||||
|
||||
@@ -93,7 +93,9 @@ export function PaymentMethodSection({
|
||||
? accountOptions.filter(
|
||||
(option) => option.accountType === "Pré-Pago | VR/VA",
|
||||
)
|
||||
: accountOptions;
|
||||
: formState.paymentMethod === "Dinheiro"
|
||||
? accountOptions.filter((option) => option.accountType === "Dinheiro")
|
||||
: accountOptions;
|
||||
|
||||
const hasSecondaryColumn = isCartaoSelected || showContaSelect;
|
||||
|
||||
|
||||
@@ -348,10 +348,12 @@ function buildColumns({
|
||||
cell: ({ row }) => {
|
||||
const isReceita = row.original.transactionType === "Receita";
|
||||
const isTransfer = row.original.transactionType === "Transferência";
|
||||
const isIncomingTransfer =
|
||||
isTransfer && Number(row.original.amount) > 0;
|
||||
return (
|
||||
<MoneyValues
|
||||
amount={row.original.amount}
|
||||
showPositiveSign={isReceita}
|
||||
showPositiveSign={isReceita || isIncomingTransfer}
|
||||
className={cn(
|
||||
"whitespace-nowrap",
|
||||
isReceita ? "text-success" : "text-foreground",
|
||||
|
||||
@@ -7,9 +7,12 @@ import { cn } from "@/shared/utils/ui";
|
||||
|
||||
function Progress({
|
||||
className,
|
||||
indicatorClassName,
|
||||
value,
|
||||
...props
|
||||
}: React.ComponentProps<typeof ProgressPrimitive.Root>) {
|
||||
}: React.ComponentProps<typeof ProgressPrimitive.Root> & {
|
||||
indicatorClassName?: string;
|
||||
}) {
|
||||
return (
|
||||
<ProgressPrimitive.Root
|
||||
data-slot="progress"
|
||||
@@ -21,7 +24,10 @@ function Progress({
|
||||
>
|
||||
<ProgressPrimitive.Indicator
|
||||
data-slot="progress-indicator"
|
||||
className="bg-primary h-full w-full flex-1 transition-all"
|
||||
className={cn(
|
||||
"bg-primary h-full w-full flex-1 transition-all",
|
||||
indicatorClassName,
|
||||
)}
|
||||
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
|
||||
/>
|
||||
</ProgressPrimitive.Root>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export const TRANSFER_CATEGORY_NAME = "Transferência interna";
|
||||
export const TRANSFER_ESTABLISHMENT_SAIDA = "Saída - Transf. entre contas";
|
||||
export const TRANSFER_ESTABLISHMENT_ENTRADA = "Entrada - Transf. entre contas";
|
||||
export const TRANSFER_PAYMENT_METHOD = "Pix";
|
||||
export const TRANSFER_PAYMENT_METHOD = "Transferência bancária";
|
||||
export const TRANSFER_CONDITION = "À vista";
|
||||
|
||||
@@ -36,13 +36,37 @@ export const getConditionIcon = (condition: string): ReactNode => {
|
||||
return registry[key] ?? null;
|
||||
};
|
||||
|
||||
export const getAccountTypeIcon = (accountType: string): ReactNode => {
|
||||
const key = normalizeKey(accountType);
|
||||
|
||||
const registry: Record<string, ReactNode> = {
|
||||
contacorrente: <RemixIcons.RiBankLine className={ICON_CLASS} aria-hidden />,
|
||||
contapoupanca: (
|
||||
<RemixIcons.RiSafe2Line className={ICON_CLASS} aria-hidden />
|
||||
),
|
||||
carteiradigital: (
|
||||
<RemixIcons.RiWalletLine className={ICON_CLASS} aria-hidden />
|
||||
),
|
||||
containvestimento: (
|
||||
<RemixIcons.RiFundsLine className={ICON_CLASS} aria-hidden />
|
||||
),
|
||||
prepagovrva: <RemixIcons.RiCouponLine className={ICON_CLASS} aria-hidden />,
|
||||
dinheiro: <RemixIcons.RiCashLine className={ICON_CLASS} aria-hidden />,
|
||||
outros: <RemixIcons.RiMoreFill className={ICON_CLASS} aria-hidden />,
|
||||
};
|
||||
|
||||
return (
|
||||
registry[key] ?? (
|
||||
<RemixIcons.RiBankLine className={ICON_CLASS} aria-hidden />
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
export const getPaymentMethodIcon = (paymentMethod: string): ReactNode => {
|
||||
const key = normalizeKey(paymentMethod);
|
||||
|
||||
const registry: Record<string, ReactNode> = {
|
||||
dinheiro: (
|
||||
<RemixIcons.RiMoneyDollarCircleLine className={ICON_CLASS} aria-hidden />
|
||||
),
|
||||
dinheiro: <RemixIcons.RiCashLine className={ICON_CLASS} aria-hidden />,
|
||||
pix: <RemixIcons.RiPixLine className={ICON_CLASS} aria-hidden />,
|
||||
boleto: <RemixIcons.RiBarcodeLine className={ICON_CLASS} aria-hidden />,
|
||||
credito: (
|
||||
|
||||
Reference in New Issue
Block a user