mirror of
https://github.com/felipegcoutinho/openmonetis.git
synced 2026-05-09 11:01:45 +00:00
style: padroniza widgets e listas do dashboard
This commit is contained in:
@@ -56,8 +56,6 @@ const VARIANT_CONFIG = {
|
||||
increase: "text-success",
|
||||
decrease: "text-destructive",
|
||||
},
|
||||
listItemClassName:
|
||||
"flex flex-col gap-1.5 py-2 border-b border-dashed last:border-0",
|
||||
includeBudgetAmount: true,
|
||||
},
|
||||
expense: {
|
||||
@@ -70,8 +68,6 @@ const VARIANT_CONFIG = {
|
||||
increase: "text-destructive",
|
||||
decrease: "text-success",
|
||||
},
|
||||
listItemClassName:
|
||||
"flex flex-col py-2 border-b border-dashed last:border-0",
|
||||
includeBudgetAmount: false,
|
||||
},
|
||||
} as const;
|
||||
@@ -194,7 +190,7 @@ export function CategoryBreakdownWidgetView({
|
||||
</div>
|
||||
|
||||
<TabsContent value="list" className="mt-0">
|
||||
<div className="flex flex-col px-0">
|
||||
<div>
|
||||
{data.categories.map((category, index) => {
|
||||
const hasIncrease =
|
||||
category.percentageChange !== null &&
|
||||
@@ -218,11 +214,8 @@ export function CategoryBreakdownWidgetView({
|
||||
: "text-muted-foreground";
|
||||
|
||||
return (
|
||||
<div
|
||||
key={category.categoryId}
|
||||
className={config.listItemClassName}
|
||||
>
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div key={category.categoryId}>
|
||||
<div className="flex items-center justify-between gap-3 transition-all duration-300 py-2">
|
||||
<div className="flex min-w-0 flex-1 items-center gap-2">
|
||||
<CategoryIconBadge
|
||||
icon={category.categoryIcon}
|
||||
@@ -245,7 +238,7 @@ export function CategoryBreakdownWidgetView({
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 text-xs text-muted-foreground">
|
||||
<div className="flex flex-wrap items-center gap-x-2 gap-y-0.5 text-xs text-muted-foreground">
|
||||
<span>
|
||||
{formatPercentage(
|
||||
category.percentageOfTotal,
|
||||
@@ -253,6 +246,36 @@ export function CategoryBreakdownWidgetView({
|
||||
)}{" "}
|
||||
da {config.shareLabel}
|
||||
</span>
|
||||
{hasBudget && category.budgetUsedPercentage !== null ? (
|
||||
<>
|
||||
<span aria-hidden>·</span>
|
||||
<span
|
||||
className={`flex items-center gap-1 ${budgetExceeded ? "text-destructive" : "text-info"}`}
|
||||
>
|
||||
<RiWallet3Line className="size-3 shrink-0" />
|
||||
{budgetExceeded ? (
|
||||
<>
|
||||
excedeu{" "}
|
||||
<span className="font-medium">
|
||||
{formatCurrency(exceededAmount)}
|
||||
</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{formatPercentage(
|
||||
category.budgetUsedPercentage,
|
||||
config.percentageDigits,
|
||||
)}{" "}
|
||||
do limite
|
||||
{config.includeBudgetAmount &&
|
||||
category.budgetAmount !== null
|
||||
? ` ${formatCurrency(category.budgetAmount)}`
|
||||
: ""}
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -280,48 +303,6 @@ export function CategoryBreakdownWidgetView({
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{hasBudget && category.budgetUsedPercentage !== null ? (
|
||||
<div className="ml-11 flex items-center gap-1.5 text-xs">
|
||||
<RiWallet3Line
|
||||
className={`size-3 ${
|
||||
budgetExceeded ? "text-destructive" : "text-info"
|
||||
}`}
|
||||
/>
|
||||
<span
|
||||
className={
|
||||
budgetExceeded ? "text-destructive" : "text-info"
|
||||
}
|
||||
>
|
||||
{budgetExceeded ? (
|
||||
<>
|
||||
{formatPercentage(
|
||||
category.budgetUsedPercentage,
|
||||
config.percentageDigits,
|
||||
)}{" "}
|
||||
do limite
|
||||
{config.includeBudgetAmount &&
|
||||
category.budgetAmount !== null
|
||||
? ` ${formatCurrency(category.budgetAmount)}`
|
||||
: ""}{" "}
|
||||
- excedeu em {formatCurrency(exceededAmount)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{formatPercentage(
|
||||
category.budgetUsedPercentage,
|
||||
config.percentageDigits,
|
||||
)}{" "}
|
||||
do limite
|
||||
{config.includeBudgetAmount &&
|
||||
category.budgetAmount !== null
|
||||
? ` ${formatCurrency(category.budgetAmount)}`
|
||||
: ""}
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -3,15 +3,14 @@ import {
|
||||
RiArrowDownSFill,
|
||||
RiArrowUpLine,
|
||||
RiArrowUpSFill,
|
||||
RiCashLine,
|
||||
RiIncreaseDecreaseLine,
|
||||
RiCalendarCheckLine,
|
||||
RiScalesLine,
|
||||
RiSubtractLine,
|
||||
} from "@remixicon/react";
|
||||
import type { DashboardCardMetrics } from "@/features/dashboard/dashboard-metrics-queries";
|
||||
import MoneyValues from "@/shared/components/money-values";
|
||||
import {
|
||||
Card,
|
||||
CardAction,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
@@ -32,20 +31,33 @@ const CARDS = [
|
||||
key: "receitas",
|
||||
icon: RiArrowUpLine,
|
||||
invertTrend: false,
|
||||
cardClass: "",
|
||||
iconClass: "text-success",
|
||||
},
|
||||
{
|
||||
label: "Despesas",
|
||||
key: "despesas",
|
||||
icon: RiArrowDownLine,
|
||||
invertTrend: true,
|
||||
cardClass: "",
|
||||
iconClass: "text-destructive",
|
||||
},
|
||||
{
|
||||
label: "Balanço",
|
||||
key: "balanco",
|
||||
icon: RiIncreaseDecreaseLine,
|
||||
icon: RiScalesLine,
|
||||
invertTrend: false,
|
||||
cardClass: "",
|
||||
iconClass: "text-amber-500",
|
||||
},
|
||||
{
|
||||
label: "Previsto",
|
||||
key: "previsto",
|
||||
icon: RiCalendarCheckLine,
|
||||
invertTrend: false,
|
||||
cardClass: "border border-dashed",
|
||||
iconClass: "",
|
||||
},
|
||||
{ label: "Previsto", key: "previsto", icon: RiCashLine, invertTrend: false },
|
||||
] as const;
|
||||
|
||||
const TREND_ICONS = {
|
||||
@@ -62,7 +74,7 @@ const getTrend = (current: number, previous: number): Trend => {
|
||||
};
|
||||
|
||||
const getPercentChange = (current: number, previous: number): string => {
|
||||
const EPSILON = 0.01; // Considera valores menores que 1 centavo como zero
|
||||
const EPSILON = 0.01;
|
||||
|
||||
if (Math.abs(previous) < EPSILON) {
|
||||
if (Math.abs(current) < EPSILON) return "0%";
|
||||
@@ -80,48 +92,49 @@ const getPercentChange = (current: number, previous: number): string => {
|
||||
};
|
||||
|
||||
const getTrendColor = (trend: Trend, invertTrend: boolean): string => {
|
||||
if (trend === "flat") return "";
|
||||
if (trend === "flat") return "text-muted-foreground";
|
||||
const isPositive = invertTrend ? trend === "down" : trend === "up";
|
||||
return isPositive
|
||||
? "text-success border-success"
|
||||
: "text-destructive border-destructive";
|
||||
return isPositive ? "text-success" : "text-destructive";
|
||||
};
|
||||
|
||||
export function DashboardMetricsCards({ metrics }: DashboardMetricsCardsProps) {
|
||||
return (
|
||||
<div className="*:data-[slot=card]:from-primary/5 *:data-[slot=card]:to-card dark:*:data-[slot=card]:bg-card grid grid-cols-1 gap-3 @xl/main:grid-cols-2 @5xl/main:grid-cols-4">
|
||||
{CARDS.map(({ label, key, icon: Icon, invertTrend }) => {
|
||||
<div className="grid grid-cols-1 gap-3 @xl/main:grid-cols-2 @5xl/main:grid-cols-4">
|
||||
{CARDS.map(
|
||||
({ label, key, icon: Icon, invertTrend, cardClass, iconClass }) => {
|
||||
const metric = metrics[key];
|
||||
const trend = getTrend(metric.current, metric.previous);
|
||||
const TrendIcon = TREND_ICONS[trend];
|
||||
const trendColor = getTrendColor(trend, invertTrend);
|
||||
|
||||
return (
|
||||
<Card key={label} className="@container/card gap-2">
|
||||
<Card
|
||||
key={label}
|
||||
className={`@container/card flex flex-col justify-between min-h-34 ${cardClass}`}
|
||||
>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-1 tracking-tighter lowercase">
|
||||
<Icon className="size-4" />
|
||||
<CardTitle className="flex items-center gap-1 tracking-tight lowercase">
|
||||
<Icon className={`size-4 ${iconClass}`} />
|
||||
{label}
|
||||
</CardTitle>
|
||||
<div className="flex items-baseline gap-2 mt-auto pt-2">
|
||||
<MoneyValues className="text-2xl" amount={metric.current} />
|
||||
<CardAction>
|
||||
<div className={`flex items-center text-xs ${trendColor}`}>
|
||||
<TrendIcon size={16} />
|
||||
<TrendIcon size={14} />
|
||||
{getPercentChange(metric.current, metric.previous)}
|
||||
</div>
|
||||
</CardAction>
|
||||
</CardHeader>
|
||||
<CardFooter className="flex-col items-start gap-2 text-sm">
|
||||
<div className="line-clamp-1 flex gap-2 text-xs">
|
||||
mês anterior
|
||||
</div>
|
||||
<div className="text-foreground">
|
||||
</CardHeader>
|
||||
<CardFooter className="text-sm">
|
||||
<div className="flex items-center gap-1 text-xs text-muted-foreground">
|
||||
<span>vs. mês anterior</span>
|
||||
<MoneyValues amount={metric.previous} />
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
},
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,9 +9,9 @@ export function DashboardWelcome({ name }: { name?: string | null }) {
|
||||
<section className="p-2">
|
||||
<div className="tracking-tight">
|
||||
<h1 className="text-xl">
|
||||
{greeting}, {displayName}
|
||||
{greeting}, <span className="text-primary">{displayName}</span>
|
||||
</h1>
|
||||
<p className="text-sm mt-1">{formattedDate}</p>
|
||||
<p className="text-sm mt-1 text-muted-foreground">{formattedDate}</p>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
||||
@@ -26,7 +26,7 @@ export function GoalProgressItem({
|
||||
const percentageDelta = item.usedPercentage - 100;
|
||||
|
||||
return (
|
||||
<li className="border-b border-dashed py-2 last:border-b-0 last:pb-0">
|
||||
<div className="transition-all duration-300 py-2">
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="flex min-w-0 flex-1 items-start gap-2">
|
||||
<CategoryIconBadge
|
||||
@@ -52,9 +52,9 @@ export function GoalProgressItem({
|
||||
</span>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
variant="outline"
|
||||
size="icon-sm"
|
||||
className="size-7 text-muted-foreground hover:text-foreground"
|
||||
className="text-muted-foreground hover:text-foreground"
|
||||
onClick={() => onEdit(item)}
|
||||
aria-label={`Editar orçamento de ${item.categoryName}`}
|
||||
>
|
||||
@@ -65,6 +65,6 @@ export function GoalProgressItem({
|
||||
<div className="ml-11 mt-1.5">
|
||||
<Progress value={progressValue} />
|
||||
</div>
|
||||
</li>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ export function InstallmentExpenseListItem({
|
||||
} = buildInstallmentExpenseDisplay(expense);
|
||||
|
||||
return (
|
||||
<li className="flex items-center gap-3 border-b border-dashed pb-3 last:border-b-0 last:pb-0">
|
||||
<div className="flex items-center gap-3 transition-all duration-300 py-2">
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div className="flex min-w-0 items-center gap-2">
|
||||
@@ -71,6 +71,6 @@ export function InstallmentExpenseListItem({
|
||||
|
||||
<Progress value={progress} className="mt-1 h-2" />
|
||||
</div>
|
||||
</li>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ export function InstallmentExpensesList({
|
||||
}
|
||||
|
||||
return (
|
||||
<ul className="flex flex-col gap-2">
|
||||
<ul className="flex flex-col">
|
||||
{expenses.map((expense) => (
|
||||
<InstallmentExpenseListItem key={expense.id} expense={expense} />
|
||||
))}
|
||||
|
||||
@@ -9,7 +9,7 @@ export function InstallmentExpensesWidgetView({
|
||||
data,
|
||||
}: InstallmentExpensesWidgetViewProps) {
|
||||
return (
|
||||
<div className="flex flex-col gap-4 px-0">
|
||||
<div className="flex flex-col">
|
||||
<InstallmentExpensesList expenses={data.expenses} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -27,12 +27,12 @@ export function MyAccountsWidget({
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex justify-between py-2">
|
||||
<div className="flex justify-between py-1">
|
||||
Saldo Total
|
||||
<MoneyValues className="text-2xl" amount={totalBalance} />
|
||||
</div>
|
||||
|
||||
<div className="py-2 px-0">
|
||||
<div>
|
||||
{displayedAccounts.length === 0 ? (
|
||||
<div className="-mt-10">
|
||||
<WidgetEmptyState
|
||||
@@ -49,12 +49,12 @@ export function MyAccountsWidget({
|
||||
const logoSrc = resolveLogoSrc(account.logo);
|
||||
|
||||
return (
|
||||
<li
|
||||
<div
|
||||
key={account.id}
|
||||
className="flex items-center justify-between gap-2 border-b border-dashed py-2 last:border-0"
|
||||
className="flex items-center justify-between transition-all duration-300 py-1.5 "
|
||||
>
|
||||
<div className="flex min-w-0 flex-1 items-center gap-3">
|
||||
<div className="relative size-10 overflow-hidden">
|
||||
<div className="flex min-w-0 flex-1 items-center gap-2 py-1">
|
||||
<div className="relative size-9.5 overflow-hidden">
|
||||
{logoSrc ? (
|
||||
<Image
|
||||
src={logoSrc}
|
||||
@@ -88,7 +88,7 @@ export function MyAccountsWidget({
|
||||
<div className="flex flex-col items-end gap-0.5 text-right">
|
||||
<MoneyValues amount={account.balance} />
|
||||
</div>
|
||||
</li>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
|
||||
@@ -23,7 +23,7 @@ export function NoteListItem({
|
||||
const createdAtLabel = formatNoteCreatedAt(note.createdAt);
|
||||
|
||||
return (
|
||||
<li className="flex items-center justify-between gap-2 border-b border-dashed py-2 last:border-b-0 last:pb-0">
|
||||
<div className="flex items-center justify-between gap-2 transition-all duration-300 py-2">
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate text-sm font-medium text-foreground">
|
||||
{displayTitle}
|
||||
@@ -40,9 +40,9 @@ export function NoteListItem({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex shrink-0 items-center">
|
||||
<div className="flex shrink-0 items-center gap-1">
|
||||
<Button
|
||||
variant="ghost"
|
||||
variant="outline"
|
||||
size="icon-sm"
|
||||
className="text-muted-foreground hover:text-foreground"
|
||||
onClick={() => onOpenEdit(note)}
|
||||
@@ -51,7 +51,7 @@ export function NoteListItem({
|
||||
<RiPencilLine className="size-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
variant="outline"
|
||||
size="icon-sm"
|
||||
className="text-muted-foreground hover:text-foreground"
|
||||
onClick={() => onOpenDetails(note)}
|
||||
@@ -60,6 +60,6 @@ export function NoteListItem({
|
||||
<RiFileList2Line className="size-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</li>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ export function PayersWidget({ payers }: PayersWidgetProps) {
|
||||
description="Quando houver despesas associadas a pagadores, eles aparecerão aqui."
|
||||
/>
|
||||
) : (
|
||||
<ul className="flex flex-col">
|
||||
<div className="flex flex-col">
|
||||
{payers.map((payer) => {
|
||||
const initials = buildInitials(payer.name);
|
||||
const hasValidPercentageChange =
|
||||
@@ -59,12 +59,12 @@ export function PayersWidget({ payers }: PayersWidgetProps) {
|
||||
: null;
|
||||
|
||||
return (
|
||||
<li
|
||||
<div
|
||||
key={payer.id}
|
||||
className="flex items-center justify-between border-b border-dashed last:border-b-0 last:pb-0"
|
||||
className="flex items-center justify-between transition-all duration-300 py-1.5"
|
||||
>
|
||||
<div className="flex min-w-0 flex-1 items-center gap-2 py-2">
|
||||
<Avatar className="size-10 shrink-0">
|
||||
<div className="flex min-w-0 flex-1 items-center gap-2 py-1">
|
||||
<Avatar className="size-9.5 shrink-0">
|
||||
<AvatarImage
|
||||
src={getAvatarSrc(payer.avatarUrl)}
|
||||
alt={`Avatar de ${payer.name}`}
|
||||
@@ -118,10 +118,10 @@ export function PayersWidget({ payers }: PayersWidgetProps) {
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</li>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
);
|
||||
|
||||
@@ -26,7 +26,7 @@ export function PaymentBreakdownListItem({
|
||||
item,
|
||||
}: PaymentBreakdownListItemProps) {
|
||||
return (
|
||||
<li className="flex items-center gap-3 border-b border-dashed pb-3 last:border-b-0 last:pb-0">
|
||||
<div className="flex items-center gap-3 transition-all duration-300 py-1.5">
|
||||
<div className={ICON_WRAPPER_CLASS}>{item.icon}</div>
|
||||
|
||||
<div className="min-w-0 flex-1">
|
||||
@@ -46,6 +46,6 @@ export function PaymentBreakdownListItem({
|
||||
<Progress value={item.percentage} />
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ export function PaymentStatusWidgetView({
|
||||
pending={data.income.pending}
|
||||
/>
|
||||
|
||||
<div className="border-t border-dashed" />
|
||||
<div className="border-t" />
|
||||
|
||||
<PaymentStatusCategorySection
|
||||
title="A Pagar"
|
||||
|
||||
@@ -170,12 +170,12 @@ export function PurchasesByCategoryWidget({
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<ul className="flex flex-col">
|
||||
<div className="flex flex-col">
|
||||
{currentTransactions.map((transaction) => {
|
||||
return (
|
||||
<li
|
||||
<div
|
||||
key={transaction.id}
|
||||
className="flex items-center justify-between gap-3 border-b border-dashed py-2 last:border-b-0 last:pb-0"
|
||||
className="flex items-center justify-between gap-3 transition-all duration-300 py-2"
|
||||
>
|
||||
<div className="flex min-w-0 flex-1 items-center gap-3">
|
||||
<EstabelecimentoLogo name={transaction.name} size={37} />
|
||||
@@ -193,10 +193,10 @@ export function PurchasesByCategoryWidget({
|
||||
<div className="shrink-0 text-foreground">
|
||||
<MoneyValues amount={transaction.amount} />
|
||||
</div>
|
||||
</li>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -30,13 +30,12 @@ export function RecurringExpensesWidget({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4 px-0">
|
||||
<ul className="flex flex-col gap-2">
|
||||
<div className="flex flex-col">
|
||||
{data.expenses.map((expense) => {
|
||||
return (
|
||||
<li
|
||||
<div
|
||||
key={expense.id}
|
||||
className="flex items-center gap-3 border-b border-dashed pb-2 last:border-b-0 last:pb-0"
|
||||
className="flex items-center gap-2 transition-all duration-300 py-1.5"
|
||||
>
|
||||
<EstabelecimentoLogo name={expense.name} size={37} />
|
||||
|
||||
@@ -56,10 +55,9 @@ export function RecurringExpensesWidget({
|
||||
<span>{formatOccurrences(expense.recurrenceCount)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -27,12 +27,12 @@ export function TopEstablishmentsWidget({
|
||||
description="Quando houver despesas registradas, elas aparecerão aqui."
|
||||
/>
|
||||
) : (
|
||||
<ul className="flex flex-col">
|
||||
<div className="flex flex-col">
|
||||
{data.establishments.map((establishment) => {
|
||||
return (
|
||||
<li
|
||||
<div
|
||||
key={establishment.id}
|
||||
className="flex items-center justify-between gap-3 border-b border-dashed py-2 last:border-b-0 last:pb-0"
|
||||
className="flex items-center justify-between gap-3 transition-all duration-300 py-2"
|
||||
>
|
||||
<div className="flex min-w-0 flex-1 items-center gap-3">
|
||||
<EstabelecimentoLogo name={establishment.name} size={37} />
|
||||
@@ -50,10 +50,10 @@ export function TopEstablishmentsWidget({
|
||||
<div className="shrink-0 text-foreground">
|
||||
<MoneyValues amount={establishment.amount} />
|
||||
</div>
|
||||
</li>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -85,9 +85,7 @@ export function TopExpensesWidget({
|
||||
htmlFor="card-only-toggle"
|
||||
className="text-sm text-muted-foreground"
|
||||
>
|
||||
{cardOnly
|
||||
? "Somente cartões de crédito ou débito."
|
||||
: "Todas as despesas"}
|
||||
Apenas cartões
|
||||
</label>
|
||||
<Switch
|
||||
id="card-only-toggle"
|
||||
@@ -107,12 +105,12 @@ export function TopExpensesWidget({
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<ul className="flex flex-col">
|
||||
<div className="flex flex-col">
|
||||
{data.expenses.map((expense) => {
|
||||
return (
|
||||
<li
|
||||
<div
|
||||
key={expense.id}
|
||||
className="flex items-center justify-between gap-3 border-b border-dashed py-2 last:border-b-0 last:pb-0"
|
||||
className="flex items-center justify-between gap-3 transition-all duration-300 py-2"
|
||||
>
|
||||
<div className="flex min-w-0 flex-1 items-center gap-3">
|
||||
<EstabelecimentoLogo name={expense.name} size={37} />
|
||||
@@ -130,10 +128,10 @@ export function TopExpensesWidget({
|
||||
<div className="shrink-0 text-foreground">
|
||||
<MoneyValues amount={expense.amount} />
|
||||
</div>
|
||||
</li>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -42,10 +42,7 @@ export function PayerCardUsageCard({ items }: PagadorCardUsageCardProps) {
|
||||
const logoPath = resolveLogoSrc(item.logo);
|
||||
const initials = buildInitials(item.name);
|
||||
return (
|
||||
<li
|
||||
key={item.id}
|
||||
className="flex items-center justify-between border-b border-dashed last:border-b-0 last:pb-0"
|
||||
>
|
||||
<div key={item.id} className="flex items-center justify-between">
|
||||
<div className="flex min-w-0 flex-1 items-center gap-2 py-2">
|
||||
<div className="flex size-9 shrink-0 items-center justify-center overflow-hidden rounded-full">
|
||||
{logoPath ? (
|
||||
@@ -72,7 +69,7 @@ export function PayerCardUsageCard({ items }: PagadorCardUsageCardProps) {
|
||||
</div>
|
||||
</div>
|
||||
<MoneyValues amount={item.amount} />
|
||||
</li>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
|
||||
@@ -9,6 +9,7 @@ import { EstabelecimentoLogo } from "@/features/transactions/components/shared/e
|
||||
import MoneyValues from "@/shared/components/money-values";
|
||||
import { CardContent } from "@/shared/components/ui/card";
|
||||
import { Progress } from "@/shared/components/ui/progress";
|
||||
import { Separator } from "@/shared/components/ui/separator";
|
||||
import { WidgetEmptyState } from "@/shared/components/widget-empty-state";
|
||||
import type {
|
||||
PayerBoletoItem,
|
||||
@@ -41,10 +42,7 @@ export function PayerBoletoCard({ items }: PagadorBoletoCardProps) {
|
||||
{items.map((item) => {
|
||||
const statusLabel = buildBillStatusLabel(item);
|
||||
return (
|
||||
<li
|
||||
key={item.id}
|
||||
className="flex items-center justify-between border-b border-dashed last:border-b-0 last:pb-0"
|
||||
>
|
||||
<div key={item.id} className="flex items-center justify-between">
|
||||
<div className="flex min-w-0 flex-1 items-center gap-3 py-2">
|
||||
<EstabelecimentoLogo name={item.name} size={36} />
|
||||
<div className="min-w-0">
|
||||
@@ -64,7 +62,7 @@ export function PayerBoletoCard({ items }: PagadorBoletoCardProps) {
|
||||
</div>
|
||||
</div>
|
||||
<MoneyValues amount={item.amount} />
|
||||
</li>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
@@ -105,7 +103,9 @@ export function PayerPaymentStatusCard({
|
||||
<span className="text-sm font-medium text-foreground">Pago</span>
|
||||
<MoneyValues amount={paidAmount} />
|
||||
</div>
|
||||
|
||||
<Progress value={paidPercentage} className="h-2" />
|
||||
|
||||
<div className="flex items-center justify-between gap-4 text-sm">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<RiCheckboxCircleLine className="size-3 text-success" />
|
||||
@@ -117,7 +117,7 @@ export function PayerPaymentStatusCard({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-dashed" />
|
||||
<Separator />
|
||||
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
|
||||
Reference in New Issue
Block a user