From 70f1541ec0aae71fcb109f2d05c56f7b2fd0158d Mon Sep 17 00:00:00 2001 From: Erik Date: Sat, 29 Nov 2025 14:07:47 -0300 Subject: [PATCH] feat: Implement global badge system with Settings model and global PartnerBadge component --- frontend/prisma/schema.prisma | 8 ++ frontend/src/app/[locale]/page.tsx | 16 ++-- frontend/src/app/api/settings/route.ts | 98 ++++++++++++++++++++++++ frontend/src/components/Footer.tsx | 25 +----- frontend/src/components/PartnerBadge.tsx | 48 ++++++++++++ 5 files changed, 163 insertions(+), 32 deletions(-) create mode 100644 frontend/src/app/api/settings/route.ts create mode 100644 frontend/src/components/PartnerBadge.tsx diff --git a/frontend/prisma/schema.prisma b/frontend/prisma/schema.prisma index 45a5d3d..15dec01 100644 --- a/frontend/prisma/schema.prisma +++ b/frontend/prisma/schema.prisma @@ -87,3 +87,11 @@ model Translation { @@unique([sourceText, sourceLang, targetLang]) @@index([sourceLang, targetLang]) } + +// Modelo de Configurações Globais +model Settings { + id String @id @default(cuid()) + showPartnerBadge Boolean @default(false) + partnerName String @default("Coca-Cola") + updatedAt DateTime @updatedAt +} diff --git a/frontend/src/app/[locale]/page.tsx b/frontend/src/app/[locale]/page.tsx index f3ffc0e..b4fd87d 100644 --- a/frontend/src/app/[locale]/page.tsx +++ b/frontend/src/app/[locale]/page.tsx @@ -4,6 +4,7 @@ import { useEffect, useState } from "react"; import Link from "next/link"; import { usePageContent } from "@/hooks/usePageContent"; import { useLocale } from "@/contexts/LocaleContext"; +import { PartnerBadge } from "@/components/PartnerBadge"; type PortfolioProject = { id: string; @@ -54,11 +55,7 @@ export default function Home() { const hero = content?.hero || { title: 'Engenharia de Excelência para Seus Projetos', subtitle: 'Soluções completas em engenharia veicular, mecânica e segurança do trabalho com mais de 15 anos de experiência.', - buttonText: 'Conheça Nossos Serviços', - badge: { - text: 'Coca-Cola', - show: false - } + buttonText: 'Conheça Nossos Serviços' }; const features = content?.features || { @@ -167,12 +164,9 @@ export default function Home() {
- {hero.badge?.show && ( -
- - {t('home.officialProvider')} {hero.badge.text} -
- )} +
+ +

{hero.title} diff --git a/frontend/src/app/api/settings/route.ts b/frontend/src/app/api/settings/route.ts new file mode 100644 index 0000000..ae3cc19 --- /dev/null +++ b/frontend/src/app/api/settings/route.ts @@ -0,0 +1,98 @@ +import { NextRequest, NextResponse } from 'next/server'; +import { PrismaClient } from '@prisma/client'; +import { cookies } from 'next/headers'; +import jwt from 'jsonwebtoken'; + +const prisma = new PrismaClient(); + +// Middleware de autenticação +async function authenticate(request: NextRequest) { + const cookieStore = await cookies(); + const token = cookieStore.get('auth_token')?.value; + + if (!token) { + return null; + } + + try { + const decoded = jwt.verify(token, process.env.JWT_SECRET!) as { userId: string }; + const user = await prisma.user.findUnique({ + where: { id: decoded.userId }, + select: { id: true, email: true, name: true } + }); + return user; + } catch (error) { + return null; + } +} + +/** + * GET /api/settings + * Busca configurações globais (público) + */ +export async function GET(request: NextRequest) { + try { + // Buscar ou criar configurações padrão + let settings = await prisma.settings.findFirst(); + + if (!settings) { + settings = await prisma.settings.create({ + data: { + showPartnerBadge: false, + partnerName: 'Coca-Cola' + } + }); + } + + return NextResponse.json(settings); + } catch (error) { + console.error('Erro ao buscar settings:', error); + return NextResponse.json( + { error: 'Erro ao buscar configurações' }, + { status: 500 } + ); + } +} + +/** + * POST /api/settings + * Atualiza configurações globais (admin apenas) + */ +export async function POST(request: NextRequest) { + try { + const user = await authenticate(request); + if (!user) { + return NextResponse.json({ error: 'Não autorizado' }, { status: 401 }); + } + + const body = await request.json(); + const { showPartnerBadge, partnerName } = body; + + let settings = await prisma.settings.findFirst(); + + if (!settings) { + settings = await prisma.settings.create({ + data: { + showPartnerBadge: showPartnerBadge ?? false, + partnerName: partnerName ?? 'Coca-Cola' + } + }); + } else { + settings = await prisma.settings.update({ + where: { id: settings.id }, + data: { + ...(showPartnerBadge !== undefined && { showPartnerBadge }), + ...(partnerName !== undefined && { partnerName }) + } + }); + } + + return NextResponse.json({ success: true, settings }); + } catch (error) { + console.error('Erro ao atualizar settings:', error); + return NextResponse.json( + { error: 'Erro ao atualizar configurações: ' + (error as Error).message }, + { status: 500 } + ); + } +} diff --git a/frontend/src/components/Footer.tsx b/frontend/src/components/Footer.tsx index b814422..03b5b61 100644 --- a/frontend/src/components/Footer.tsx +++ b/frontend/src/components/Footer.tsx @@ -1,28 +1,14 @@ "use client"; import Link from 'next/link'; -import { useEffect } from 'react'; import { useLocale } from '@/contexts/LocaleContext'; -import { usePageContent } from '@/hooks/usePageContent'; +import { PartnerBadge } from './PartnerBadge'; export default function Footer() { const { locale, t } = useLocale(); - const { content } = usePageContent('home', locale); // Prefixo para links const prefix = locale === 'pt' ? '' : `/${locale}`; - - // Badge do hero (dinâmica) - const badge = content?.hero?.badge || { text: 'Coca-Cola', show: false }; - - // Recarregar quando conteúdo mudar - useEffect(() => { - const handleRefresh = () => { - window.location.reload(); - }; - window.addEventListener('translation:refresh', handleRefresh); - return () => window.removeEventListener('translation:refresh', handleRefresh); - }, []); return (