mirror of
https://github.com/felipegcoutinho/openmonetis.git
synced 2026-05-09 11:01:45 +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
|
### 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
|
||||||
|
|||||||
@@ -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.
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";
|
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.
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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[]> {
|
||||||
|
|||||||
@@ -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 &&
|
||||||
|
|||||||
@@ -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 { PrivacyProvider } from "./privacy-provider";
|
||||||
export { ThemeProvider } from "./theme-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