Simplifica tipografia para fonte America

This commit is contained in:
Felipe Coutinho
2026-03-13 18:21:01 +00:00
parent 20c14aa96f
commit fa9bf17663
21 changed files with 34 additions and 469 deletions

View File

@@ -14,6 +14,7 @@ e este projeto adere ao [Versionamento Semântico](https://semver.org/lang/pt-BR
### Alterado ### Alterado
- Tipografia: a aplicação passou a carregar apenas a família `America` (`regular`, `medium` e `bold`) como fonte global, removendo a personalização por usuário e a distinção de fonte para valores monetários.
- Queries e organização de domínios: o dashboard passou a nomear queries de leitura com sufixo `-queries.ts` e `preferences-queries.ts`, relatórios adotaram nomes explícitos como `cards-report-queries.ts`, `category-report-queries.ts`, `category-chart-queries.ts` e `establishments/queries.ts`, `insights/queries.ts` foi renomeado para `constants.ts` e as leituras reutilizáveis de lançamentos foram concentradas em `transactions/queries.ts`, deixando `page-helpers.ts` focado em parsing, filtros e transformação. - Queries e organização de domínios: o dashboard passou a nomear queries de leitura com sufixo `-queries.ts` e `preferences-queries.ts`, relatórios adotaram nomes explícitos como `cards-report-queries.ts`, `category-report-queries.ts`, `category-chart-queries.ts` e `establishments/queries.ts`, `insights/queries.ts` foi renomeado para `constants.ts` e as leituras reutilizáveis de lançamentos foram concentradas em `transactions/queries.ts`, deixando `page-helpers.ts` focado em parsing, filtros e transformação.
## [2.0.0] - 2026-03-09 ## [2.0.0] - 2026-03-09

View File

@@ -78,7 +78,7 @@ A ideia é simples: ter um lugar onde consigo ver todas as minhas contas, cartõ
📲 **OpenMonetis Companion** — App Android que captura notificações bancárias (Nubank, Itaú, Bradesco, Inter, C6 e outros) e envia como pré-lançamentos para revisão. [Repositório](https://github.com/felipegcoutinho/openmonetis-companion). 📲 **OpenMonetis Companion** — App Android que captura notificações bancárias (Nubank, Itaú, Bradesco, Inter, C6 e outros) e envia como pré-lançamentos para revisão. [Repositório](https://github.com/felipegcoutinho/openmonetis-companion).
⚙️ **Personalização** — Tema dark/light, modo privacidade, fontes customizáveis, preferências por usuário. ⚙️ **Personalização** — Tema dark/light, modo privacidade e preferências por usuário.
### Stack técnica ### Stack técnica

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,74 +1,6 @@
import {
Fira_Code,
Fira_Sans,
Geist,
IBM_Plex_Mono,
Inter,
JetBrains_Mono,
Reddit_Sans,
Roboto,
Ubuntu,
} from "next/font/google";
import localFont from "next/font/local"; import localFont from "next/font/local";
const ai_sans = localFont({ export const america = localFont({
src: [
{
path: "./ai-sans-regular.woff2",
weight: "400",
style: "normal",
},
{
path: "./ai-sans-semibold.woff2",
weight: "700",
style: "normal",
},
],
display: "swap",
variable: "--font-ai-sans",
});
const aeonik = localFont({
src: [
{
path: "./aeonikfono-regular.woff2",
weight: "400",
style: "normal",
},
{
path: "./aeonikfono-medium.woff2",
weight: "600",
style: "normal",
},
],
display: "swap",
variable: "--font-aeonik",
});
const itau = localFont({
src: [
{
path: "./itau-text-regular.woff2",
weight: "400",
style: "normal",
},
{
path: "./itau-text-bold.woff2",
weight: "700",
style: "normal",
},
],
display: "swap",
variable: "--font-itau",
});
const anthropic_sans = localFont({
src: "./anthropic-sans.woff2",
display: "swap",
variable: "--font-anthropic-sans",
});
const america = localFont({
src: [ src: [
{ {
path: "./america-regular.woff2", path: "./america-regular.woff2",
@@ -77,12 +9,12 @@ const america = localFont({
}, },
{ {
path: "./america-medium.woff2", path: "./america-medium.woff2",
weight: "700", weight: "500",
style: "normal", style: "normal",
}, },
{ {
path: "./america-bold.woff2", path: "./america-bold.woff2",
weight: "900", weight: "700",
style: "normal", style: "normal",
}, },
], ],
@@ -90,144 +22,4 @@ const america = localFont({
variable: "--font-america", variable: "--font-america",
}); });
const inter = Inter({ export const americaFontVariable = america.variable;
subsets: ["latin"],
display: "swap",
variable: "--font-inter",
});
const geist_sans = Geist({
subsets: ["latin"],
display: "swap",
variable: "--font-geist",
});
const roboto = Roboto({
subsets: ["latin"],
display: "swap",
variable: "--font-roboto",
});
const reddit_sans = Reddit_Sans({
subsets: ["latin"],
display: "swap",
variable: "--font-reddit-sans",
});
const fira_sans = Fira_Sans({
weight: ["400", "500", "600", "700"],
subsets: ["latin"],
display: "swap",
variable: "--font-fira-sans",
});
const ubuntu = Ubuntu({
weight: ["400"],
subsets: ["latin"],
display: "swap",
variable: "--font-ubuntu",
});
const jetbrains_mono = JetBrains_Mono({
subsets: ["latin"],
display: "swap",
variable: "--font-jetbrains-mono",
});
const fira_code = Fira_Code({
subsets: ["latin"],
display: "swap",
variable: "--font-fira-code",
});
const ibm_plex_mono = IBM_Plex_Mono({
weight: ["400", "500", "600"],
subsets: ["latin"],
display: "swap",
variable: "--font-ibm-plex-mono",
});
export const DEFAULT_FONT_KEY = "ai-sans";
export const FONT_OPTIONS = [
{ key: "ai-sans", label: "Open AI Sans", variable: "var(--font-ai-sans)" },
{ key: "america", label: "America", variable: "var(--font-america)" },
{ key: "aeonik", label: "Aeonik", variable: "var(--font-aeonik)" },
{
key: "anthropic-sans",
label: "Anthropic Sans",
variable: "var(--font-anthropic-sans)",
},
{ key: "fira-code", label: "Fira Code", variable: "var(--font-fira-code)" },
{
key: "fira-sans",
label: "Fira Sans",
variable: "var(--font-fira-sans)",
},
{
key: "itau",
label: "Itaú Sans",
variable: "var(--font-itau)",
},
{ key: "geist", label: "Geist Sans", variable: "var(--font-geist)" },
{
key: "ibm-plex-mono",
label: "IBM Plex Mono",
variable: "var(--font-ibm-plex-mono)",
},
{ key: "inter", label: "Inter", variable: "var(--font-inter)" },
{
key: "jetbrains-mono",
label: "JetBrains Mono",
variable: "var(--font-jetbrains-mono)",
},
{
key: "reddit-sans",
label: "Reddit Sans",
variable: "var(--font-reddit-sans)",
},
{ key: "roboto", label: "Roboto", variable: "var(--font-roboto)" },
{ key: "ubuntu", label: "Ubuntu", variable: "var(--font-ubuntu)" },
] as const;
export type FontKey = (typeof FONT_OPTIONS)[number]["key"];
export const FONT_KEYS = FONT_OPTIONS.map((option) => option.key) as [
FontKey,
...FontKey[],
];
const VALID_FONT_KEY_SET = new Set<string>(FONT_KEYS);
const allFonts = [
ai_sans,
anthropic_sans,
inter,
geist_sans,
roboto,
reddit_sans,
fira_sans,
ubuntu,
jetbrains_mono,
fira_code,
ibm_plex_mono,
itau,
america,
aeonik,
];
export const allFontVariables = allFonts.map((f) => f.variable).join(" ");
function isValidFontKey(value: string): value is FontKey {
return VALID_FONT_KEY_SET.has(value);
}
export function normalizeFontKey(value: string | null | undefined): FontKey {
if (!value) return DEFAULT_FONT_KEY;
return isValidFontKey(value) ? value : DEFAULT_FONT_KEY;
}
export function getFontVariable(key: string): string {
const option = FONT_OPTIONS.find((o) => o.key === key);
return option?.variable ?? `var(--font-${DEFAULT_FONT_KEY})`;
}

Binary file not shown.

Binary file not shown.

View File

@@ -1,12 +1,10 @@
import { fetchDashboardNotifications } from "@/features/dashboard/notifications-queries"; import { fetchDashboardNotifications } from "@/features/dashboard/notifications-queries";
import { fetchPendingInboxCount } from "@/features/inbox/queries"; import { fetchPendingInboxCount } from "@/features/inbox/queries";
import { AppNavbar } from "@/shared/components/navigation/navbar/app-navbar"; import { AppNavbar } from "@/shared/components/navigation/navbar/app-navbar";
import { FontProvider } from "@/shared/components/providers/font-provider";
import { PrivacyProvider } from "@/shared/components/providers/privacy-provider"; import { PrivacyProvider } from "@/shared/components/providers/privacy-provider";
import { getUserSession } from "@/shared/lib/auth/server"; import { getUserSession } from "@/shared/lib/auth/server";
import { fetchPagadoresWithAccess } from "@/shared/lib/payers/access"; import { fetchPagadoresWithAccess } from "@/shared/lib/payers/access";
import { PAGADOR_ROLE_ADMIN } from "@/shared/lib/payers/constants"; import { PAGADOR_ROLE_ADMIN } from "@/shared/lib/payers/constants";
import { fetchUserFontPreferences } from "@/shared/lib/preferences/fonts";
import { parsePeriodParam } from "@/shared/utils/period"; import { parsePeriodParam } from "@/shared/utils/period";
export default async function DashboardLayout({ export default async function DashboardLayout({
@@ -36,35 +34,27 @@ export default async function DashboardLayout({
const { period: currentPeriod } = parsePeriodParam( const { period: currentPeriod } = parsePeriodParam(
singlePeriodoParam ?? null, singlePeriodoParam ?? null,
); );
// Buscar notificações, contagem de pré-lançamentos e preferências de fonte em paralelo const [notificationsSnapshot, preLancamentosCount] = await Promise.all([
const [notificationsSnapshot, preLancamentosCount, fontPrefs] = fetchDashboardNotifications(session.user.id, currentPeriod),
await Promise.all([ fetchPendingInboxCount(session.user.id),
fetchDashboardNotifications(session.user.id, currentPeriod), ]);
fetchPendingInboxCount(session.user.id),
fetchUserFontPreferences(session.user.id),
]);
return ( return (
<FontProvider <PrivacyProvider>
systemFont={fontPrefs.systemFont} <AppNavbar
moneyFont={fontPrefs.moneyFont} user={{ ...session.user, image: session.user.image ?? null }}
> pagadorAvatarUrl={adminPagador?.avatarUrl ?? null}
<PrivacyProvider> preLancamentosCount={preLancamentosCount}
<AppNavbar notificationsSnapshot={notificationsSnapshot}
user={{ ...session.user, image: session.user.image ?? null }} />
pagadorAvatarUrl={adminPagador?.avatarUrl ?? null} <div className="relative flex flex-1 flex-col pt-16">
preLancamentosCount={preLancamentosCount} <div className="pointer-events-none absolute inset-0" />
notificationsSnapshot={notificationsSnapshot} <div className="@container/main flex flex-1 flex-col gap-2">
/> <div className="flex flex-col gap-4 py-5 md:gap-6 w-full max-w-8xl mx-auto px-4 ">
<div className="relative flex flex-1 flex-col pt-16"> {children}
<div className="pointer-events-none absolute inset-0" />
<div className="@container/main flex flex-1 flex-col gap-2">
<div className="flex flex-col gap-4 py-5 md:gap-6 w-full max-w-8xl mx-auto px-4 ">
{children}
</div>
</div> </div>
</div> </div>
</PrivacyProvider> </div>
</FontProvider> </PrivacyProvider>
); );
} }

View File

@@ -10,7 +10,6 @@ import { UpdateEmailForm } from "@/features/settings/components/update-email-for
import { UpdateNameForm } from "@/features/settings/components/update-name-form"; import { UpdateNameForm } from "@/features/settings/components/update-name-form";
import { UpdatePasswordForm } from "@/features/settings/components/update-password-form"; import { UpdatePasswordForm } from "@/features/settings/components/update-password-form";
import { fetchAjustesPageData } from "@/features/settings/queries"; import { fetchAjustesPageData } from "@/features/settings/queries";
import { DEFAULT_FONT_KEY } from "@/public/fonts/font_index";
import { Card } from "@/shared/components/ui/card"; import { Card } from "@/shared/components/ui/card";
import { import {
Tabs, Tabs,
@@ -78,8 +77,6 @@ export default async function Page() {
lancamentosColumnOrder={ lancamentosColumnOrder={
userPreferences?.lancamentosColumnOrder ?? null userPreferences?.lancamentosColumnOrder ?? null
} }
systemFont={userPreferences?.systemFont ?? DEFAULT_FONT_KEY}
moneyFont={userPreferences?.moneyFont ?? DEFAULT_FONT_KEY}
/> />
</div> </div>
</Card> </Card>

View File

@@ -9,10 +9,6 @@
} }
:root { :root {
/* Font customization */
--font-app: var(--font-ai-sans);
--font-money: var(--font-ai-sans);
/* Base surfaces - warm cream with subtle orange undertone */ /* Base surfaces - warm cream with subtle orange undertone */
--background: oklch(98.01% 0.00331 67.026); --background: oklch(98.01% 0.00331 67.026);
--foreground: #201207; --foreground: #201207;
@@ -186,8 +182,7 @@
} }
@theme inline { @theme inline {
--default-font-family: var(--font-app); --default-font-family: var(--font-america);
--default-mono-font-family: var(--font-money);
--color-background: var(--background); --color-background: var(--background);
--color-foreground: var(--foreground); --color-foreground: var(--foreground);
--color-card: var(--card); --color-card: var(--card);
@@ -259,6 +254,7 @@
body { body {
@apply bg-background text-foreground; @apply bg-background text-foreground;
font-family: var(--font-america), sans-serif;
} }
*::selection { *::selection {

View File

@@ -1,10 +1,10 @@
import { Analytics } from "@vercel/analytics/next"; import { Analytics } from "@vercel/analytics/next";
import { SpeedInsights } from "@vercel/speed-insights/next"; import { SpeedInsights } from "@vercel/speed-insights/next";
import type { Metadata } from "next"; import type { Metadata } from "next";
import { allFontVariables } from "@/public/fonts/font_index";
import { ThemeProvider } from "@/shared/components/providers/theme-provider"; import { ThemeProvider } from "@/shared/components/providers/theme-provider";
import { Toaster } from "@/shared/components/ui/sonner"; import { Toaster } from "@/shared/components/ui/sonner";
import "./globals.css"; import "./globals.css";
import { america } from "@/public/fonts/font_index";
export const metadata: Metadata = { export const metadata: Metadata = {
title: { title: {
@@ -21,7 +21,11 @@ export default function RootLayout({
children: React.ReactNode; children: React.ReactNode;
}>) { }>) {
return ( return (
<html lang="pt-BR" className={allFontVariables} suppressHydrationWarning> <html
lang="pt-BR"
className={`${america.variable} ${america.className}`}
suppressHydrationWarning
>
<head> <head>
<meta name="apple-mobile-web-app-title" content="OpenMonetis" /> <meta name="apple-mobile-web-app-title" content="OpenMonetis" />
</head> </head>

View File

@@ -7,7 +7,6 @@ import { revalidatePath } from "next/cache";
import { headers } from "next/headers"; import { headers } from "next/headers";
import { z } from "zod"; import { z } from "zod";
import { account, pagadores, tokensApi } from "@/db/schema"; import { account, pagadores, tokensApi } from "@/db/schema";
import { DEFAULT_FONT_KEY, FONT_KEYS } from "@/public/fonts/font_index";
import { auth } from "@/shared/lib/auth/config"; import { auth } from "@/shared/lib/auth/config";
import { db, schema } from "@/shared/lib/db"; import { db, schema } from "@/shared/lib/db";
import { PAGADOR_ROLE_ADMIN } from "@/shared/lib/payers/constants"; import { PAGADOR_ROLE_ADMIN } from "@/shared/lib/payers/constants";
@@ -56,8 +55,6 @@ const deleteAccountSchema = z.object({
const updatePreferencesSchema = z.object({ const updatePreferencesSchema = z.object({
extratoNoteAsColumn: z.boolean(), extratoNoteAsColumn: z.boolean(),
lancamentosColumnOrder: z.array(z.string()).nullable(), lancamentosColumnOrder: z.array(z.string()).nullable(),
systemFont: z.enum(FONT_KEYS).default(DEFAULT_FONT_KEY),
moneyFont: z.enum(FONT_KEYS).default(DEFAULT_FONT_KEY),
}); });
// Actions // Actions
@@ -404,8 +401,6 @@ export async function updatePreferencesAction(
.set({ .set({
extratoNoteAsColumn: validated.extratoNoteAsColumn, extratoNoteAsColumn: validated.extratoNoteAsColumn,
lancamentosColumnOrder: validated.lancamentosColumnOrder, lancamentosColumnOrder: validated.lancamentosColumnOrder,
systemFont: validated.systemFont,
moneyFont: validated.moneyFont,
updatedAt: new Date(), updatedAt: new Date(),
}) })
.where(eq(schema.preferenciasUsuario.userId, session.user.id)); .where(eq(schema.preferenciasUsuario.userId, session.user.id));
@@ -415,8 +410,6 @@ export async function updatePreferencesAction(
userId: session.user.id, userId: session.user.id,
extratoNoteAsColumn: validated.extratoNoteAsColumn, extratoNoteAsColumn: validated.extratoNoteAsColumn,
lancamentosColumnOrder: validated.lancamentosColumnOrder, lancamentosColumnOrder: validated.lancamentosColumnOrder,
systemFont: validated.systemFont,
moneyFont: validated.moneyFont,
}); });
} }

View File

@@ -18,31 +18,20 @@ import {
import { CSS } from "@dnd-kit/utilities"; import { CSS } from "@dnd-kit/utilities";
import { RiDragMove2Line } from "@remixicon/react"; import { RiDragMove2Line } from "@remixicon/react";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { useEffect, useState, useTransition } from "react"; import { useState, useTransition } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import { updatePreferencesAction } from "@/features/settings/actions"; import { updatePreferencesAction } from "@/features/settings/actions";
import { import {
DEFAULT_LANCAMENTOS_COLUMN_ORDER, DEFAULT_LANCAMENTOS_COLUMN_ORDER,
LANCAMENTOS_COLUMN_LABELS, LANCAMENTOS_COLUMN_LABELS,
} from "@/features/transactions/column-order"; } from "@/features/transactions/column-order";
import { FONT_OPTIONS } from "@/public/fonts/font_index";
import { useFont } from "@/shared/components/providers/font-provider";
import { Button } from "@/shared/components/ui/button"; import { Button } from "@/shared/components/ui/button";
import { Label } from "@/shared/components/ui/label"; import { Label } from "@/shared/components/ui/label";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/shared/components/ui/select";
import { Switch } from "@/shared/components/ui/switch"; import { Switch } from "@/shared/components/ui/switch";
interface PreferencesFormProps { interface PreferencesFormProps {
extratoNoteAsColumn: boolean; extratoNoteAsColumn: boolean;
lancamentosColumnOrder: string[] | null; lancamentosColumnOrder: string[] | null;
systemFont: string;
moneyFont: string;
} }
function SortableColumnItem({ id }: { id: string }) { function SortableColumnItem({ id }: { id: string }) {
@@ -85,8 +74,6 @@ function SortableColumnItem({ id }: { id: string }) {
export function PreferencesForm({ export function PreferencesForm({
extratoNoteAsColumn: initialExtratoNoteAsColumn, extratoNoteAsColumn: initialExtratoNoteAsColumn,
lancamentosColumnOrder: initialColumnOrder, lancamentosColumnOrder: initialColumnOrder,
systemFont: initialSystemFont,
moneyFont: initialMoneyFont,
}: PreferencesFormProps) { }: PreferencesFormProps) {
const router = useRouter(); const router = useRouter();
const [isPending, startTransition] = useTransition(); const [isPending, startTransition] = useTransition();
@@ -98,9 +85,6 @@ export function PreferencesForm({
? initialColumnOrder ? initialColumnOrder
: DEFAULT_LANCAMENTOS_COLUMN_ORDER, : DEFAULT_LANCAMENTOS_COLUMN_ORDER,
); );
const [selectedSystemFont, setSelectedSystemFont] =
useState(initialSystemFont);
const [selectedMoneyFont, setSelectedMoneyFont] = useState(initialMoneyFont);
const sensors = useSensors( const sensors = useSensors(
useSensor(PointerSensor, { activationConstraint: { distance: 8 } }), useSensor(PointerSensor, { activationConstraint: { distance: 8 } }),
@@ -118,17 +102,6 @@ export function PreferencesForm({
} }
}; };
const fontCtx = useFont();
// Live preview: update CSS vars when font selection changes
useEffect(() => {
fontCtx.setSystemFont(selectedSystemFont);
}, [selectedSystemFont, fontCtx.setSystemFont]);
useEffect(() => {
fontCtx.setMoneyFont(selectedMoneyFont);
}, [selectedMoneyFont, fontCtx.setMoneyFont]);
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => { const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault(); event.preventDefault();
@@ -136,8 +109,6 @@ export function PreferencesForm({
const result = await updatePreferencesAction({ const result = await updatePreferencesAction({
extratoNoteAsColumn, extratoNoteAsColumn,
lancamentosColumnOrder: columnOrder, lancamentosColumnOrder: columnOrder,
systemFont: selectedSystemFont,
moneyFont: selectedMoneyFont,
}); });
if (result.success) { if (result.success) {
@@ -151,70 +122,6 @@ export function PreferencesForm({
return ( return (
<form onSubmit={handleSubmit} className="flex flex-col gap-8"> <form onSubmit={handleSubmit} className="flex flex-col gap-8">
{/* Seção 1: Tipografia */}
<section className="space-y-5">
<div>
<h3 className="text-base font-semibold">Tipografia</h3>
<p className="text-sm text-muted-foreground">
Personalize as fontes usadas na interface e nos valores monetários.
</p>
</div>
{/* Fonte do sistema */}
<div className="space-y-2 max-w-md">
<Label htmlFor="system-font">Fonte do sistema</Label>
<Select
value={selectedSystemFont}
onValueChange={setSelectedSystemFont}
>
<SelectTrigger id="system-font">
<SelectValue />
</SelectTrigger>
<SelectContent>
{FONT_OPTIONS.map((opt) => (
<SelectItem key={opt.key} value={opt.key}>
<span
style={{
fontFamily: opt.variable,
}}
>
{opt.label}
</span>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{/* Fonte de valores */}
<div className="space-y-2 max-w-md">
<Label htmlFor="money-font">Fonte de valores</Label>
<Select
value={selectedMoneyFont}
onValueChange={setSelectedMoneyFont}
>
<SelectTrigger id="money-font">
<SelectValue />
</SelectTrigger>
<SelectContent>
{FONT_OPTIONS.map((opt) => (
<SelectItem key={opt.key} value={opt.key}>
<span
style={{
fontFamily: opt.variable,
}}
>
{opt.label}
</span>
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</section>
<div className="border-b" />
{/* Seção: Extrato / Lançamentos */} {/* Seção: Extrato / Lançamentos */}
<section className="space-y-4"> <section className="space-y-4">
<div> <div>

View File

@@ -1,13 +1,10 @@
import { desc, eq } from "drizzle-orm"; import { desc, eq } from "drizzle-orm";
import { tokensApi } from "@/db/schema"; import { tokensApi } from "@/db/schema";
import { type FontKey, normalizeFontKey } from "@/public/fonts/font_index";
import { db, schema } from "@/shared/lib/db"; import { db, schema } from "@/shared/lib/db";
export interface UserPreferences { export interface UserPreferences {
extratoNoteAsColumn: boolean; extratoNoteAsColumn: boolean;
lancamentosColumnOrder: string[] | null; lancamentosColumnOrder: string[] | null;
systemFont: FontKey;
moneyFont: FontKey;
} }
export interface ApiToken { export interface ApiToken {
@@ -35,8 +32,6 @@ export async function fetchUserPreferences(
.select({ .select({
extratoNoteAsColumn: schema.preferenciasUsuario.extratoNoteAsColumn, extratoNoteAsColumn: schema.preferenciasUsuario.extratoNoteAsColumn,
lancamentosColumnOrder: schema.preferenciasUsuario.lancamentosColumnOrder, lancamentosColumnOrder: schema.preferenciasUsuario.lancamentosColumnOrder,
systemFont: schema.preferenciasUsuario.systemFont,
moneyFont: schema.preferenciasUsuario.moneyFont,
}) })
.from(schema.preferenciasUsuario) .from(schema.preferenciasUsuario)
.where(eq(schema.preferenciasUsuario.userId, userId)) .where(eq(schema.preferenciasUsuario.userId, userId))
@@ -44,11 +39,7 @@ export async function fetchUserPreferences(
if (!result[0]) return null; if (!result[0]) return null;
return { return result[0];
...result[0],
systemFont: normalizeFontKey(result[0].systemFont),
moneyFont: normalizeFontKey(result[0].moneyFont),
};
} }
export async function fetchApiTokens(userId: string): Promise<ApiToken[]> { export async function fetchApiTokens(userId: string): Promise<ApiToken[]> {

View File

@@ -19,7 +19,6 @@ function MoneyValues({ amount, className, showPositiveSign = false }: Props) {
return ( return (
<span <span
style={{ fontFamily: "var(--font-money)" }}
className={cn( className={cn(
"inline-flex items-baseline transition-all duration-200 tracking-tighter", "inline-flex items-baseline transition-all duration-200 tracking-tighter",
privacyMode && privacyMode &&

View File

@@ -1,66 +0,0 @@
"use client";
import { createContext, useContext, useEffect, useMemo, useState } from "react";
import { getFontVariable } from "@/public/fonts/font_index";
type FontContextValue = {
systemFont: string;
moneyFont: string;
setSystemFont: (key: string) => void;
setMoneyFont: (key: string) => void;
};
const FontContext = createContext<FontContextValue | null>(null);
export function FontProvider({
systemFont: initialSystemFont,
moneyFont: initialMoneyFont,
children,
}: {
systemFont: string;
moneyFont: string;
children: React.ReactNode;
}) {
const [systemFont, setSystemFontState] = useState(initialSystemFont);
const [moneyFont, setMoneyFontState] = useState(initialMoneyFont);
useEffect(() => {
document.documentElement.style.setProperty(
"--font-app",
getFontVariable(systemFont),
);
document.documentElement.style.setProperty(
"--font-money",
getFontVariable(moneyFont),
);
}, [systemFont, moneyFont]);
const value = useMemo(
() => ({
systemFont,
moneyFont,
setSystemFont: setSystemFontState,
setMoneyFont: setMoneyFontState,
}),
[systemFont, moneyFont],
);
return (
<>
<style
dangerouslySetInnerHTML={{
__html: `:root { --font-app: ${getFontVariable(initialSystemFont)}; --font-money: ${getFontVariable(initialMoneyFont)}; }`,
}}
/>
<FontContext value={value}>{children}</FontContext>
</>
);
}
export function useFont() {
const ctx = useContext(FontContext);
if (!ctx) {
throw new Error("useFont must be used within FontProvider");
}
return ctx;
}

View File

@@ -1,3 +1,2 @@
export { FontProvider } from "./font-provider";
export { PrivacyProvider } from "./privacy-provider"; export { PrivacyProvider } from "./privacy-provider";
export { ThemeProvider } from "./theme-provider"; export { ThemeProvider } from "./theme-provider";

View File

@@ -1,38 +0,0 @@
import { eq } from "drizzle-orm";
import { cache } from "react";
import {
DEFAULT_FONT_KEY,
type FontKey,
normalizeFontKey,
} from "@/public/fonts/font_index";
import { db, schema } from "@/shared/lib/db";
export type FontPreferences = {
systemFont: FontKey;
moneyFont: FontKey;
};
const DEFAULT_FONT_PREFS: FontPreferences = {
systemFont: DEFAULT_FONT_KEY,
moneyFont: DEFAULT_FONT_KEY,
};
export const fetchUserFontPreferences = cache(
async (userId: string): Promise<FontPreferences> => {
const result = await db
.select({
systemFont: schema.preferenciasUsuario.systemFont,
moneyFont: schema.preferenciasUsuario.moneyFont,
})
.from(schema.preferenciasUsuario)
.where(eq(schema.preferenciasUsuario.userId, userId))
.limit(1);
if (!result[0]) return DEFAULT_FONT_PREFS;
return {
systemFont: normalizeFontKey(result[0].systemFont),
moneyFont: normalizeFontKey(result[0].moneyFont),
};
},
);