mirror of
https://github.com/felipegcoutinho/openmonetis.git
synced 2026-06-14 00:56:00 +00:00
Ajusta widgets do dashboard
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
@custom-variant dark (&:is(.dark *));
|
@custom-variant dark (&:is(.dark *));
|
||||||
|
|
||||||
@theme {
|
@theme {
|
||||||
--spacing-custom-height-card: 29rem;
|
--spacing-custom-height-card: 30rem;
|
||||||
--spacing-8xl: 90rem;
|
--spacing-8xl: 90rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,7 +117,7 @@
|
|||||||
--destructive: oklch(62% 0.2 28);
|
--destructive: oklch(62% 0.2 28);
|
||||||
--destructive-foreground: oklch(98% 0.005 30);
|
--destructive-foreground: oklch(98% 0.005 30);
|
||||||
|
|
||||||
--border: oklch(24.576% 0.0072 67.399);
|
--border: oklch(29.675% 0.01144 67.3);
|
||||||
--input: var(--border);
|
--input: var(--border);
|
||||||
--ring: var(--primary);
|
--ring: var(--primary);
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export function BillListItem({ bill, period, onPay }: BillListItemProps) {
|
|||||||
<TooltipTrigger asChild>
|
<TooltipTrigger asChild>
|
||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
"cursor-help rounded-full py-0.5",
|
"cursor-help",
|
||||||
bill.isSettled && "text-success font-semibold",
|
bill.isSettled && "text-success font-semibold",
|
||||||
overdue && "text-destructive font-semibold",
|
overdue && "text-destructive font-semibold",
|
||||||
)}
|
)}
|
||||||
@@ -80,7 +80,6 @@ export function BillListItem({ bill, period, onPay }: BillListItemProps) {
|
|||||||
) : (
|
) : (
|
||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
"rounded-full py-0.5",
|
|
||||||
bill.isSettled && "text-success font-semibold",
|
bill.isSettled && "text-success font-semibold",
|
||||||
overdue && "text-destructive font-semibold",
|
overdue && "text-destructive font-semibold",
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -102,18 +102,19 @@ export function CategoryBreakdownListItem({
|
|||||||
className="text-foreground font-medium"
|
className="text-foreground font-medium"
|
||||||
amount={category.currentAmount}
|
amount={category.currentAmount}
|
||||||
/>
|
/>
|
||||||
|
{category.percentageChange !== null ? (
|
||||||
|
<span className="flex items-center gap-1 text-xs text-muted-foreground">
|
||||||
<PercentageChangeIndicator
|
<PercentageChangeIndicator
|
||||||
value={category.percentageChange}
|
value={category.percentageChange}
|
||||||
label={
|
label={formatPercentage(
|
||||||
category.percentageChange !== null
|
|
||||||
? formatPercentage(
|
|
||||||
category.percentageChange,
|
category.percentageChange,
|
||||||
config.percentageDigits,
|
config.percentageDigits,
|
||||||
)
|
)}
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
positiveTrend={config.positiveTrend}
|
positiveTrend={config.positiveTrend}
|
||||||
/>
|
/>
|
||||||
|
<span>vs. mês ant.</span>
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
RiBankCard2Line,
|
RiBankCard2Line,
|
||||||
|
RiChat1Line,
|
||||||
RiCheckboxCircleFill,
|
RiCheckboxCircleFill,
|
||||||
RiFileList2Line,
|
RiFileList2Line,
|
||||||
RiTimeLine,
|
RiTimeLine,
|
||||||
@@ -30,6 +31,11 @@ import {
|
|||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@/shared/components/ui/dialog";
|
} from "@/shared/components/ui/dialog";
|
||||||
import { Progress } from "@/shared/components/ui/progress";
|
import { Progress } from "@/shared/components/ui/progress";
|
||||||
|
import {
|
||||||
|
Tooltip,
|
||||||
|
TooltipContent,
|
||||||
|
TooltipTrigger,
|
||||||
|
} from "@/shared/components/ui/tooltip";
|
||||||
import { resolveLogoSrc } from "@/shared/lib/logo";
|
import { resolveLogoSrc } from "@/shared/lib/logo";
|
||||||
import { cn } from "@/shared/utils";
|
import { cn } from "@/shared/utils";
|
||||||
import type { InstallmentGroup } from "./types";
|
import type { InstallmentGroup } from "./types";
|
||||||
@@ -83,6 +89,7 @@ export function InstallmentGroupCard({
|
|||||||
);
|
);
|
||||||
const cardLogoSrc = resolveLogoSrc(group.cartaoLogo);
|
const cardLogoSrc = resolveLogoSrc(group.cartaoLogo);
|
||||||
const cardName = group.cartaoName ?? "Compra parcelada";
|
const cardName = group.cartaoName ?? "Compra parcelada";
|
||||||
|
const hasNote = Boolean(group.note?.trim().length);
|
||||||
const untrackedLabel =
|
const untrackedLabel =
|
||||||
group.untrackedInstallments === 1
|
group.untrackedInstallments === 1
|
||||||
? "1 parcela anterior fora do acompanhamento"
|
? "1 parcela anterior fora do acompanhamento"
|
||||||
@@ -121,8 +128,28 @@ export function InstallmentGroupCard({
|
|||||||
<div className="flex items-center gap-3 flex-wrap">
|
<div className="flex items-center gap-3 flex-wrap">
|
||||||
<EstablishmentLogo name={group.name} size={40} />
|
<EstablishmentLogo name={group.name} size={40} />
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<CardTitle className="text-base truncate">
|
<CardTitle className="flex items-center gap-1 text-base">
|
||||||
{group.name}
|
<span className="truncate">{group.name}</span>
|
||||||
|
{hasNote ? (
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<span className="inline-flex shrink-0 rounded-full p-1 hover:bg-accent transition-colors duration-300">
|
||||||
|
<RiChat1Line
|
||||||
|
className="h-4 w-4 text-muted-foreground"
|
||||||
|
aria-hidden
|
||||||
|
/>
|
||||||
|
<span className="sr-only">Ver anotação</span>
|
||||||
|
</span>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent
|
||||||
|
side="top"
|
||||||
|
align="start"
|
||||||
|
className="max-w-xs whitespace-pre-line"
|
||||||
|
>
|
||||||
|
{group.note}
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
) : null}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<CardDescription className="flex min-w-0 items-center gap-1 text-xs">
|
<CardDescription className="flex min-w-0 items-center gap-1 text-xs">
|
||||||
{cardLogoSrc ? (
|
{cardLogoSrc ? (
|
||||||
@@ -235,8 +262,28 @@ export function InstallmentGroupCard({
|
|||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<EstablishmentLogo name={group.name} size={32} />
|
<EstablishmentLogo name={group.name} size={32} />
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<DialogTitle className="truncate text-base">
|
<DialogTitle className="flex items-center gap-1 text-base">
|
||||||
{group.name}
|
<span className="truncate">{group.name}</span>
|
||||||
|
{hasNote ? (
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger asChild>
|
||||||
|
<span className="inline-flex shrink-0 rounded-full p-1 hover:bg-accent transition-colors duration-300">
|
||||||
|
<RiChat1Line
|
||||||
|
className="h-4 w-4 text-muted-foreground"
|
||||||
|
aria-hidden
|
||||||
|
/>
|
||||||
|
<span className="sr-only">Ver anotação</span>
|
||||||
|
</span>
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent
|
||||||
|
side="top"
|
||||||
|
align="start"
|
||||||
|
className="max-w-xs whitespace-pre-line"
|
||||||
|
>
|
||||||
|
{group.note}
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
) : null}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<div className="mt-0.5 flex min-w-0 items-center gap-1.5 text-xs text-muted-foreground">
|
<div className="mt-0.5 flex min-w-0 items-center gap-1.5 text-xs text-muted-foreground">
|
||||||
{cardLogoSrc ? (
|
{cardLogoSrc ? (
|
||||||
|
|||||||
@@ -123,9 +123,14 @@ export function InvoiceListItem({ invoice, onPay }: InvoiceListItemProps) {
|
|||||||
className="font-medium"
|
className="font-medium"
|
||||||
amount={share.amount}
|
amount={share.amount}
|
||||||
/>
|
/>
|
||||||
|
{share.percentageChange !== null ? (
|
||||||
|
<span className="flex items-center gap-1 text-xs text-muted-foreground">
|
||||||
<PercentageChangeIndicator
|
<PercentageChangeIndicator
|
||||||
value={share.percentageChange}
|
value={share.percentageChange}
|
||||||
/>
|
/>
|
||||||
|
<span>vs. mês ant.</span>
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ export function CategoryTrendsWidget({
|
|||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<span className="flex shrink-0 items-center gap-1 text-xs text-muted-foreground">
|
||||||
<PercentageChangeIndicator
|
<PercentageChangeIndicator
|
||||||
value={change}
|
value={change}
|
||||||
label={formatPercentage(change, {
|
label={formatPercentage(change, {
|
||||||
@@ -89,9 +90,11 @@ export function CategoryTrendsWidget({
|
|||||||
maximumFractionDigits: 0,
|
maximumFractionDigits: 0,
|
||||||
})}
|
})}
|
||||||
positiveTrend="down"
|
positiveTrend="down"
|
||||||
className="shrink-0 text-sm font-semibold"
|
className="text-sm font-semibold"
|
||||||
iconClassName="size-3.5"
|
iconClassName="size-3.5"
|
||||||
/>
|
/>
|
||||||
|
<span>vs. mês ant.</span>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ export function MyAccountsWidget({
|
|||||||
className="flex items-center justify-between py-1.5 transition-all duration-300"
|
className="flex items-center justify-between py-1.5 transition-all duration-300"
|
||||||
>
|
>
|
||||||
<div className="flex min-w-0 flex-1 items-center gap-2 py-1">
|
<div className="flex min-w-0 flex-1 items-center gap-2 py-1">
|
||||||
<div className="relative flex size-9.5 shrink-0 items-center justify-center overflow-hidden rounded-full bg-primary/10">
|
<div className="relative flex size-9.5 shrink-0 items-center justify-center overflow-hidden rounded-full">
|
||||||
{logoSrc ? (
|
{logoSrc ? (
|
||||||
<Image
|
<Image
|
||||||
src={logoSrc}
|
src={logoSrc}
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ type InstallmentDetail = {
|
|||||||
export type InstallmentGroup = {
|
export type InstallmentGroup = {
|
||||||
seriesId: string;
|
seriesId: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
note: string | null;
|
||||||
paymentMethod: string;
|
paymentMethod: string;
|
||||||
cardId: string | null;
|
cardId: string | null;
|
||||||
cartaoName: string | null;
|
cartaoName: string | null;
|
||||||
@@ -80,6 +81,7 @@ export async function fetchInstallmentAnalysis(
|
|||||||
id: transactions.id,
|
id: transactions.id,
|
||||||
seriesId: transactions.seriesId,
|
seriesId: transactions.seriesId,
|
||||||
name: transactions.name,
|
name: transactions.name,
|
||||||
|
note: transactions.note,
|
||||||
amount: transactions.amount,
|
amount: transactions.amount,
|
||||||
paymentMethod: transactions.paymentMethod,
|
paymentMethod: transactions.paymentMethod,
|
||||||
currentInstallment: transactions.currentInstallment,
|
currentInstallment: transactions.currentInstallment,
|
||||||
@@ -150,6 +152,7 @@ export async function fetchInstallmentAnalysis(
|
|||||||
seriesMap.set(row.seriesId, {
|
seriesMap.set(row.seriesId, {
|
||||||
seriesId: row.seriesId,
|
seriesId: row.seriesId,
|
||||||
name: row.name,
|
name: row.name,
|
||||||
|
note: row.note,
|
||||||
paymentMethod: row.paymentMethod,
|
paymentMethod: row.paymentMethod,
|
||||||
cardId: row.cardId,
|
cardId: row.cardId,
|
||||||
cartaoName: row.cartaoName,
|
cartaoName: row.cartaoName,
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ export function ExpandableWidgetCard({
|
|||||||
if (!element) return;
|
if (!element) return;
|
||||||
|
|
||||||
let frameId = 0;
|
let frameId = 0;
|
||||||
|
const observedElements = new Set<Element>();
|
||||||
|
|
||||||
const checkOverflow = () => {
|
const checkOverflow = () => {
|
||||||
cancelAnimationFrame(frameId);
|
cancelAnimationFrame(frameId);
|
||||||
@@ -44,13 +45,33 @@ export function ExpandableWidgetCard({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const observeContentElements = (resizeObserver: ResizeObserver) => {
|
||||||
|
for (const child of Array.from(element.children)) {
|
||||||
|
if (!observedElements.has(child)) {
|
||||||
|
resizeObserver.observe(child);
|
||||||
|
observedElements.add(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
checkOverflow();
|
checkOverflow();
|
||||||
|
|
||||||
const resizeObserver = new ResizeObserver(checkOverflow);
|
const resizeObserver = new ResizeObserver(checkOverflow);
|
||||||
resizeObserver.observe(element);
|
resizeObserver.observe(element);
|
||||||
|
observeContentElements(resizeObserver);
|
||||||
|
|
||||||
|
const mutationObserver = new MutationObserver(() => {
|
||||||
|
observeContentElements(resizeObserver);
|
||||||
|
checkOverflow();
|
||||||
|
});
|
||||||
|
mutationObserver.observe(element, {
|
||||||
|
childList: true,
|
||||||
|
subtree: true,
|
||||||
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
cancelAnimationFrame(frameId);
|
cancelAnimationFrame(frameId);
|
||||||
|
mutationObserver.disconnect();
|
||||||
resizeObserver.disconnect();
|
resizeObserver.disconnect();
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|||||||
Reference in New Issue
Block a user