mirror of
https://github.com/felipegcoutinho/openmonetis.git
synced 2026-05-09 19:01:47 +00:00
Merge branch 'main' into feat/fix-ui
This commit is contained in:
@@ -11,7 +11,7 @@ export default function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<section className="space-y-6 pt-4">
|
||||
<section className="space-y-6">
|
||||
<PageDescription
|
||||
icon={<RiBankLine />}
|
||||
title="Contas"
|
||||
|
||||
26
src/app/(dashboard)/attachments/layout.tsx
Normal file
26
src/app/(dashboard)/attachments/layout.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { RiAttachmentLine } from "@remixicon/react";
|
||||
import MonthNavigation from "@/shared/components/month-picker/month-navigation";
|
||||
import PageDescription from "@/shared/components/page-description";
|
||||
|
||||
export const metadata = {
|
||||
title: "Anexos",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<section className="space-y-6">
|
||||
<PageDescription
|
||||
icon={<RiAttachmentLine />}
|
||||
title="Anexos"
|
||||
subtitle="Gerencie os anexos das suas transações"
|
||||
/>
|
||||
<MonthNavigation />
|
||||
|
||||
{children}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -11,7 +11,7 @@ export default function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<section className="space-y-6 pt-4">
|
||||
<section className="space-y-6">
|
||||
<PageDescription
|
||||
icon={<RiBarChart2Line />}
|
||||
title="Orçamentos"
|
||||
|
||||
@@ -11,7 +11,7 @@ export default function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<section className="space-y-6 pt-4">
|
||||
<section className="space-y-6">
|
||||
<PageDescription
|
||||
icon={<RiCalendarEventLine />}
|
||||
title="Calendário"
|
||||
|
||||
@@ -11,7 +11,7 @@ export default function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<section className="space-y-6 pt-4">
|
||||
<section className="space-y-6">
|
||||
<PageDescription
|
||||
icon={<RiBankCard2Line />}
|
||||
title="Cartões"
|
||||
|
||||
@@ -11,7 +11,7 @@ export default function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<section className="space-y-6 pt-4">
|
||||
<section className="space-y-6">
|
||||
<PageDescription
|
||||
icon={<RiPriceTag3Line />}
|
||||
title="Categorias"
|
||||
|
||||
@@ -11,7 +11,7 @@ export default function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<section className="space-y-6 pt-4">
|
||||
<section className="space-y-6">
|
||||
<PageDescription
|
||||
icon={<RiHistoryLine />}
|
||||
title="Changelog"
|
||||
|
||||
@@ -11,7 +11,7 @@ export default function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<section className="space-y-6 pt-4">
|
||||
<section className="space-y-6">
|
||||
<PageDescription
|
||||
icon={<RiAtLine />}
|
||||
title="Pré-Lançamentos"
|
||||
|
||||
@@ -11,7 +11,7 @@ export default function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<section className="space-y-6 pt-4">
|
||||
<section className="space-y-6">
|
||||
<PageDescription
|
||||
icon={<RiSparklingLine />}
|
||||
title="Insights"
|
||||
|
||||
@@ -11,7 +11,7 @@ export default function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<section className="space-y-6 pt-4">
|
||||
<section className="space-y-6">
|
||||
<PageDescription
|
||||
icon={<RiTodoLine />}
|
||||
title="Anotações"
|
||||
|
||||
@@ -80,6 +80,8 @@ const EMPTY_FILTERS: TransactionSearchFilters = {
|
||||
categoryFilter: null,
|
||||
accountCardFilter: null,
|
||||
searchFilter: null,
|
||||
settledFilter: null,
|
||||
attachmentFilter: null,
|
||||
};
|
||||
|
||||
const createEmptySlugMaps = (): SlugMaps => ({
|
||||
|
||||
@@ -11,7 +11,7 @@ export default function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<section className="space-y-6 pt-4">
|
||||
<section className="space-y-6">
|
||||
<PageDescription
|
||||
icon={<RiGroupLine />}
|
||||
title="Pagadores"
|
||||
|
||||
@@ -11,7 +11,7 @@ export default function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<section className="space-y-6 pt-4">
|
||||
<section className="space-y-6">
|
||||
<PageDescription
|
||||
icon={<RiBankCard2Line />}
|
||||
title="Uso de Cartões"
|
||||
|
||||
@@ -71,7 +71,7 @@ export default async function RelatorioCartoesPage({
|
||||
<div className="flex size-14 items-center justify-center rounded-full bg-muted mb-4">
|
||||
<RiBankCard2Line className="size-7 text-muted-foreground" />
|
||||
</div>
|
||||
<p className="text-base font-medium">Nenhum cartão selecionado</p>
|
||||
<p className="text-base font-semibold">Nenhum cartão selecionado</p>
|
||||
<p className="text-sm text-muted-foreground mt-1">
|
||||
Selecione um cartão para ver os detalhes de uso.
|
||||
</p>
|
||||
|
||||
@@ -11,7 +11,7 @@ export default function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<section className="space-y-6 pt-4">
|
||||
<section className="space-y-6">
|
||||
<PageDescription
|
||||
icon={<RiFileChartLine />}
|
||||
title="Tendências"
|
||||
|
||||
@@ -11,7 +11,7 @@ export default function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<section className="space-y-6 pt-4">
|
||||
<section className="space-y-6">
|
||||
<PageDescription
|
||||
icon={<RiStore2Line />}
|
||||
title="Top Estabelecimentos"
|
||||
|
||||
@@ -11,7 +11,7 @@ export default function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<section className="space-y-6 pt-4">
|
||||
<section className="space-y-6">
|
||||
<PageDescription
|
||||
icon={<RiSecurePaymentLine />}
|
||||
title="Análise de Parcelas"
|
||||
|
||||
@@ -11,7 +11,7 @@ export default function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<section className="space-y-6 pt-4">
|
||||
<section className="space-y-6">
|
||||
<PageDescription
|
||||
icon={<RiSettings2Line />}
|
||||
title="Ajustes"
|
||||
|
||||
@@ -68,7 +68,7 @@ export default async function Page() {
|
||||
<Card className="p-6">
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<h2 className="text-xl font-medium mb-1">Preferências</h2>
|
||||
<h2 className="text-xl font-semibold mb-1">Preferências</h2>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Personalize sua experiência no OpenMonetis ajustando as
|
||||
configurações de acordo com suas necessidades.
|
||||
@@ -93,7 +93,9 @@ export default async function Page() {
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<h2 className="text-xl font-medium">OpenMonetis Companion</h2>
|
||||
<h2 className="text-xl font-semibold">
|
||||
OpenMonetis Companion
|
||||
</h2>
|
||||
<span className="inline-flex items-center gap-1 rounded-full bg-success/10 px-2 py-0.5 text-xs font-medium text-success dark:bg-success/10">
|
||||
<RiAndroidLine className="h-3 w-3" />
|
||||
Android
|
||||
@@ -115,7 +117,7 @@ export default async function Page() {
|
||||
<Card className="p-6">
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<h2 className="text-xl font-medium mb-1">Alterar nome</h2>
|
||||
<h2 className="text-xl font-semibold mb-1">Alterar nome</h2>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Atualize como seu nome aparece no OpenMonetis. Esse nome pode
|
||||
ser exibido em diferentes seções do app e em comunicações.
|
||||
@@ -131,7 +133,7 @@ export default async function Page() {
|
||||
<Card className="p-6">
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<h2 className="text-xl font-medium mb-1">Alterar senha</h2>
|
||||
<h2 className="text-xl font-semibold mb-1">Alterar senha</h2>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Defina uma nova senha para sua conta. Guarde-a em local
|
||||
seguro.
|
||||
@@ -147,7 +149,7 @@ export default async function Page() {
|
||||
<Card className="p-6">
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<h2 className="text-xl font-medium mb-1">Passkeys</h2>
|
||||
<h2 className="text-xl font-semibold mb-1">Passkeys</h2>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Passkeys permitem login sem senha, usando biometria (Face ID,
|
||||
Touch ID, Windows Hello) ou chaves de segurança.
|
||||
@@ -163,7 +165,7 @@ export default async function Page() {
|
||||
<Card className="p-6">
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<h2 className="text-xl font-medium mb-1">Alterar e-mail</h2>
|
||||
<h2 className="text-xl font-semibold mb-1">Alterar e-mail</h2>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Atualize o e-mail associado à sua conta. Você precisará
|
||||
confirmar os links enviados para o novo e também para o e-mail
|
||||
@@ -183,9 +185,7 @@ export default async function Page() {
|
||||
<Card className="p-6">
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<h2 className="text-xl font-medium mb-1 text-destructive">
|
||||
Ações perigosas
|
||||
</h2>
|
||||
<h2 className="text-xl font-semibold mb-1">Ações perigosas</h2>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Você pode zerar os dados do OpenMonetis e manter seu acesso,
|
||||
ou excluir sua conta inteira de forma irreversível.
|
||||
|
||||
@@ -11,7 +11,7 @@ export default function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<section className="space-y-6 pt-4">
|
||||
<section className="space-y-6">
|
||||
<PageDescription
|
||||
icon={<RiArrowLeftRightLine />}
|
||||
title="Lançamentos"
|
||||
|
||||
@@ -120,13 +120,13 @@ export default async function Page() {
|
||||
</div>
|
||||
|
||||
<div className="max-w-8xl mx-auto px-4 relative">
|
||||
<div className="mx-auto flex max-w-3xl flex-col items-center text-center gap-5 md:gap-6 pb-10 md:pb-14">
|
||||
<div className="mx-auto flex max-w-4xl flex-col items-center text-center gap-5 md:gap-6 pb-10 md:pb-14">
|
||||
<Badge variant="outline">
|
||||
<RiGithubFill className="size-4 mr-1" />
|
||||
Projeto Open Source
|
||||
</Badge>
|
||||
|
||||
<h1 className="text-3xl sm:text-4xl md:text-5xl lg:text-6xl font-medium tracking-tight">
|
||||
<h1 className="text-3xl sm:text-4xl md:text-5xl lg:text-6xl font-semibold">
|
||||
Suas finanças,
|
||||
<span className="text-primary"> do seu jeito</span>
|
||||
</h1>
|
||||
@@ -207,7 +207,7 @@ export default async function Page() {
|
||||
className="flex flex-col items-center text-center gap-1.5"
|
||||
>
|
||||
<Icon className="size-5" style={{ color: colorVar }} />
|
||||
<span className="text-2xl md:text-3xl font-medium">
|
||||
<span className="text-2xl md:text-3xl font-semibold">
|
||||
{value}
|
||||
</span>
|
||||
<span className="text-xs md:text-sm text-muted-foreground">
|
||||
@@ -229,7 +229,7 @@ export default async function Page() {
|
||||
<Badge variant="outline" className="mb-4">
|
||||
Conheça as telas
|
||||
</Badge>
|
||||
<h2 className="text-2xl sm:text-3xl md:text-4xl font-medium tracking-tight mb-3 md:mb-4">
|
||||
<h2 className="text-2xl sm:text-3xl md:text-4xl mb-3 md:mb-4 font-semibold">
|
||||
Veja o que você pode fazer
|
||||
</h2>
|
||||
<p className="text-base md:text-lg text-muted-foreground max-w-2xl mx-auto px-4 sm:px-0">
|
||||
@@ -254,7 +254,7 @@ export default async function Page() {
|
||||
<Badge variant="outline" className="mb-4">
|
||||
O que tem aqui
|
||||
</Badge>
|
||||
<h2 className="text-2xl sm:text-3xl md:text-4xl font-medium tracking-tight mb-3 md:mb-4">
|
||||
<h2 className="text-2xl sm:text-3xl md:text-4xl mb-3 md:mb-4 font-semibold">
|
||||
Funcionalidades que importam
|
||||
</h2>
|
||||
<p className="text-base md:text-lg text-muted-foreground max-w-2xl mx-auto px-4 sm:px-0">
|
||||
@@ -282,7 +282,7 @@ export default async function Page() {
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-medium text-base md:text-lg mb-1.5 md:mb-2">
|
||||
<h3 className="font-semibold text-base md:text-lg mb-1.5 md:mb-2">
|
||||
{feature.title}
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground leading-relaxed">
|
||||
@@ -298,7 +298,7 @@ export default async function Page() {
|
||||
|
||||
<AnimateOnScroll>
|
||||
<div className="mt-8 md:mt-12">
|
||||
<h3 className="text-sm font-medium text-center mb-4 md:mb-6 text-muted-foreground">
|
||||
<h3 className="text-sm font-semibold text-center mb-4 md:mb-6 text-muted-foreground">
|
||||
Também inclui
|
||||
</h3>
|
||||
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
|
||||
@@ -319,7 +319,7 @@ export default async function Page() {
|
||||
/>
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<h4 className="font-medium text-sm mb-0.5">
|
||||
<h4 className="font-semibold text-sm mb-0.5">
|
||||
{feature.title}
|
||||
</h4>
|
||||
<p className="text-xs text-muted-foreground leading-relaxed">
|
||||
@@ -346,7 +346,7 @@ export default async function Page() {
|
||||
<RiSmartphoneLine className="size-3.5 mr-1" />
|
||||
Mobile
|
||||
</Badge>
|
||||
<h2 className="text-2xl sm:text-3xl md:text-4xl font-medium tracking-tight mb-3 md:mb-4">
|
||||
<h2 className="text-2xl sm:text-3xl md:text-4xl mb-3 md:mb-4 font-semibold">
|
||||
Use o OpenMonetis no celular sem perder o fluxo
|
||||
</h2>
|
||||
<p className="text-base md:text-lg text-muted-foreground max-w-2xl mx-auto px-4 sm:px-0">
|
||||
@@ -529,7 +529,7 @@ export default async function Page() {
|
||||
<Badge variant="outline" className="mb-4">
|
||||
Stack técnica
|
||||
</Badge>
|
||||
<h2 className="text-2xl sm:text-3xl md:text-4xl font-medium tracking-tight mb-3 md:mb-4">
|
||||
<h2 className="text-2xl sm:text-3xl md:text-4xl mb-3 md:mb-4 font-semibold">
|
||||
O que roda por baixo
|
||||
</h2>
|
||||
<p className="text-base md:text-lg text-muted-foreground max-w-2xl mx-auto px-4 sm:px-0">
|
||||
@@ -556,7 +556,7 @@ export default async function Page() {
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-medium text-base md:text-lg mb-1.5 md:mb-2">
|
||||
<h3 className="font-semibold text-base md:text-lg mb-1.5 md:mb-2">
|
||||
{item.title}
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground mb-2 md:mb-3">
|
||||
@@ -582,7 +582,7 @@ export default async function Page() {
|
||||
<Badge variant="outline" className="mb-4">
|
||||
Como usar
|
||||
</Badge>
|
||||
<h2 className="text-2xl sm:text-3xl md:text-4xl font-medium tracking-tight mb-3 md:mb-4">
|
||||
<h2 className="text-2xl sm:text-3xl md:text-4xl mb-3 md:mb-4 font-semibold">
|
||||
Rode no seu computador
|
||||
</h2>
|
||||
<p className="text-base md:text-lg text-muted-foreground px-4 sm:px-0">
|
||||
@@ -617,7 +617,7 @@ export default async function Page() {
|
||||
<Badge variant="outline" className="mb-4">
|
||||
Para quem é?
|
||||
</Badge>
|
||||
<h2 className="text-2xl sm:text-3xl md:text-4xl font-medium tracking-tight mb-3 md:mb-4">
|
||||
<h2 className="text-2xl sm:text-3xl md:text-4xl mb-3 md:mb-4 font-semibold">
|
||||
Feito para quem gosta de controle
|
||||
</h2>
|
||||
<p className="text-base md:text-lg text-muted-foreground px-4 sm:px-0">
|
||||
@@ -644,7 +644,7 @@ export default async function Page() {
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-medium mb-1">{item.title}</h3>
|
||||
<h3 className="font-semibold mb-1">{item.title}</h3>
|
||||
<p className="text-xs sm:text-sm text-muted-foreground">
|
||||
{item.description}
|
||||
</p>
|
||||
@@ -664,7 +664,7 @@ export default async function Page() {
|
||||
<div className="max-w-8xl mx-auto px-4">
|
||||
<AnimateOnScroll>
|
||||
<div className="mx-auto max-w-4xl rounded-2xl border bg-card px-8 py-12 md:py-16 text-center">
|
||||
<h2 className="text-2xl sm:text-3xl md:text-4xl font-medium tracking-tight mb-3 md:mb-4">
|
||||
<h2 className="text-2xl sm:text-3xl md:text-4xl mb-3 md:mb-4 font-semibold">
|
||||
Pronto para testar?
|
||||
</h2>
|
||||
<p className="text-base md:text-lg text-muted-foreground mb-6 md:mb-8">
|
||||
@@ -715,7 +715,7 @@ export default async function Page() {
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="font-medium mb-3 md:mb-4">Projeto</h3>
|
||||
<h3 className="font-semibold mb-3 md:mb-4">Projeto</h3>
|
||||
<ul className="space-y-2.5 md:space-y-3 text-sm text-muted-foreground">
|
||||
<li>
|
||||
<Link
|
||||
@@ -749,7 +749,7 @@ export default async function Page() {
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 className="font-medium mb-3 md:mb-4">Companion</h3>
|
||||
<h3 className="font-semibold mb-3 md:mb-4">Companion</h3>
|
||||
<ul className="space-y-2.5 md:space-y-3 text-sm text-muted-foreground">
|
||||
<li>
|
||||
<Link
|
||||
|
||||
26
src/app/api/logo/mapping/route.ts
Normal file
26
src/app/api/logo/mapping/route.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { getOptionalUserSession } from "@/shared/lib/auth/server";
|
||||
import { fetchEstablishmentLogoDomain } from "@/shared/lib/logo/establishment-logo-queries";
|
||||
|
||||
/**
|
||||
* GET /api/logo/mapping?name={name}
|
||||
*
|
||||
* Retorna o domínio Logo.dev salvo pelo usuário para um estabelecimento.
|
||||
* Usado pelo EstablishmentLogo para hidratar o domain salvo no banco.
|
||||
*/
|
||||
export async function GET(request: Request) {
|
||||
const session = await getOptionalUserSession();
|
||||
if (!session) {
|
||||
return NextResponse.json({ domain: null }, { status: 200 });
|
||||
}
|
||||
|
||||
const { searchParams } = new URL(request.url);
|
||||
const name = searchParams.get("name")?.trim();
|
||||
|
||||
if (!name) {
|
||||
return NextResponse.json({ domain: null }, { status: 200 });
|
||||
}
|
||||
|
||||
const domain = await fetchEstablishmentLogoDomain(session.user.id, name);
|
||||
return NextResponse.json({ domain });
|
||||
}
|
||||
80
src/app/api/logo/search/route.ts
Normal file
80
src/app/api/logo/search/route.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { getOptionalUserSession } from "@/shared/lib/auth/server";
|
||||
|
||||
const LOGO_DEV_SEARCH_URL = "https://api.logo.dev/search";
|
||||
|
||||
interface LogoResult {
|
||||
name: string;
|
||||
domain: string;
|
||||
}
|
||||
|
||||
async function searchByStrategy(
|
||||
q: string,
|
||||
strategy: "match" | "typeahead",
|
||||
secretKey: string,
|
||||
): Promise<LogoResult[]> {
|
||||
try {
|
||||
const url = `${LOGO_DEV_SEARCH_URL}?q=${encodeURIComponent(q)}&strategy=${strategy}`;
|
||||
const res = await fetch(url, {
|
||||
headers: { Authorization: `Bearer ${secretKey}` },
|
||||
next: { revalidate: 3600 },
|
||||
});
|
||||
if (!res.ok) return [];
|
||||
const data = await res.json();
|
||||
return Array.isArray(data) ? data : [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /api/logo/search?q={name}
|
||||
*
|
||||
* Proxy seguro para a Logo.dev Brand Search API.
|
||||
* Faz duas buscas paralelas (match + typeahead) e retorna até 20 resultados únicos.
|
||||
* Usa LOGO_DEV_SECRET_KEY server-side — nunca exposta ao cliente.
|
||||
*/
|
||||
export async function GET(request: Request) {
|
||||
const session = await getOptionalUserSession();
|
||||
if (!session) {
|
||||
return NextResponse.json({ error: "Não autorizado." }, { status: 401 });
|
||||
}
|
||||
|
||||
const { searchParams } = new URL(request.url);
|
||||
const q = searchParams.get("q")?.trim();
|
||||
|
||||
if (!q) {
|
||||
return NextResponse.json(
|
||||
{ error: "Parâmetro q obrigatório." },
|
||||
{ status: 400 },
|
||||
);
|
||||
}
|
||||
|
||||
const secretKey = process.env.LOGO_DEV_SECRET_KEY;
|
||||
if (!secretKey) {
|
||||
return NextResponse.json(
|
||||
{ error: "Logo.dev não configurado." },
|
||||
{ status: 503 },
|
||||
);
|
||||
}
|
||||
|
||||
// Duas buscas paralelas para maximizar resultados (cada uma retorna até 10)
|
||||
const [matchResults, typeaheadResults] = await Promise.all([
|
||||
searchByStrategy(q, "match", secretKey),
|
||||
searchByStrategy(q, "typeahead", secretKey),
|
||||
]);
|
||||
|
||||
// Mescla e deduplica por domain, mantendo ordem (match tem prioridade)
|
||||
const seen = new Set<string>();
|
||||
const merged: LogoResult[] = [];
|
||||
|
||||
for (const result of [...matchResults, ...typeaheadResults]) {
|
||||
if (!seen.has(result.domain)) {
|
||||
seen.add(result.domain);
|
||||
merged.push(result);
|
||||
if (merged.length >= 20) break;
|
||||
}
|
||||
}
|
||||
|
||||
return NextResponse.json(merged);
|
||||
}
|
||||
@@ -177,7 +177,7 @@
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--default-font-family: var(--font-america);
|
||||
--default-font-family: var(--font-inter);
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--color-card: var(--card);
|
||||
|
||||
@@ -4,7 +4,7 @@ import { QueryProvider } from "@/shared/components/providers/query-provider";
|
||||
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";
|
||||
import { inter } from "@/public/fonts/font_index";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: {
|
||||
@@ -24,19 +24,23 @@ export default function RootLayout({
|
||||
<html
|
||||
data-scroll-behavior="smooth"
|
||||
lang="pt-BR"
|
||||
className={`${america.variable} ${america.className} `}
|
||||
className={`${inter.variable}`}
|
||||
suppressHydrationWarning
|
||||
>
|
||||
<head>
|
||||
<meta name="apple-mobile-web-app-title" content="OpenMonetis" />
|
||||
<script
|
||||
defer
|
||||
src="https://umami.felipecoutinho.com/script.js"
|
||||
data-website-id="ea438854-a014-42ea-b416-0a8321471f0f"
|
||||
data-domains="openmonetis.com"
|
||||
/>
|
||||
{process.env.UMAMI_URL && process.env.UMAMI_WEBSITE_ID && (
|
||||
<script
|
||||
defer
|
||||
src={`${process.env.UMAMI_URL}/script.js`}
|
||||
data-website-id={process.env.UMAMI_WEBSITE_ID}
|
||||
{...(process.env.UMAMI_DOMAINS
|
||||
? { "data-domains": process.env.UMAMI_DOMAINS }
|
||||
: {})}
|
||||
/>
|
||||
)}
|
||||
</head>
|
||||
<body className="antialiased" suppressHydrationWarning>
|
||||
<body className="subpixel-antialiased" suppressHydrationWarning>
|
||||
<ThemeProvider attribute="class" defaultTheme="light">
|
||||
<QueryProvider>
|
||||
<Suspense>{children}</Suspense>
|
||||
|
||||
Reference in New Issue
Block a user