diff --git a/public/fonts/america-bold.woff2 b/public/fonts/america-bold.woff2 deleted file mode 100644 index a5cdbf5..0000000 Binary files a/public/fonts/america-bold.woff2 and /dev/null differ diff --git a/src/features/attachments/components/attachment-grid-item.tsx b/src/features/attachments/components/attachment-grid-item.tsx index a2bf193..34e65f1 100644 --- a/src/features/attachments/components/attachment-grid-item.tsx +++ b/src/features/attachments/components/attachment-grid-item.tsx @@ -117,6 +117,7 @@ export function AttachmentGridItem({ src={url} alt={attachment.fileName} fill + sizes="(max-width: 640px) 50vw, (max-width: 1024px) 33vw, (max-width: 1280px) 25vw, 20vw" unoptimized className="object-cover transition-transform duration-300 group-hover:scale-105" /> diff --git a/src/features/auth/components/auth-sidebar-invoices-mock.tsx b/src/features/auth/components/auth-sidebar-invoices-mock.tsx index 0efe957..28605b9 100644 --- a/src/features/auth/components/auth-sidebar-invoices-mock.tsx +++ b/src/features/auth/components/auth-sidebar-invoices-mock.tsx @@ -14,13 +14,13 @@ const MOCK_INVOICES: MockInvoice[] = [ cardName: "Nubank", logo: "nubank.png", amount: 1898, - dueLabel: "Vence em 3 dias", + dueLabel: "Vence hoje", }, { cardName: "Itaú", logo: "itau.png", amount: 1923, - dueLabel: "Vence em 8 dias", + dueLabel: "Vence amanhã", }, ]; diff --git a/src/shared/components/calculator/calculator-display.tsx b/src/shared/components/calculator/calculator-display.tsx index cea6096..34db5a0 100644 --- a/src/shared/components/calculator/calculator-display.tsx +++ b/src/shared/components/calculator/calculator-display.tsx @@ -2,7 +2,7 @@ import { RiCheckLine, RiFileCopyLine } from "@remixicon/react"; import { Button } from "@/shared/components/ui/button"; import { cn } from "@/shared/utils/ui"; -export type CalculatorDisplayProps = { +type CalculatorDisplayProps = { history: string | null; expression: string; resultText: string | null; diff --git a/src/shared/components/logo.tsx b/src/shared/components/logo.tsx index 083a100..d22358f 100644 --- a/src/shared/components/logo.tsx +++ b/src/shared/components/logo.tsx @@ -21,60 +21,67 @@ export function Logo({ if (variant === "compact") { return (
- OpenMonetis - OpenMonetis +
+ OpenMonetis +
+
+ OpenMonetis +
); } if (variant === "small") { return ( - OpenMonetis +
+ OpenMonetis +
); } return (
- OpenMonetis - OpenMonetis +
+ OpenMonetis +
+
+ OpenMonetis +
); } diff --git a/src/shared/components/navigation/navbar/app-navbar.tsx b/src/shared/components/navigation/navbar/app-navbar.tsx index 476c2aa..57e136b 100644 --- a/src/shared/components/navigation/navbar/app-navbar.tsx +++ b/src/shared/components/navigation/navbar/app-navbar.tsx @@ -2,6 +2,7 @@ import { AnimatedThemeToggler } from "@/shared/components/animated-theme-toggler import { NotificationBell } from "@/shared/components/navigation/navbar/notification-bell"; import { RefreshPageButton } from "@/shared/components/refresh-page-button"; import type { DashboardNotificationsSnapshot } from "@/shared/lib/types/notifications"; +import { checkForUpdate } from "@/shared/lib/version/check-update"; import { NavMenu } from "./nav-menu"; import { NavbarShell } from "./navbar-shell"; import { NavbarUser } from "./navbar-user"; @@ -18,12 +19,14 @@ type AppNavbarProps = { notificationsSnapshot: DashboardNotificationsSnapshot; }; -export function AppNavbar({ +export async function AppNavbar({ user, pagadorAvatarUrl, preLancamentosCount = 0, notificationsSnapshot, }: AppNavbarProps) { + const updateCheck = await checkForUpdate(); + return ( @@ -38,7 +41,11 @@ export function AppNavbar({ - + ); } diff --git a/src/shared/components/navigation/navbar/navbar-user.tsx b/src/shared/components/navigation/navbar/navbar-user.tsx index 7b524e4..d6f81b3 100644 --- a/src/shared/components/navigation/navbar/navbar-user.tsx +++ b/src/shared/components/navigation/navbar/navbar-user.tsx @@ -3,6 +3,7 @@ import { RiHistoryLine, RiLogoutCircleLine, + RiMegaphoneLine, RiMessageLine, RiSettings2Line, } from "@remixicon/react"; @@ -24,6 +25,7 @@ import { import { Spinner } from "@/shared/components/ui/spinner"; import { authClient } from "@/shared/lib/auth/client"; import { getAvatarSrc } from "@/shared/lib/payers/utils"; +import type { UpdateCheckResult } from "@/shared/lib/version/check-update"; import { cn } from "@/shared/utils/ui"; const itemClass = @@ -37,9 +39,14 @@ type NavbarUserProps = { image: string | null; }; pagadorAvatarUrl: string | null; + updateCheck: UpdateCheckResult; }; -export function NavbarUser({ user, pagadorAvatarUrl }: NavbarUserProps) { +export function NavbarUser({ + user, + pagadorAvatarUrl, + updateCheck, +}: NavbarUserProps) { const router = useRouter(); const [logoutLoading, setLogoutLoading] = useState(false); const [feedbackOpen, setFeedbackOpen] = useState(false); @@ -61,33 +68,42 @@ export function NavbarUser({ user, pagadorAvatarUrl }: NavbarUserProps) { return ( - - - +
+ + + + {updateCheck.hasUpdate && ( + + )} +
- {user.name} +
+ {user.name} +
{user.name} @@ -122,6 +138,20 @@ export function NavbarUser({ user, pagadorAvatarUrl }: NavbarUserProps) { Enviar Feedback + + {updateCheck.hasUpdate && ( + + + + Atualização {updateCheck.latestVersion} disponível + + + )}
diff --git a/src/shared/components/navigation/sidebar/nav-user.tsx b/src/shared/components/navigation/sidebar/nav-user.tsx index b6ea956..8ce0066 100644 --- a/src/shared/components/navigation/sidebar/nav-user.tsx +++ b/src/shared/components/navigation/sidebar/nav-user.tsx @@ -30,13 +30,15 @@ export function NavUser({ user, pagadorAvatarUrl }: NavUserProps) { size="lg" className="data-popup-open:bg-sidebar-accent data-popup-open:text-sidebar-accent-foreground " > - {user.name} +
+ {user.name} +
{user.name} diff --git a/src/shared/components/ui/chart.tsx b/src/shared/components/ui/chart.tsx index b760c4c..b64c972 100644 --- a/src/shared/components/ui/chart.tsx +++ b/src/shared/components/ui/chart.tsx @@ -31,6 +31,13 @@ type ChartContextProps = { const ChartContext = React.createContext(null); +const CHART_MIN_WIDTH = 280; +const CHART_MIN_HEIGHT = 200; +const CHART_INITIAL_DIMENSION = { + width: CHART_MIN_WIDTH, + height: CHART_MIN_HEIGHT, +} as const; + function useChart() { const context = React.useContext(ChartContext); @@ -80,8 +87,9 @@ function ChartContainer({ {children} diff --git a/src/shared/components/ui/currency-input.tsx b/src/shared/components/ui/currency-input.tsx index cc4f105..6ebc360 100644 --- a/src/shared/components/ui/currency-input.tsx +++ b/src/shared/components/ui/currency-input.tsx @@ -56,7 +56,7 @@ const formatDigits = (digits: string) => { return BRL_FORMATTER.format(numeric); }; -export interface CurrencyInputProps +interface CurrencyInputProps extends Omit< React.ComponentProps, "value" | "defaultValue" | "type" | "inputMode" | "onChange" diff --git a/src/shared/lib/version/check-update.ts b/src/shared/lib/version/check-update.ts new file mode 100644 index 0000000..4853745 --- /dev/null +++ b/src/shared/lib/version/check-update.ts @@ -0,0 +1,55 @@ +import { version as currentVersion } from "@/package.json"; + +const GITHUB_REPO = "felipegcoutinho/openmonetis"; +const RELEASES_URL = `https://github.com/${GITHUB_REPO}/releases`; + +export type UpdateCheckResult = { + hasUpdate: boolean; + latestVersion: string; + releaseUrl: string; +}; + +function compareVersions(a: string, b: string): number { + const normalize = (v: string) => v.replace(/^v/, "").split(".").map(Number); + const partsA = normalize(a); + const partsB = normalize(b); + + for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) { + const diff = (partsA[i] ?? 0) - (partsB[i] ?? 0); + if (diff !== 0) return diff; + } + return 0; +} + +export async function checkForUpdate(): Promise { + const fallback: UpdateCheckResult = { + hasUpdate: false, + latestVersion: currentVersion, + releaseUrl: RELEASES_URL, + }; + + try { + // GitHub redireciona /releases/latest para a URL com a tag — sem API, sem rate limit + const response = await fetch( + `https://github.com/${GITHUB_REPO}/releases/latest`, + { redirect: "manual", next: { revalidate: 86400 } }, + ); + + const location = response.headers.get("location"); + if (!location) return fallback; + + const match = location.match(/releases\/tag\/v?(.+)$/); + if (!match) return fallback; + + const latestVersion = match[1]; + const releaseUrl = `https://github.com/${GITHUB_REPO}/releases/tag/v${latestVersion}`; + + return { + hasUpdate: compareVersions(latestVersion, currentVersion) > 0, + latestVersion, + releaseUrl, + }; + } catch { + return fallback; + } +}