Imported from ndrd79/guia (
.opencode/skills/guia-comercio/SKILL.md). Install upstream withnpx skills add ndrd79/guia --skill guia-comercio. Copyright stays with the author.
Portal Maria Helena — Skill de contexto completo
Identidade
- Nome: portal-maria-helena
- Domínio: portalmariahelena.com.br
- Propósito: Portal de cidade para Maria Helena/PR — guia comercial, notícias, classificados, eventos, serviços
- Versão: 0.1.0 (privado)
Stack
| Tech | Versão |
|---|---|
| Next.js | ^14.2.33 (Pages Router) |
| TypeScript | 5.2.2 |
| Tailwind CSS | 3.3.5 |
| Prisma | ^5.6.0 |
| PostgreSQL | Supabase |
| Supabase Auth | Gerenciado |
| React Hook Form + Zod | ^7.61.1 + ^3.25.76 |
| Lucide React | ^0.526.0 |
| Recharts | ^2.12.7 |
| Nodemailer | ^6.10.1 |
Scripts principais
npm run dev— next devnpm run build— next buildnpm run lint— next lintnpm run db:push— prisma db pushnpm run db:studio— prisma studionpm run db:generate— prisma generate
Variáveis de ambiente (.env.example)
NEXT_PUBLIC_SUPABASE_URL
NEXT_PUBLIC_SUPABASE_ANON_KEY
SUPABASE_SERVICE_ROLE_KEY
SUPABASE_POOLER_URL
SUPABASE_DIRECT_URL
DATABASE_URL
NEXT_PUBLIC_SITE_URL=https://portalmariahelena.com.br
EMAIL_FROM
NEXT_PUBLIC_ADMIN_DEV_BYPASS # dev only
Estrutura de diretórios
F:\guia-comercio\
├── pages/ # Páginas (Pages Router)
├── components/ # Componentes React
├── lib/ # Utilitários, API helpers, banner catalog
├── hooks/ # Custom hooks
├── src/ # Código duplicado (hooks, types)
├── prisma/ # Schema do banco
├── supabase/ # Migrations
├── docs/ # Documentação
├── scripts/ # Scripts diversos
├── types/ # Definições TypeScript
├── public/ # Assets estáticos
└── styles/ # CSS global
Páginas públicas
| Rota | Arquivo | Descrição |
|---|---|---|
/ |
pages/index.tsx |
Home (notícias, empresas, banners, eventos, classificados) |
/guia |
pages/guia/index.tsx |
Guia comercial com busca/filtros/paginação |
/guia/[id] |
pages/guia/[id].tsx |
Detalhe da empresa |
/noticias |
pages/noticias/index.tsx |
Lista de notícias |
/noticias/[id] |
pages/noticias/[id].tsx |
Notícia individual |
/eventos |
pages/eventos/index.tsx |
Lista de eventos |
/eventos/[id] |
pages/eventos/[id].tsx |
Evento individual |
/eventos/feira-do-produtor |
pages/eventos/feira-do-produtor.tsx |
Feira do Produtor |
/classificados |
pages/classificados/index.tsx |
Classificados |
/contato |
pages/contato/index.tsx |
Contato |
/servicos |
pages/servicos/index.tsx |
Serviços |
/servicos/telefones-uteis |
pages/servicos/telefones-uteis.tsx |
Telefones úteis |
/servicos/transporte |
pages/servicos/transporte.tsx |
Transporte |
/servicos/clima |
pages/servicos/clima.tsx |
Clima |
/empresas-locais |
pages/empresas-locais.tsx |
Empresas locais |
/cadastro-empresa |
pages/cadastro-empresa.tsx |
Cadastro de empresa |
/cadastro |
pages/cadastro.tsx |
Cadastro de usuário |
/login |
pages/login.tsx |
Login |
/anuncie |
pages/anuncie.tsx |
Anuncie conosco |
/sobre |
pages/sobre.tsx |
Sobre |
/termos |
pages/termos.tsx |
Termos de uso |
/privacidade |
pages/privacidade.tsx |
Privacidade |
/cookies-preferencias |
pages/cookies-preferencias.tsx |
Cookies |
/mapa-site |
pages/mapa-site.tsx |
Mapa do site |
/transparencia |
pages/transparencia.tsx |
Transparência |
Páginas do usuário (/area-usuario)
| Rota | Descrição |
|---|---|
/area-usuario |
Dashboard |
/area-usuario/perfil |
Perfil |
/area-usuario/meus-classificados |
Meus classificados |
/area-usuario/novo-classificado |
Novo classificado |
/area-usuario/minhas-empresas |
Minhas empresas |
/area-usuario/favoritos |
Favoritos |
/area-usuario/mensagens |
Mensagens |
/area-usuario/configuracoes |
Configurações |
Páginas de admin (/admin)
| Rota | Arquivo | Descrição |
|---|---|---|
/admin |
index.tsx |
Dashboard com stats |
/admin/login |
login.tsx |
Login admin |
/admin/redirect |
redirect.tsx |
Redirect auth |
/admin/noticias |
noticias.tsx |
CRUD notícias (858 linhas) |
/admin/classificados |
classificados.tsx |
Gerenciar classificados |
/admin/eventos |
eventos.tsx |
Gerenciar eventos |
/admin/feira-produtor |
feira-produtor.tsx |
Feira do Produtor |
/admin/banners |
banners.tsx |
Gerenciar banners (615 linhas) |
/admin/media |
media.tsx |
Biblioteca de mídia |
/admin/empresas |
empresas.tsx |
Lista empresas |
/admin/empresas/gerenciar |
empresas/gerenciar.tsx |
Gerenciar empresas (660 linhas) |
/admin/empresas/pendentes |
empresas/pendentes.tsx |
Aprovações pendentes |
/admin/empresas/importar |
empresas/importar.tsx |
Importar CSV |
/admin/empresas/relatorios |
empresas/relatorios.tsx |
Relatórios |
/admin/temas-sazonais |
temas-sazonais.tsx |
Temas sazonais (675 linhas) |
/admin/usuarios |
usuarios.tsx |
Gerenciar usuários (407 linhas) |
/admin/diagnostico |
diagnostico.tsx |
Diagnóstico |
Database (Prisma — modelos principais)
noticias
id UUID PK, titulo String(255), categoria String(100), data Date, imagem String?, descricao String?, conteudo String?, banner_id UUID? FK→banners, credito_foto String(255)?, fonte String(500)?, created_at, updated_at, destaque Boolean? default false, workflow_status String(50)? default "draft" (draft/review/approved/rejected/published/archived), meta_title String(500)?, meta_description String?, slug String(500)? UNIQUE, tags String[], created_by UUID? FK→users, updated_by UUID? FK→users
empresas
id UUID PK, name String(255), description String?, category String(100), rating Decimal(2,1)? default 0.0, reviews Int? default 0, location String(255)?, phone String(20)?, email String(255)?, website String(255)?, address String?, image String?, featured Boolean? default false, is_new Boolean? default false, ativo Boolean? default true, plan_type plan_type_enum (basic/premium), premium_expires_at Timestamptz?, exibir_em_empresas_locais Boolean? default false, plano String(20)? default "basico", status empresa_status_enum (pending/approved/rejected), whatsapp String(20)?, horario_funcionamento_dias String(100)?, horario_funcionamento_horario String(100)?, facebook String(255)?, instagram String(255)?, maps String?, cidade String(100)?, user_source String(50)?, import_batch_id UUID? FK→import_batches
banners
id UUID PK, nome String(255), posicao String(100), imagem String, link String?, largura Int? default 400, altura Int? default 200, ativo Boolean? default true, ordem Int default 0, local String(50)? default "geral" (geral/home/guia_comercial/noticias/eventos/classificados/contato), tempo_exibicao Int? default 5, data_inicio Timestamptz?, data_fim Timestamptz?, is_principal Boolean? default false, prioridade Int? default 0, categoria String(50)? default "regular" (regular/hybrid), tipo String(20)? default "estatico"
banner_analytics
id BigInt PK auto, banner_id UUID FK, tipo String(20) (view/click), created_at, ip_address Inet?, user_agent String?, session_id String(255)?
banner_positions
Catálogo de posições: id Int PK auto, nome String(100) UNIQUE, descricao String?, largura_rec Int?, altura_rec Int?, paginas String(100)?
Outras tabelas
classificados, eventos, profiles, user_profiles, media_library, media_folders, media_usage, audit_logs, feira_produtor, produtores_feira, import_batches, import_results, seasonal_themes, news_analytics, news_versions, news_activity_log, workflow_comments, plan_history, backup_jobs/files/logs, storage_alerts/stats, video_ads/analytics/placements (legado)
API Routes
Públicas
| Rota | Método | Descrição |
|---|---|---|
/api/banners |
GET | Listar banners (filtros: posicao, local, ativo) |
/api/banners/deactivate |
POST | Desativar banners expirados |
/api/banners/validate |
POST | Validar dados de banner |
/api/empresas |
GET | Listar empresas |
/api/empresas-locais |
GET | Empresas locais |
/api/cadastro-empresa |
POST | Cadastro público de empresa |
/api/contact |
POST | Formulário de contato |
/api/upload |
POST | Upload de arquivo |
/api/ai-rewrite |
POST | Reescrever texto com IA |
/api/revalidate |
POST | Revalidar Next.js |
Admin (protegidas por withAdminAuth)
| Rota | Descrição |
|---|---|
/api/admin/stats |
Dashboard stats |
/api/admin/banners |
CRUD banners |
/api/admin/createBanner |
Criar banner |
/api/admin/updateBanner |
Atualizar banner |
/api/admin/noticias |
CRUD notícias |
/api/admin/eventos |
CRUD eventos |
/api/admin/usuarios |
CRUD usuários |
/api/admin/media |
Biblioteca de mídia |
/api/admin/diagnostico |
Diagnóstico |
/api/admin/empresas/* |
CRUD empresas |
Analytics
/api/analytics/banner-click, /api/analytics/banner-view, /api/analytics/ctr-roi, /api/analytics/dashboard, /api/analytics/summary, /api/analytics/track
Webhook
/api/webhook/empresa — Integração Google Forms (recebe submissões, cria empresa como pending)
Sistema de Autenticação
Admin
- Middleware (
middleware.ts): verifica sessão Supabase + role admin no banco - Cache cookie
admin-role-okcom 5 min TTL - APIs: wrapper
withAdminAuth()emlib/api/withAdminAuth.ts- Usa
getUser()(server-side), nãogetSession() - Cache in-memory de 1 minuto
- Suporte Bearer token ou cookie
- Usa
- Dev bypass:
NEXT_PUBLIC_ADMIN_DEV_BYPASS=true
Usuário
- Supabase Auth (email/senha)
- Tabela
profilescom role (user/admin) - Tabela
user_profilescom dados estendidos
Sistema de Banners
- 15+ posições centralizadas em
lib/banners/catalog.ts - Posições:
home_hero,categorias,servicos,noticias_topo,noticias_meio,noticias_fim,guia_sidebar,eventos_topo,classificados_topo,contato_topo,guia_listagem_topo,noticia_individual_topo,noticia_individual_meio,noticia_individual_fim,layout_global - Tipos:
estatico(imagem) — vídeo removido - Híbridos: banner com logo + CTA + texto patrocinado
- Agendamento:
data_inicio/data_fim - Analytics: view/click tracking, CTR
- Cache: 5 minutos in-memory para queries públicas
- Validação: Zod em
lib/banners/validation.ts - Workflow para adicionar posição:
.agent/workflows/add-banner-position.md
Componentes principais
Layout
Header.tsx, Nav.tsx, Footer.tsx, UserLayout.tsx, SearchBar.tsx, WeatherSlider.tsx, CookieBanner.tsx, SeasonalDecorations.tsx, ErrorBoundary.tsx
Banners
BannerCarousel.tsx, BannerContainer.tsx, BannerAd.tsx, BannerDebug.tsx, OptimizedBanner.tsx, HeroBanner.tsx, SecondaryBanner.tsx, FooterBanner.tsx, PageBanner.tsx, BusinessCarousel.tsx, banners/BannerSlot.tsx, banners/index.ts, banners/templates/
Admin
AdminLayout.tsx, StatCard.tsx, MetricCard.tsx, StatsPanel.tsx, AnalyticsDashboard.tsx, ChartCard.tsx, TrendChart.tsx, ModernTable.tsx, Pagination.tsx, ImageUploader.tsx, ImageUploaderWithLibrary.tsx, MediaLibrary.tsx, MediaPicker.tsx, BannerPreview.tsx, PreviewModal.tsx, CategoryBadge.tsx, StatusIndicator.tsx, Breadcrumb.tsx, RelativeDate.tsx, ActionButtons.tsx, AINewsRewriter.tsx, EventoWizard.tsx, banners/BannerCard.tsx, banners/BannerList.tsx, banners/BannerFilters.tsx, banners/BannerForm.tsx (750+ linhas), banners/BannerWizard.tsx
Hooks principais
/hooks/useBanners.ts— Carregar banners com cache/hooks/useBannersAdmin.ts— Admin de banners/hooks/useBannerFilters.ts— Filtros de banner/hooks/useBannerValidation.ts— Validação/hooks/useAnalytics.ts— Analytics/hooks/useAnalyticsDashboard.ts— Dashboard analytics/hooks/useImageOptimization.ts— Otimização de imagem/src/hooks/useBannerAnalytics.ts— Analytics de banners
Libs utilitárias
lib/prisma.ts— PrismaClient singletonlib/supabase.ts— Clientes Supabase (browser + server)lib/logger.ts— Logging estruturado (info, warn, error, debug, middleware, api, auth)lib/auth-helpers.ts— Refresh tokenlib/email.ts— Envio de email com Nodemailerlib/formatters.ts— Formatação de data/moedalib/api/withAdminAuth.ts— Middleware de autenticação adminlib/api/rateLimit.ts— Rate limitinglib/banners/catalog.ts— Catálogo centralizado de posiçõeslib/banners/utils.ts— Utilitários (status de agendamento, etc.)lib/banners/helpers.ts— Helperslib/banners/validation.ts— Schemas Zodlib/banners/BannerTemplateRegistry.ts— Registry de templateslib/constants/categorias.ts— Categorias válidaslib/images/cropImage.ts— Crop de imagem
Categorias
Notícias (22)
Cidade, Policial, Política, Economia, Esportes, Cultura, Saúde, Educação, Tecnologia, Meio Ambiente, Trânsito, Infraestrutura, Agricultura, Turismo, Social, Justiça, Eventos, Negócios, Ciência, Lazer, Religião, Outras
Empresas
Restaurante, Automotivo, Saude, Alimentacao, Beleza, Tecnologia, Comercio, Servicos, Educacao, Imoveis
Problemas conhecidos
Segurança (críticos)
- RLS desabilitado em
audit_logs— CRÍTICO, requer correção manual - Function search path vulnerável — 3 alerts pendentes
- Proteção vazada não habilitada no Supabase
- MFA insuficiente — apenas básico
- Postgres versão vulnerável — precisa upgrade
- Dev bypass em produção —
NEXT_PUBLIC_ADMIN_DEV_BYPASSpode ser perigoso - APIs sem
withAdminAuth— algumas ainda usam auth manual
Qualidade de código
- Arquivos grandes:
noticias.tsx(858),banners.tsx(615),temas-sazonais.tsx(675),empresas/gerenciar.tsx(660) - Código morto: arquivos
.disabled.tsxe.bak.tsx - Auth misto: Supabase Auth + NextAuth.js instalados
- Type safety:
src/types/supabase.tsusatype Database = any - Código duplicado:
/hookse/src/hookstêm arquivos similares - 99 migrations no Supabase — schema evoluiu iterativamente
Convenções de código
- Pages Router (não App Router, apesar de
/src/appexistir) - Server-side:
getServerSidePropscomcreateServerSupabaseClient() - Client-side: hooks custom +
supabase.from() - Form validation: Zod + React Hook Form com
@hookform/resolvers/zod - API auth:
withAdminAuth()wrapper para admin - Logger estruturado:
lib/logger.ts(info, warn, error, debug, middleware, api, auth) - Notificações:
react-hot-toastviaToastProvider - Ícones: Lucide React (admin), Font Awesome CDN (público)
- Imagens: Supabase Storage, fallback placeholder em erro
- Tailwind para estilos, sem CSS modules
- Mobile-first responsivo
Documentação relevante
docs/BANNERS-README.md— Sistema de banners (132 linhas)docs/IMPLEMENTACAO-BANNERS-GUIDE.md— Guia completo (846 linhas)docs/MAPA-BANNERS-SITE.md— Mapa de posições (328 linhas)docs/GOOGLE-FORMS-INTEGRATION.md— Webhook (311 linhas)docs/GUIA-MIGRACAO-SUPABASE.md— Migração (564 linhas)docs/VERCEL-DEPLOY.md— Deploydocs/SECURITY-AUTH-CONFIG.md— Segurança.agent/workflows/add-banner-position.md— Workflow para adicionar posição de banner