mirror of
https://github.com/felipegcoutinho/openmonetis.git
synced 2026-05-09 02:51:46 +00:00
Simplifica tipografia para fonte America
This commit is contained in:
@@ -14,6 +14,7 @@ e este projeto adere ao [Versionamento Semântico](https://semver.org/lang/pt-BR
|
||||
|
||||
### 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.
|
||||
|
||||
## [2.0.0] - 2026-03-09
|
||||
|
||||
@@ -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).
|
||||
|
||||
⚙️ **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
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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";
|
||||
|
||||
const ai_sans = 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({
|
||||
export const america = localFont({
|
||||
src: [
|
||||
{
|
||||
path: "./america-regular.woff2",
|
||||
@@ -77,12 +9,12 @@ const america = localFont({
|
||||
},
|
||||
{
|
||||
path: "./america-medium.woff2",
|
||||
weight: "700",
|
||||
weight: "500",
|
||||
style: "normal",
|
||||
},
|
||||
{
|
||||
path: "./america-bold.woff2",
|
||||
weight: "900",
|
||||
weight: "700",
|
||||
style: "normal",
|
||||
},
|
||||
],
|
||||
@@ -90,144 +22,4 @@ const america = localFont({
|
||||
variable: "--font-america",
|
||||
});
|
||||
|
||||
const inter = Inter({
|
||||
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})`;
|
||||
}
|
||||
export const americaFontVariable = america.variable;
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,12 +1,10 @@
|
||||
import { fetchDashboardNotifications } from "@/features/dashboard/notifications-queries";
|
||||
import { fetchPendingInboxCount } from "@/features/inbox/queries";
|
||||
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 { getUserSession } from "@/shared/lib/auth/server";
|
||||
import { fetchPagadoresWithAccess } from "@/shared/lib/payers/access";
|
||||
import { PAGADOR_ROLE_ADMIN } from "@/shared/lib/payers/constants";
|
||||
import { fetchUserFontPreferences } from "@/shared/lib/preferences/fonts";
|
||||
import { parsePeriodParam } from "@/shared/utils/period";
|
||||
|
||||
export default async function DashboardLayout({
|
||||
@@ -36,35 +34,27 @@ export default async function DashboardLayout({
|
||||
const { period: currentPeriod } = parsePeriodParam(
|
||||
singlePeriodoParam ?? null,
|
||||
);
|
||||
// Buscar notificações, contagem de pré-lançamentos e preferências de fonte em paralelo
|
||||
const [notificationsSnapshot, preLancamentosCount, fontPrefs] =
|
||||
await Promise.all([
|
||||
fetchDashboardNotifications(session.user.id, currentPeriod),
|
||||
fetchPendingInboxCount(session.user.id),
|
||||
fetchUserFontPreferences(session.user.id),
|
||||
]);
|
||||
const [notificationsSnapshot, preLancamentosCount] = await Promise.all([
|
||||
fetchDashboardNotifications(session.user.id, currentPeriod),
|
||||
fetchPendingInboxCount(session.user.id),
|
||||
]);
|
||||
|
||||
return (
|
||||
<FontProvider
|
||||
systemFont={fontPrefs.systemFont}
|
||||
moneyFont={fontPrefs.moneyFont}
|
||||
>
|
||||
<PrivacyProvider>
|
||||
<AppNavbar
|
||||
user={{ ...session.user, image: session.user.image ?? null }}
|
||||
pagadorAvatarUrl={adminPagador?.avatarUrl ?? null}
|
||||
preLancamentosCount={preLancamentosCount}
|
||||
notificationsSnapshot={notificationsSnapshot}
|
||||
/>
|
||||
<div className="relative flex flex-1 flex-col pt-16">
|
||||
<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>
|
||||
<PrivacyProvider>
|
||||
<AppNavbar
|
||||
user={{ ...session.user, image: session.user.image ?? null }}
|
||||
pagadorAvatarUrl={adminPagador?.avatarUrl ?? null}
|
||||
preLancamentosCount={preLancamentosCount}
|
||||
notificationsSnapshot={notificationsSnapshot}
|
||||
/>
|
||||
<div className="relative flex flex-1 flex-col pt-16">
|
||||
<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>
|
||||
</PrivacyProvider>
|
||||
</FontProvider>
|
||||
</div>
|
||||
</PrivacyProvider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@ import { UpdateEmailForm } from "@/features/settings/components/update-email-for
|
||||
import { UpdateNameForm } from "@/features/settings/components/update-name-form";
|
||||
import { UpdatePasswordForm } from "@/features/settings/components/update-password-form";
|
||||
import { fetchAjustesPageData } from "@/features/settings/queries";
|
||||
import { DEFAULT_FONT_KEY } from "@/public/fonts/font_index";
|
||||
import { Card } from "@/shared/components/ui/card";
|
||||
import {
|
||||
Tabs,
|
||||
@@ -78,8 +77,6 @@ export default async function Page() {
|
||||
lancamentosColumnOrder={
|
||||
userPreferences?.lancamentosColumnOrder ?? null
|
||||
}
|
||||
systemFont={userPreferences?.systemFont ?? DEFAULT_FONT_KEY}
|
||||
moneyFont={userPreferences?.moneyFont ?? DEFAULT_FONT_KEY}
|
||||
/>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
@@ -9,10 +9,6 @@
|
||||
}
|
||||
|
||||
:root {
|
||||
/* Font customization */
|
||||
--font-app: var(--font-ai-sans);
|
||||
--font-money: var(--font-ai-sans);
|
||||
|
||||
/* Base surfaces - warm cream with subtle orange undertone */
|
||||
--background: oklch(98.01% 0.00331 67.026);
|
||||
--foreground: #201207;
|
||||
@@ -186,8 +182,7 @@
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--default-font-family: var(--font-app);
|
||||
--default-mono-font-family: var(--font-money);
|
||||
--default-font-family: var(--font-america);
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--color-card: var(--card);
|
||||
@@ -259,6 +254,7 @@
|
||||
|
||||
body {
|
||||
@apply bg-background text-foreground;
|
||||
font-family: var(--font-america), sans-serif;
|
||||
}
|
||||
|
||||
*::selection {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Analytics } from "@vercel/analytics/next";
|
||||
import { SpeedInsights } from "@vercel/speed-insights/next";
|
||||
import type { Metadata } from "next";
|
||||
import { allFontVariables } from "@/public/fonts/font_index";
|
||||
import { ThemeProvider } from "@/shared/components/providers/theme-provider";
|
||||
import { Toaster } from "@/shared/components/ui/sonner";
|
||||
import "./globals.css";
|
||||
import { america } from "@/public/fonts/font_index";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: {
|
||||
@@ -21,7 +21,11 @@ export default function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="pt-BR" className={allFontVariables} suppressHydrationWarning>
|
||||
<html
|
||||
lang="pt-BR"
|
||||
className={`${america.variable} ${america.className}`}
|
||||
suppressHydrationWarning
|
||||
>
|
||||
<head>
|
||||
<meta name="apple-mobile-web-app-title" content="OpenMonetis" />
|
||||
</head>
|
||||
|
||||
@@ -7,7 +7,6 @@ import { revalidatePath } from "next/cache";
|
||||
import { headers } from "next/headers";
|
||||
import { z } from "zod";
|
||||
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 { db, schema } from "@/shared/lib/db";
|
||||
import { PAGADOR_ROLE_ADMIN } from "@/shared/lib/payers/constants";
|
||||
@@ -56,8 +55,6 @@ const deleteAccountSchema = z.object({
|
||||
const updatePreferencesSchema = z.object({
|
||||
extratoNoteAsColumn: z.boolean(),
|
||||
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
|
||||
@@ -404,8 +401,6 @@ export async function updatePreferencesAction(
|
||||
.set({
|
||||
extratoNoteAsColumn: validated.extratoNoteAsColumn,
|
||||
lancamentosColumnOrder: validated.lancamentosColumnOrder,
|
||||
systemFont: validated.systemFont,
|
||||
moneyFont: validated.moneyFont,
|
||||
updatedAt: new Date(),
|
||||
})
|
||||
.where(eq(schema.preferenciasUsuario.userId, session.user.id));
|
||||
@@ -415,8 +410,6 @@ export async function updatePreferencesAction(
|
||||
userId: session.user.id,
|
||||
extratoNoteAsColumn: validated.extratoNoteAsColumn,
|
||||
lancamentosColumnOrder: validated.lancamentosColumnOrder,
|
||||
systemFont: validated.systemFont,
|
||||
moneyFont: validated.moneyFont,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -18,31 +18,20 @@ import {
|
||||
import { CSS } from "@dnd-kit/utilities";
|
||||
import { RiDragMove2Line } from "@remixicon/react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useEffect, useState, useTransition } from "react";
|
||||
import { useState, useTransition } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { updatePreferencesAction } from "@/features/settings/actions";
|
||||
import {
|
||||
DEFAULT_LANCAMENTOS_COLUMN_ORDER,
|
||||
LANCAMENTOS_COLUMN_LABELS,
|
||||
} 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 { Label } from "@/shared/components/ui/label";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/shared/components/ui/select";
|
||||
import { Switch } from "@/shared/components/ui/switch";
|
||||
|
||||
interface PreferencesFormProps {
|
||||
extratoNoteAsColumn: boolean;
|
||||
lancamentosColumnOrder: string[] | null;
|
||||
systemFont: string;
|
||||
moneyFont: string;
|
||||
}
|
||||
|
||||
function SortableColumnItem({ id }: { id: string }) {
|
||||
@@ -85,8 +74,6 @@ function SortableColumnItem({ id }: { id: string }) {
|
||||
export function PreferencesForm({
|
||||
extratoNoteAsColumn: initialExtratoNoteAsColumn,
|
||||
lancamentosColumnOrder: initialColumnOrder,
|
||||
systemFont: initialSystemFont,
|
||||
moneyFont: initialMoneyFont,
|
||||
}: PreferencesFormProps) {
|
||||
const router = useRouter();
|
||||
const [isPending, startTransition] = useTransition();
|
||||
@@ -98,9 +85,6 @@ export function PreferencesForm({
|
||||
? initialColumnOrder
|
||||
: DEFAULT_LANCAMENTOS_COLUMN_ORDER,
|
||||
);
|
||||
const [selectedSystemFont, setSelectedSystemFont] =
|
||||
useState(initialSystemFont);
|
||||
const [selectedMoneyFont, setSelectedMoneyFont] = useState(initialMoneyFont);
|
||||
|
||||
const sensors = useSensors(
|
||||
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>) => {
|
||||
event.preventDefault();
|
||||
|
||||
@@ -136,8 +109,6 @@ export function PreferencesForm({
|
||||
const result = await updatePreferencesAction({
|
||||
extratoNoteAsColumn,
|
||||
lancamentosColumnOrder: columnOrder,
|
||||
systemFont: selectedSystemFont,
|
||||
moneyFont: selectedMoneyFont,
|
||||
});
|
||||
|
||||
if (result.success) {
|
||||
@@ -151,70 +122,6 @@ export function PreferencesForm({
|
||||
|
||||
return (
|
||||
<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 */}
|
||||
<section className="space-y-4">
|
||||
<div>
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import { desc, eq } from "drizzle-orm";
|
||||
import { tokensApi } from "@/db/schema";
|
||||
import { type FontKey, normalizeFontKey } from "@/public/fonts/font_index";
|
||||
import { db, schema } from "@/shared/lib/db";
|
||||
|
||||
export interface UserPreferences {
|
||||
extratoNoteAsColumn: boolean;
|
||||
lancamentosColumnOrder: string[] | null;
|
||||
systemFont: FontKey;
|
||||
moneyFont: FontKey;
|
||||
}
|
||||
|
||||
export interface ApiToken {
|
||||
@@ -35,8 +32,6 @@ export async function fetchUserPreferences(
|
||||
.select({
|
||||
extratoNoteAsColumn: schema.preferenciasUsuario.extratoNoteAsColumn,
|
||||
lancamentosColumnOrder: schema.preferenciasUsuario.lancamentosColumnOrder,
|
||||
systemFont: schema.preferenciasUsuario.systemFont,
|
||||
moneyFont: schema.preferenciasUsuario.moneyFont,
|
||||
})
|
||||
.from(schema.preferenciasUsuario)
|
||||
.where(eq(schema.preferenciasUsuario.userId, userId))
|
||||
@@ -44,11 +39,7 @@ export async function fetchUserPreferences(
|
||||
|
||||
if (!result[0]) return null;
|
||||
|
||||
return {
|
||||
...result[0],
|
||||
systemFont: normalizeFontKey(result[0].systemFont),
|
||||
moneyFont: normalizeFontKey(result[0].moneyFont),
|
||||
};
|
||||
return result[0];
|
||||
}
|
||||
|
||||
export async function fetchApiTokens(userId: string): Promise<ApiToken[]> {
|
||||
|
||||
@@ -19,7 +19,6 @@ function MoneyValues({ amount, className, showPositiveSign = false }: Props) {
|
||||
|
||||
return (
|
||||
<span
|
||||
style={{ fontFamily: "var(--font-money)" }}
|
||||
className={cn(
|
||||
"inline-flex items-baseline transition-all duration-200 tracking-tighter",
|
||||
privacyMode &&
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -1,3 +1,2 @@
|
||||
export { FontProvider } from "./font-provider";
|
||||
export { PrivacyProvider } from "./privacy-provider";
|
||||
export { ThemeProvider } from "./theme-provider";
|
||||
|
||||
@@ -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),
|
||||
};
|
||||
},
|
||||
);
|
||||
Reference in New Issue
Block a user