Portal web desenvolvido para a The WHO Traditional Medicine Global Library (TMGL) em parceria com a BIREME. Plataforma Next.js que consome conteúdo gerenciado no WordPress.
Este projeto é um portal web moderno e responsivo que serve como biblioteca digital global para medicina tradicional. O frontend consome conteúdo de múltiplas fontes (WordPress CMS, APIs externas) e apresenta informações organizadas por regiões, países, dimensões temáticas e recursos especializados.
- 🌍 Multi-regional: Suporte para diferentes regiões e países
- 🌐 Multi-idioma: Sistema de internacionalização (em desenvolvimento)
- 📱 Responsivo: Design adaptável para todos os dispositivos
- 🔍 Busca Avançada: Sistema de busca e filtros para recursos bibliográficos
- 📊 Diversos Formatos: Suporte para PDFs, vídeos, multimídia, RSS feeds
- 🎨 UI Moderna: Interface construída com Mantine UI
- Tecnologias
- Bibliotecas Principais
- Integrações
- Estrutura do Projeto
- Como Executar
- Funcionalidades Principais
- Documentação de Componentes
- Documentação de Páginas e Rotas
- Documentação de Services e APIs
- Documentação de WordPress: Posts, CPTs e Custom Fields
- Arquitetura do Sistema
- Documentação Adicional
Ver detalhes
- TypeScript - Linguagem de programação
- Next.js 14.2.4 - Framework React para produção
- React 18 - Biblioteca UI
- Node.js - Runtime JavaScript
- SASS/SCSS - Pré-processador CSS
- Mantine UI v7 - Biblioteca de componentes React
- PostCSS - Processamento de CSS
- CSS Modules - Estilos com escopo
Ver detalhes
@mantine/core- Componentes base do Mantine@mantine/carousel- Carrossel de imagens/conteúdo@mantine/dates- Seletores de data@mantine/form- Gerenciamento de formulários@mantine/hooks- Hooks utilitários@mantine/modals- Sistema de modais@tabler/icons-react- Ícones SVGreact-slick/slick-carousel- Carrosséis adicionaisreact-slideshow-image- Slideshows de imagensreact-background-slider- Slider de fundo
swr- Data fetching e cacheaxios- Cliente HTTPreact-hotjar- Analytics e heatmaps
pdfjs-dist- Renderização de PDFs no navegadorpdf-lib- Manipulação de PDFspdf-poppler- Conversão de PDF para imagenspdf2pic- Conversão PDF para imagempdf-thumbnail- Geração de thumbnails de PDFspuppeteer- Automação de navegador (para PDFs)
dayjs/moment- Manipulação de datascrypto-js- Criptografiaspark-md5- Hash MD5js-cookie- Gerenciamento de cookieshe- Decodificação HTML entitieszod- Validação de schemas TypeScriptrss-parser- Parsing de feeds RSSxml2js- Conversão XML para JSON
sharp- Processamento de imagenscanvas- Renderização de canvas@coveops/vimeo-thumbnail- Thumbnails do Vimeo
@stoddabr/react-tableau-embed-live- Embed de dashboards Tableau
Ver detalhes
- WordPress REST API - Gerenciamento de conteúdo principal
- Posts, páginas, mídia
- Taxonomias e categorias
- Menus e configurações globais
- DIREV API - Base de dados bibliográficos
- LIS API - Literatura em Saúde
- Journals API - Catálogo de periódicos
- Multimedia API - Recursos multimídia
- Evidence Maps API - Mapas de evidências
- Regulations and Policies API - Legislações e políticas
- Mailchimp - Newsletter e email marketing
- Hotjar - Analytics e comportamento do usuário
- RSS Feeds - Agregação de conteúdo externo
- Geração de thumbnails de PDFs
- Conversão de documentos
- Proxy de PDFs para visualização segura
- Processamento de vídeos e imagens
Ver detalhes
src/
├── components/ # Componentes React reutilizáveis
│ ├── layout/ # Header, Footer, Skip Links
│ ├── sections/ # Blocos de conteúdo (Hero, News, etc.)
│ ├── feed/ # Renderizadores de listas e feeds
│ ├── forms/ # Formulários de busca e filtros
│ └── ...
├── pages/ # Rotas Next.js (App Router)
│ ├── api/ # API Routes (proxies e utilitários)
│ └── [rotas dinâmicas]
├── services/ # Clientes de API e serviços
│ ├── apiRepositories/ # Serviços de APIs externas
│ ├── globalConfig/ # Configurações globais
│ └── ...
├── contexts/ # React Contexts (estado global)
├── helpers/ # Funções utilitárias
├── styles/ # Estilos globais e temas
└── types/ # Definições TypeScript
Ver detalhes
- Node.js 18+
- npm, yarn ou bun
# Instalar dependências
npm install
# ou
yarn install
# ou
bun installConfigure as seguintes variáveis de ambiente (crie um arquivo .env.local):
# URLs Base
NEXT_PUBLIC_BASE_URL=
NEXT_PUBLIC_API_BASE_URL=
BASE_URL=
WP_BASE_URL=
# APIs Externas
DIREV_API_KEY=
DIREV_API_URL=
LIS_API_URL=
Journals_API_URL=
MULTIMEDIA_API_URL=
# Mailchimp
MAILCHIMP_API_KEY=
MAILCHIMP_LIST_ID=
MAILCHIMP_DATA_CENTER=
# Outros
RSS_FEED_URL=
SECRET=
POSTSPERPAGE=
BASE_SEARCH_URL=
FIADMIN_URL=
PRODUCTION=falsenpm run dev
# ou
yarn dev
# ou
bun devA aplicação estará disponível em http://localhost:3000
npm run build
npm run startnpm run dev- Inicia servidor de desenvolvimentonpm run build- Cria build de produçãonpm run start- Inicia servidor de produçãonpm run lint- Executa ESLintnpm run generate-pdf- Gera PDFs de documentaçãonpm run generate-pdf:manual- Gera PDF do manual técniconpm run generate-pdf:sitemap- Gera PDF do mapa do site
Ver detalhes
- Páginas Regionais: Conteúdo específico por região e país
- Dimensões Temáticas: Organização por temas de medicina tradicional
- Biblioteca de Recursos: Busca e filtros avançados
- Multimídia: Galeria de vídeos, imagens e documentos
- Notícias e Eventos: Feed de notícias e calendário de eventos
- Periódicos: Catálogo de revistas científicas
- Mapas de Evidências: Visualização de evidências científicas
- Newsletter: Sistema de inscrição via Mailchimp
- RSS Feeds: Agregação de conteúdo externo
Ver componentes de Layout
Localização: src/components/layout/header.tsx
Descrição: Componente principal de cabeçalho do site com menu de navegação responsivo e mega menu.
Atributos: Nenhum (componente sem props)
Integrações:
- WordPress REST API (
MenusApi): Busca menus "global-menu" e "regional-menu" - GlobalContext: Acessa
regionNameecountryNamepara exibição no logo
Funcionalidades:
- Menu responsivo com hamburger para mobile
- Mega menu com navegação em 3 níveis
- Scroll detection para alterar estilo do header
- Suporte a navegação por teclado (acessibilidade)
- Menu regional e global configuráveis via WordPress
Dependências:
@mantine/core(Container, Flex, Grid, Button)@tabler/icons-react(ícones)next/router(navegação)
Localização: src/components/layout/footer.tsx
Descrição: Rodapé do site com links organizados em colunas e imagens de parceiros.
Atributos: Nenhum (componente sem props)
Integrações:
- WordPress REST API (
MenusApi): Busca menus "footer-left", "footer-right", "footer-center" - GlobalContext: Acessa
globalConfigpara imagens do footer e URL de termos
Funcionalidades:
- Renderização de menus hierárquicos do WordPress
- Exibição de imagens de parceiros (BIREME, WHO)
- Link para política de privacidade e termos
- Layout responsivo com Grid do Mantine
Localização: src/components/layout/skip-link.tsx
Descrição: Link de acessibilidade para pular para o conteúdo principal.
Atributos: Nenhum (componente sem props)
Funcionalidades:
- Link invisível que aparece no foco do teclado
- Navegação rápida para
#main-content - Melhora acessibilidade para leitores de tela
Localização: src/components/layout/helper.ts
Descrição: Função utilitária para dividir arrays em chunks.
Funções:
chunkArray<T>(arr: T[], size: number): T[][]- Divide array em sub-arrays de tamanho especificado
Ver componentes de Breadcrumbs
Localização: src/components/breadcrumbs/index.tsx
Descrição: Componente de navegação breadcrumb para mostrar o caminho da página atual.
Atributos:
path: Array<pathItem>- Array de itens do breadcrumb (obrigatório)pathItem:{ name: string, path: string }
blackColor?: boolean- Aplica estilo de cor preta (opcional)
Funcionalidades:
- Renderiza links navegáveis com separador ">"
- Limita nomes a 40 caracteres com "..."
- Remove tags HTML dos nomes
- Substitui hífens por espaços
Dependências:
@helpers/stringhelper(removeHTMLTagsAndLimit)
Ver componentes de Cards
Localização: src/components/cards/index.tsx
Descrição: Card com ícone e título, usado para navegação visual.
Atributos:
title: string- Título do card (obrigatório)icon: ReactNode- Ícone ou elemento React (obrigatório)callBack?: Function- Função executada ao clicar (opcional)small?: boolean- Aplica estilo reduzido (opcional)
Funcionalidades:
- Layout flexível com ícone centralizado
- Callback opcional ao clicar
- Suporte a tamanho pequeno
Localização: src/components/cards/index.tsx
Descrição: Card com imagem e título.
Atributos:
title: string- Título do card (obrigatório)icon: ReactNode- Imagem ou elemento React (obrigatório)callBack?: Function- Função executada ao clicar (opcional)small?: boolean- Aplica estilo reduzido (opcional)
Funcionalidades:
- Similar ao IconCard mas otimizado para imagens
- Imagem ocupa largura total do card
Ver componentes de Categories
Localização: src/components/categories/index.tsx
Descrição: Exibe badges coloridos para categorias.
Atributos:
categories?: Array<String>- Array de nomes de categorias (opcional)
Funcionalidades:
- Filtra categoria "Uncategorized"
- Aplica cores rotativas do helper
colors - Usa Badge do Mantine
Dependências:
@helpers/colors(array de cores)
Localização: src/components/categories/index.tsx
Descrição: Wrapper para exibir badges de taxonomias customizadas.
Atributos:
names: Array<string>- Array de nomes de taxonomia (obrigatório)
Funcionalidades:
- Reutiliza
CategorieBadgeinternamente
Ver componentes de Embed
Localização: src/components/embed/EmbedIframe.tsx
Descrição: Componente iframe com suporte a redimensionamento automático via postMessage.
Atributos:
src: string- URL do iframe (obrigatório)width?: string | number- Largura (padrão: "100%")height?: string | number- Altura inicial (padrão: 600)className?: string- Classe CSS adicional (opcional)style?: React.CSSProperties- Estilos inline (opcional)onResize?: (height: number) => void- Callback quando altura muda (opcional)
Funcionalidades:
- Escuta mensagens postMessage do tipo "resize"
- Ajusta altura automaticamente (+200px de margem)
- Suporte a fullscreen
Uso: Ideal para embeds de dashboards Tableau ou outras aplicações que enviam eventos de resize.
Ver componentes de Forms
Localização: src/components/forms/search/index.tsx
Descrição: Formulário de busca principal do site.
Atributos:
title?: string- Título do formulário (opcional)subtitle?: string- Subtítulo (opcional)small?: boolean- Aplica estilo reduzido (opcional)
Integrações:
- Variável de Ambiente:
BASE_SEARCH_URL- URL base da busca externa
Funcionalidades:
- Redireciona para URL de busca externa com query string
- Links para busca avançada e Subject Headings
- Suporte a Enter para submeter
- Layout responsivo
Dependências:
next/router(navegação)
Localização: src/components/forms/filters/index.tsx
Descrição: Formulário de filtros para posts (atualmente com funcionalidade comentada).
Atributos:
onSubmit: (regions?: number[]) => void- Callback ao submeter (obrigatório)
Integrações:
- WordPress REST API (
PostsApi): Busca taxonomias "tm-dimension", "country", "region"
Funcionalidades:
- Carrega taxonomias do WordPress
- Formulário com checkboxes (comentado no código)
- Botão "Apply" para aplicar filtros
Status: Funcionalidade principal está comentada, apenas estrutura visível.
Localização: src/components/forms/filters/index.tsx
Descrição: Formulário de busca para tópicos em destaque.
Atributos:
queryString: string- String de busca atual (obrigatório)setQueryString: (queryString: string) => void- Função para atualizar busca (obrigatório)
Funcionalidades:
- Campo de busca simples
- Submete query string via callback
- Usa
@mantine/formpara gerenciamento
Ver componentes de Feed
Localização: src/components/feed/index.tsx
Descrição: Seção genérica de feed de posts do WordPress.
Atributos:
postType: string- Tipo de post a buscar (obrigatório, ex: "news", "events")
Integrações:
- WordPress REST API (
PostsApi): Busca posts customizados - FiltersForm: Integração com filtros de região
Funcionalidades:
- Carrega 9 posts do tipo especificado
- Renderiza lista de
PostItem - Suporte a filtros por região
- Loading overlay durante carregamento
Localização: src/components/feed/index.tsx
Descrição: Feed de tópicos em destaque via RSS.
Atributos:
filter?: string- Filtro RSS opcional
Integrações:
- RSS Feed Service (
FetchRSSFeed): Busca artigos de feed RSS - GlobalContext: Acessa
globalConfigpara filtro padrão
Funcionalidades:
- Carrega artigos de feed RSS
- Suporte a busca por query string
- Botão "Load more" para carregar mais itens (+9)
- Integração com
TrendingTopicsFiltersForm
Localização: src/components/feed/post/postItem.tsx
Descrição: Item individual de post no feed.
Atributos:
title: string- Título do post (obrigatório)excerpt: string- Resumo/excerpt (obrigatório)href: string- URL de destino (obrigatório)thumbnail: string- URL da imagem (obrigatório)tags?: Array<string>- Array de tags (opcional)
Funcionalidades:
- Card clicável que navega para href
- Exibe thumbnail como background
- Renderiza excerpt como HTML
- Badges coloridos para tags
Dependências:
next/router(navegação)
Localização: src/components/feed/pagination/index.tsx
Descrição: Componente de paginação numérica.
Atributos:
currentIndex: number- Índice atual (baseado em 0) (obrigatório)totalPages: number- Total de páginas (obrigatório)callBack: Dispatch<SetStateAction<number>>- Função para mudar página (obrigatório)
Funcionalidades:
- Botões Prev/Next desabilitados nas extremidades
- Lista de números de página (máximo 100)
- Página atual destacada
- Índice interno baseado em 0, exibição baseada em 1
Localização: src/components/multitab/index.tsx
Descrição: Sistema de tabs aninhadas com conteúdo HTML.
Atributos:
props: MultTabItems[]- Array de itens de tab (obrigatório)MultTabItems:{ tab_name: string, tab_items?: Array<{ item_name: string, item_content: string }> }
Funcionalidades:
- Tabs horizontais principais
- Tabs verticais internas (se houver tab_items)
- Renderiza conteúdo HTML via
dangerouslySetInnerHTML - Suporte a múltiplos níveis de navegação
Dependências:
@mantine/core(Tabs)
Localização: src/components/multitab/disclaimer.tsx
Descrição: Tabs com conteúdo de disclaimer/termos de uso.
Atributos: Nenhum (conteúdo hardcoded)
Funcionalidades:
- 12 seções de disclaimer pré-definidas
- Tabs verticais em desktop, horizontais em mobile
- Conteúdo sobre licença CC BY-NC-SA 3.0 IGO
- Informações sobre uso, copyright, responsabilidade
Dependências:
@mantine/core(Container, Grid, Tabs)@mantine/hooks(useMediaQuery para responsividade)
Localização: src/components/multitab/dimension.tsx
Descrição: Tabs geradas dinamicamente a partir de HTML com tags <h3>.
Atributos:
content: string- HTML content com tags<h3 class="wp-block-heading">(obrigatório)
Funcionalidades:
- Parse HTML para extrair seções por
<h3> - Cria tabs dinamicamente a partir dos títulos
- Renderiza conteúdo HTML de cada seção
Uso: Ideal para conteúdo WordPress formatado com blocos de cabeçalho.
Ver componentes de PDF View
Localização: src/components/pdfview/index.tsx
Descrição: Hook React para renderizar primeira página de PDF em canvas.
Parâmetros:
pdfUrl: string- URL do PDF (obrigatório)
Retorno: RefObject<HTMLCanvasElement> - Referência ao canvas
Integrações:
- pdfjs-dist: Biblioteca para renderização de PDFs
Funcionalidades:
- Carrega PDF via URL
- Renderiza primeira página em canvas HTML5
- Escala 1:1 (scale: 1)
- Auto-ajusta dimensões do canvas
Dependências:
pdfjs-dist(PDF.js)- Worker configurado via
pdfjs-dist/build/pdf.worker.min.js
Ver componentes de Share
Localização: src/components/share/index.tsx
Descrição: Modal para compartilhamento em redes sociais.
Atributos:
link: string- URL para compartilhar (obrigatório)open: boolean- Estado de abertura (obrigatório)setOpen: (open: boolean) => void- Função para controlar abertura (obrigatório)
Funcionalidades:
- Ícones para Facebook, Twitter/X, LinkedIn
- Abre URLs de compartilhamento em nova aba
- Modal centralizado do Mantine
- Sem botão de fechar (usa overlay)
Dependências:
@mantine/core(Modal, Flex)@tabler/icons-react(ícones sociais)next/router(navegação)
Ver componentes de Slider
Ver detalhes do HeroSlider
Localização: src/components/slider/index.tsx
Descrição: Slider de imagens com fade para hero section.
Atributos:
images: Array<AcfImageArray | undefined>- Array de imagens (obrigatório)AcfImageArray:{ url: string }
Funcionalidades:
- Fade automático entre imagens
- Duração de 5 segundos por slide
- Sem setas ou indicadores
- Background images com CSS
Dependências:
react-slideshow-image(Fade component)
Localização: src/components/slider/index.tsx
Descrição: Componente para exibir uma única imagem de hero.
Atributos:
image: string- URL da imagem (obrigatório)
Funcionalidades:
- Background image simples
- Estilo de hero section
Ver componentes de Video
Localização: src/components/video/index.tsx
Descrição: Seção com vídeo de fundo em loop.
Atributos:
children: ReactNode- Conteúdo sobreposto ao vídeo (obrigatório)
Funcionalidades:
- Vídeo de fundo (
/local/video/bg.mp4) - Auto-play, loop, muted
- Conteúdo sobreposto via children
Localização: src/components/video/index.tsx
Descrição: Seção similar ao VideoSection mas sem vídeo.
Atributos:
children: ReactNode- Conteúdo da seção (obrigatório)
Funcionalidades:
- Layout similar ao VideoSection
- Apenas conteúdo, sem vídeo de fundo
Ver componentes de Videos
Localização: src/components/videos/index.tsx
Descrição: Item individual de vídeo/multimídia.
Atributos:
title: string- Título do vídeo (obrigatório)href: string- URL de destino (obrigatório)thumbnail: string- URL da thumbnail (obrigatório)main?: boolean- Se é o vídeo principal (opcional)
Funcionalidades:
- Detecta se thumbnail é PDF e renderiza iframe
- Layout responsivo (row em desktop, column em mobile)
- Altura ajustável (100% se main, 47% se não)
Dependências:
IframeThumbNail(para PDFs)
Localização: src/components/videos/index.tsx
Descrição: Seção fixa de vídeos relacionados com lista pré-definida.
Atributos:
items: VideoItemProps[]- Array de itens de vídeo (obrigatório)title?: string- Título da seção (opcional, padrão: "Related videos")backgroundColor?: string- Cor de fundo (opcional)
Funcionalidades:
- Layout com vídeo principal grande e 2 laterais
- Suporte a 1-3 vídeos
- Container responsivo
Localização: src/components/videos/index.tsx
Descrição: Seção de vídeos relacionados carregados da API.
Atributos:
filter?: string- Filtro opcional (não utilizado atualmente)
Integrações:
- Multimedia API (
MultimediaService): Busca 3 recursos multimídia - GlobalContext: Acessa
languagepara tradução
Funcionalidades:
- Carrega 3 itens da API de multimídia
- Suporte a múltiplos idiomas (title vs title_translated)
- Layout similar ao FixedRelatedVideosSection
Localização: src/components/videos/index.tsx
Descrição: Seção de itens multimídia recentes com filtros.
Atributos:
filter?: queryType[]- Array de filtros de query (opcional)
Integrações:
- Multimedia API (
MultimediaService): Busca recursos com filtros - GlobalContext: Acessa
language
Funcionalidades:
- Carrega 3 itens com filtros opcionais
- Suporte a tradução
- Título "Related media"
Ver componentes de GPT
Localização: src/components/gpt/index.tsx
Descrição: Widget flutuante para acessar ChatGPT customizado.
Atributos: Nenhum (componente sem props)
Funcionalidades:
- Botão flutuante fixo
- Abre ChatGPT customizado em nova aba
- URL hardcoded:
https://chatgpt.com/g/g-2rwZEsuEj-tmgl-gpt-the-integrative-public-health-model
Uso: Widget de assistente de IA para TMGL.
Ver componentes de RSS
Localização: src/components/rss/slider/index.tsx
Descrição: Carrossel horizontal de tópicos em destaque via RSS.
Atributos: Nenhum (componente sem props)
Integrações:
- RSS Feed Service (
FetchRSSFeed): Busca 10 artigos - GlobalContext: Acessa
globalConfigeregionNamepara filtros regionais
Funcionalidades:
- Carrossel com Embla Carousel
- Controles de navegação (prev/next)
- Slide size: 15%
- Filtro regional automático se
regionNamepresente - Loading state com Loader do Mantine
Dependências:
@mantine/carousel(Carousel)TrendingTopicSection(componente de card)
Localização: src/components/rss/slider/index.tsx
Descrição: Carrossel configurável de literatura recente.
Atributos:
title?: string- Título da seção (opcional, padrão: "Recent literature reviews")rssString?: string- Filtro RSS customizado (opcional)allFilter?: string- Filtro de país (opcional)exploreAllLabel?: string- Label do botão "Explore all" (opcional)
Integrações:
- RSS Feed Service (
FetchRSSFeed): Busca 10 artigos - GlobalContext: Acessa
globalConfigeregionName
Funcionalidades:
- Carrossel responsivo (100% mobile, 50% tablet, 32% desktop)
- Botão "Explore all" que navega para
/recent-literature-reviews - Suporte a filtros customizados
- 3 slides por scroll
Localização: src/components/rss/slider/index.tsx
Descrição: Card individual para tópico em destaque.
Atributos:
title: string- Título do tópico (obrigatório)excerpt: string- Resumo/excerpt (obrigatório)href: string- URL de destino (obrigatório)height?: string- Altura do card (opcional, padrão: undefined)
Funcionalidades:
- Card clicável com botão de ação
- Limita título a 140 caracteres
- Renderiza excerpt como HTML
- Botão com ícone de seta
Ver componentes de Sections
Localização: src/components/sections/hero/HeroSection.tsx
Descrição: Seção hero principal com slider, breadcrumbs e busca.
Atributos:
sliderImages?: AcfImageArray[] | string[]- Imagens do slider (opcional)breadcrumbs: BreadcrumbItem[]- Array de breadcrumbs (obrigatório)searchTitle?: string- Título do formulário de busca (opcional)searchSubtitle?: string- Subtítulo do formulário (opcional)small?: boolean- Estilo reduzido (padrão: false)blackColor?: boolean- Cor preta para breadcrumbs (padrão: false)className?: string- Classe CSS adicional (opcional)
Funcionalidades:
- Combina
HeroSlider,BreadCrumbseSearchForm - Container responsivo
- Suporte a múltiplos estilos
Dependências:
HeroSlider,BreadCrumbs,SearchForm
Localização: src/components/sections/index.tsx
Descrição: Seção de cards de dimensões temáticas.
Atributos:
items?: ItemResource[]- Array de itens customizados (opcional)- Se não fornecido, busca do WordPress
Integrações:
- WordPress REST API (
PostsApi): Busca 20 posts do tipo "dimensions"
Funcionalidades:
- Renderiza cards de dimensões
- Se
itemsfornecido, usa lista customizada - Caso contrário, busca do WordPress
- Resolve URLs (externas ou
/dimensions/{slug})
Dependências:
TraditionalSectionCard(componente interno)
Localização: src/components/sections/index.tsx
Descrição: Card individual para seção tradicional.
Atributos:
iconPath: string- URL do ícone/imagem (obrigatório)title: string- Título do card (obrigatório)target?: string- URL de destino (opcional, padrão: "/")sm?: boolean- Tamanho pequeno (opcional)
Funcionalidades:
- Card clicável que abre em nova aba
- Layout flexível com ícone e título
- Padding ajustável (20px se small, 40px se não)
Localização: src/components/sections/news/index.tsx
Descrição: Seção de notícias com cards em grid horizontal.
Atributos:
region?: string- Filtro por região (opcional)title?: string- Título da seção (opcional)posType?: string- Tipo de post customizado (opcional, ex: "events", "news")archive?: string- Slug do arquivo para link "Explore more" (opcional)includeDemo?: boolean- Incluir posts com tag "demo" (padrão: false)excludedTagIds?: number[]- IDs de tags para excluir (opcional)
Integrações:
- WordPress REST API (
PostsApi): Busca 4 posts- Se
posTypefornecido: busca posts do tipo customizado - Caso contrário: busca posts normais excluindo categoria "thematic-page"
- Se
- Tag "demo": Detecta e marca posts com tag demo
Funcionalidades:
- Carrega 4 posts
- Suporte a filtros por região e exclusão de tags
- Cards em layout horizontal responsivo
- Link "Explore more" ou "Explore archived news"
- Detecta tag "demo" e marca visualmente
Dependências:
ResourceCard(componente de card)moment(formatação de datas)
Localização: src/components/sections/newsletter/index.tsx
Descrição: Seção de newsletter com formulário de inscrição.
Atributos: Nenhum (componente sem props)
Integrações:
- Mailchimp API (
MailChimpService): Inscreve email na lista
Funcionalidades:
- Formulário de email com validação
- Loading overlay durante submissão
- Redireciona para
/subscriptionem sucesso - Tratamento de erros (email já cadastrado, etc.)
- Background image de newsletter
Dependências:
@mantine/form(validação de formulário)MailChimpService(integração com Mailchimp)
Como Funcionam os Filtros e Busca nos Feeds
Os feeds utilizam um sistema unificado de filtros baseado no tipo queryType:
interface queryType {
parameter: string; // Nome do parâmetro de filtro (ex: "region", "country", "tags")
query: string; // Valor do filtro (ex: "AFRO", "Ghana", "123")
}-
Inicialização: Filtros podem vir de:
- Parâmetros da URL (
?country=ghana®ion=afro) - Seleção do usuário nos checkboxes
- Busca por texto livre
- Parâmetros da URL (
-
Aplicação de Filtros:
- Filtros são convertidos para
queryType[] - Passados para o service correspondente
- Service aplica filtros na requisição à API
- Filtros são convertidos para
-
Filtros Dinâmicos:
- Gerados a partir dos resultados da API
- Mostram apenas valores que existem nos resultados
- Contagem de ocorrências por filtro
WordPress (NewsFeed, EventsFeed):
region: Filtra por taxonomia "region" (term ID)country: Filtra por taxonomia "country" (term ID)tags: Filtra por tags (term ID)after/before: Filtra por data de publicaçãosearch: Busca textual no título e conteúdo
APIs Externas (MultimediaFeed, EventsFeed via DIREV):
Region: Filtra por região (string)country: Filtra por país (string)descriptor: Filtra por área temática (string)year: Filtra por ano (string)resource_type: Filtra por tipo de recurso (string)title: Busca textual no título
Ver detalhes do NewsFeed
Localização: src/components/feed/news/index.tsx
Descrição: Feed completo de notícias com filtros e paginação.
Atributos:
displayType: string- Tipo de display: "column" ou "row" (obrigatório)country?: string- Filtro por país (opcional)region?: string- Filtro por região (opcional)thematicArea?: string- Filtro por área temática (slug) (opcional)
Integrações:
- WordPress REST API (
PostsApi): Lista posts com paginação - TaxonomiesApi: Converte slugs para term IDs
Como Funciona a Busca:
-
Filtros Iniciais da URL:
// Se URL contém ?country=ghana®ion=afro&thematicArea=herbal-medicine // Componente converte slugs para IDs: const tag = await api.getTagBySlug("herbal-medicine"); // → tag ID: 123 const filters = [ { parameter: "country", query: "ghana" }, { parameter: "region", query: "afro" }, { parameter: "tags", query: "123" } ];
-
Busca de Posts:
// PostsApi recebe filtros e constrói query WordPress const posts = await api.listPosts("posts", 12, page, filters); // Query gerada: /wp-json/wp/v2/posts?per_page=12&page=1&country=ghana&tags=123
-
Filtros Dinâmicos:
- Busca todos os posts (sem filtros) para gerar lista de filtros
- Extrai valores únicos de: regiões, países, tags, anos
- Exibe apenas filtros que existem nos resultados
-
Busca Textual:
// Usuário digita "traditional medicine" const filters = [ { parameter: "search", query: "traditional medicine" } ]; // WordPress busca em título e conteúdo
Funcionalidades:
- Paginação (12 itens por página, baseada em 1 na API, 0 no componente)
- Filtros dinâmicos gerados dos resultados
- Filtros iniciais via URL (conversão slug → ID)
- Grid responsivo (column ou row mode)
- Loading overlay fixo durante carregamento
- Contador de resultados: "Showing X of Y results found"
- Componente
ResourceFilterscom accordions colapsáveis
Dependências:
ResourceFilters(componente de filtros com checkboxes)ResourceCard(card de recurso)Pagination(componente de paginação)
Ver detalhes do MultimediaFeed
Localização: src/components/feed/multimedia/index.tsx
Descrição: Feed completo de recursos multimídia com filtros.
Atributos:
displayType: string- Tipo de display: "column" ou "row" (obrigatório)country?: string- Filtro por país (opcional)region?: string- Filtro por região (opcional)thematicArea?: string- Filtro por área temática (opcional)mediaType?: string- Filtro por tipo de mídia (opcional)
Integrações:
- Multimedia API (
MultimediaService): Busca recursos multimídia - GlobalContext: Acessa
languageeglobalConfig
Como Funciona a Busca:
-
Filtros Iniciais:
// Função initialFilters converte parâmetros URL para queryType[] initialFilters(applyFilters, setLoading, setInitialFilterDone, country, thematicArea, region, mediaType); // Gera: [{ parameter: "country", query: "Ghana" }, ...]
-
Busca na API:
// MultimediaService recebe filtros const response = await service.getDefaultResources( 12, // count page * 12, // start (offset) language, // "en", "es", etc. filters // queryType[] ); // Service faz POST para /api/multimedia com filtros
-
Filtros Dinâmicos da API:
- API retorna filtros disponíveis nos resultados:
{ countryFilter: [{ type: "Ghana", count: 5 }, ...], regionFilter: [{ type: "AFRO", count: 10 }, ...], thematicAreaFilter: [{ type: "Herbal Medicine", count: 3 }, ...], yearFilter: [{ type: "2023", count: 2 }, ...], resourceTypeFilter: [{ type: "Video", count: 8 }, ...] }
- Componente
DefaultFeedFilterComponentrenderiza filtros
- API retorna filtros disponíveis nos resultados:
-
Aplicação de Filtros:
// Usuário seleciona filtros nos checkboxes const selectedFilters = { "Region": ["AFRO"], "country": ["Ghana"], "descriptor": ["Herbal Medicine"] }; // Converte para queryType[] const queryItems = [ { parameter: "Region", query: "AFRO" }, { parameter: "country", query: "Ghana" }, { parameter: "descriptor", query: "Herbal Medicine" } ]; // Aplica filtros e busca novamente
Funcionalidades:
- Paginação (12 itens por página, offset-based)
- Filtros dinâmicos da API (apenas valores existentes)
- Suporte a múltiplos idiomas (title vs title_translated)
- Filtros iniciais via URL
- Grid responsivo
- Tags: país, região, área temática
- Links abrem em nova aba (
target="_blank")
Dependências:
DefaultFeedFilterComponent(filtros específicos de multimídia)ResourceCard(card de recurso)Pagination(componente de paginação)
Localização: src/components/feed/multimedia/index.tsx
Descrição: Feed completo de recursos multimídia com filtros.
Atributos:
displayType: string- Tipo de display: "column" ou "row" (obrigatório)country?: string- Filtro por país (opcional)region?: string- Filtro por região (opcional)thematicArea?: string- Filtro por área temática (opcional)mediaType?: string- Filtro por tipo de mídia (opcional)
Integrações:
- Multimedia API (
MultimediaService): Busca recursos multimídia - GlobalContext: Acessa
languageeglobalConfig
Funcionalidades:
- Paginação (12 itens por página)
- Filtros dinâmicos da API de multimídia
- Suporte a múltiplos idiomas
- Filtros iniciais via URL
- Grid responsivo
- Tags: país, região, área temática
- Links abrem em nova aba (
target="_blank")
Dependências:
DefaultFeedFilterComponent(filtros específicos de multimídia)ResourceCard(card de recurso)Pagination(componente de paginação)
Nota: Outros componentes de sections (PageHeaderSection, ContentSection, ResourcesSection, NewsEventsSection, MultimediaSection, JournalsSection, FundingOpportunitiesSection, PeriodicalsSection, etc.) seguem padrões similares de integração com WordPress REST API e APIs externas. Consulte os arquivos individuais em src/components/sections/ para detalhes específicos.
Nota: Outros feeds (EventsFeed, StoriesFeed, DatabaseAndRepositoriesFeed, JournalsFeed, etc.) seguem padrões similares aos feeds documentados acima, adaptados para seus respectivos tipos de conteúdo e APIs.
Ver detalhes sobre Rotas e Páginas
O projeto utiliza o sistema de roteamento do Next.js baseado em arquivos na pasta src/pages/. Cada arquivo/pasta representa uma rota na aplicação.
Arquivo: src/pages/index.tsx
Descrição: Página inicial global do site.
Comportamento:
- Define
regionNamecomo vazio no contexto global - Busca propriedades da página "home-global" do WordPress
- Exibe slider de imagens, formulário de busca, dimensões temáticas, notícias, eventos e multimídia
- Título:
"Home - The WHO Traditional Medicine Global Library"
Logo e Título:
- Logo padrão sem região/country
- Título completo: "The WHO Traditional Medicine Global Library"
Busca de Conteúdo:
- Busca posts sem filtro de região
- Usa
PostsApisem parâmetro de região - Conteúdo global de todas as regiões
Arquivo: src/pages/[region]/index.tsx
Descrição: Página inicial de uma região específica (ex: /afro, /amro, /emro).
Parâmetros:
region: Slug da região (ex: "afro", "amro", "emro")
Comportamento:
- Valida se a região existe em
globalConfig.acf.regionais - Se região inválida, redireciona para
/404(exceto/enque redireciona para/) - Define
regionNameno contexto global - Busca propriedades da página "home" do WordPress com prefixo regional
- Título:
"{REGION} - The WHO Traditional Medicine Global Library"
Logo e Título:
- Logo exibe nome da região (ex: "AFRO", "AMRO")
- Título inclui prefixo da região em maiúsculas
- Subtítulo: "The WHO Traditional Medicine Global Library"
Busca de Conteúdo:
- WordPress:
PostsApié instanciado comregioncomo parâmetro- Base URL muda para:
{region}/wp-json/wp/v2/ - Busca posts específicos da região via taxonomia "region"
- Base URL muda para:
- RSS Feeds: Filtro regional aplicado via
globalConfig.acf.region_filters- Cada região tem um filtro RSS específico configurado
- Componentes: Recebem prop
regionque filtra conteúdo
Exemplo de URL: /afro, /amro, /emro
Arquivo: src/pages/[region]/[country]/[lang]/index.tsx
Descrição: Página inicial de um país específico dentro de uma região.
Parâmetros:
region: Slug da regiãocountry: Slug do paíslang: Idioma (atualmente não utilizado ativamente)
Comportamento:
- Valida região e país através de taxonomias do WordPress
- Define
regionNameecountryNameno contexto global - Busca post do tipo "country" com slug correspondente
- Carrega recursos específicos do país (bibliográficos, eventos, notícias)
- Título:
"{COUNTRY} - {REGION} - The WHO Traditional Medicine Global Library"
Logo e Título:
- Logo exibe nome do país
- Título inclui país e região
- Subtítulo: "The WHO Traditional Medicine Global Library"
Busca de Conteúdo:
- WordPress:
- Busca posts filtrados por taxonomia "country" e "region"
PostsApiusa prefixo regional:{region}/wp-json/wp/v2/
- DIREV API:
- Filtra recursos bibliográficos por país
- Converte país para term ID do WordPress
- Componentes:
- Recebem props
regionecountry - Filtram conteúdo específico do país
- Recebem props
Exemplo de URL: /afro/ghana/en, /amro/brazil/en
Arquivos: src/pages/news/index.tsx, src/pages/news/[slug].tsx
Comportamento:
- Lista e exibe posts do tipo "posts" do WordPress
- Filtros por região, país e área temática
- Busca via
PostsApicom filtros de taxonomia
Influência da Região:
- Se
regionNamepresente no contexto, filtra posts por região - Componente
NewsFeedrecebe propregione aplica filtro
Arquivos: src/pages/events/index.tsx, src/pages/events/[slug].tsx
Comportamento:
- Lista eventos do DIREV API e WordPress (tipo "event")
- Mescla resultados de ambas as fontes
- Filtros por região, país, modalidade, área temática
Influência da Região:
DireveServiceaplica filtros regionais na query- Posts do WordPress filtrados por taxonomia "region"
Arquivo: src/pages/multimedia/index.tsx
Comportamento:
- Lista recursos multimídia da Multimedia API
- Filtros por país, região, tipo de mídia, área temática
Influência da Região:
MultimediaServicerecebe filtros viaqueryType[]- Filtros regionais aplicados na API externa
Arquivos: src/pages/dimensions/index.tsx, src/pages/dimensions/[slug].tsx
Comportamento:
- Lista e exibe dimensões temáticas do WordPress
- Tipo de post customizado: "dimensions"
Influência da Região:
- Se região presente, busca dimensões específicas da região
PostsApiinstanciado com prefixo regional
Arquivo: src/pages/[region]/[...customRoute]/index.tsx
Descrição: Rota catch-all para páginas customizadas dentro de uma região.
Comportamento:
- Permite rotas dinâmicas como
/[region]/content/[slug] - Busca páginas do WordPress por slug
- Mantém contexto regional
-
Instanciação do Service:
// Sem região (global) const api = new PostsApi(); // Base URL: /wp-json/wp/v2/ // Com região const api = new PostsApi("afro"); // Base URL: /afro/wp-json/wp/v2/
-
Filtros por Taxonomia:
- Região: Filtra posts pela taxonomia "region" usando term ID
- País: Filtra posts pela taxonomia "country" usando term ID
- Conversão: Slug → Term ID via
TaxonomiesApi
-
Exemplo de Query:
// Busca posts da região AFRO const posts = await api.getCustomPost("posts", 10, 0, [regionTermId]); // Busca posts do país Gana na região AFRO const posts = await api.getCustomPost("posts", 10, 0, [regionTermId], undefined, { countryId: [countryTermId] });
-
Filtros via Query Parameters:
- Região e país convertidos para strings de filtro
- Adicionados ao array
queryType[]:[ { parameter: "region", query: "AFRO" }, { parameter: "country", query: "Ghana" } ]
-
RSS Feeds:
- Filtros regionais configurados em
globalConfig.acf.region_filters - Cada região tem um filtro RSS específico
- Aplicado automaticamente em componentes como
TrendingSlider
- Filtros regionais configurados em
O GlobalContext mantém:
regionName: Slug da região atual (ex: "afro")countryName: Nome do país atual (ex: "Ghana")globalConfig: Configurações globais do WordPresslanguage: Idioma atual
Componentes acessam o contexto para:
- Filtrar conteúdo automaticamente
- Exibir informações regionais no header
- Aplicar filtros padrão baseados na região
Ver detalhes sobre Services e APIs
O projeto utiliza uma arquitetura de proxy onde:
- Services (
src/services/): Clientes que fazem requisições HTTP - API Routes (
src/pages/api/): Endpoints Next.js que atuam como proxy
Problema: APIs externas requerem chaves de API que não devem ser expostas no cliente.
Solução: API Routes executam no servidor (Node.js), onde credenciais podem ser armazenadas em variáveis de ambiente.
Exemplo:
// ❌ NÃO FAZER (expõe API key no cliente)
// src/services/DireveService.ts
const apiKey = "secret-key"; // Exposto no bundle do cliente!
// ✅ CORRETO (API key no servidor)
// src/pages/api/direve.ts
const apiKey = decryptFromEnv(process.env.BVSALUD_API_KEY); // Seguro no servidorProblema: APIs externas podem ter restrições CORS que bloqueiam requisições diretas do navegador.
Solução: API Routes fazem requisições server-side, evitando problemas de CORS.
Problema: APIs externas podem retornar formatos diferentes ou inconsistentes.
Solução: API Routes podem transformar dados antes de enviar ao cliente.
Exemplo:
// src/pages/api/direve.ts
const response = await axios.get(externalApiUrl);
// Transforma dados aqui
return res.json({ data: transformData(response.data) });Problema: Múltiplas requisições do cliente podem sobrecarregar APIs externas.
Solução: API Routes podem implementar cache e rate limiting no servidor.
Problema: Requisições diretas do cliente dificultam monitoramento.
Solução: API Routes centralizam logs e métricas no servidor.
Cliente (Browser)
↓
Service (src/services/apiRepositories/DireveService.ts)
↓ axios.post("/api/direve", ...)
API Route (src/pages/api/direve.ts)
↓ axios.get(externalApiUrl, { headers: { apiKey } })
API Externa (BVSALUD, DIREV, etc.)
↓
API Route (transforma dados)
↓
Service (recebe dados normalizados)
↓
Componente (renderiza dados)
BaseUnauthenticatedApi: Classe base para serviços WordPress.
Services Principais:
PostsApi: Gerenciamento de posts, páginas e tipos customizadosPagesApi: Propriedades de páginas (ACF)MenusApi: Menus de navegaçãoTaxonomiesApi: Taxonomias (categorias, tags, países, regiões)MediaApi: Mídia e imagensSettingsApi: Configurações do site
Características:
- Herdam de
BaseUnauthenticatedApi - Suportam prefixo regional:
new PostsApi("afro") - Base URL:
{region}/wp-json/wp/v2/ou/wp-json/wp/v2/
Exemplo:
// Service WordPress
const api = new PostsApi("afro");
const posts = await api.getCustomPost("posts", 10, 0, [regionId]);
// Faz requisição para: /afro/wp-json/wp/v2/posts?per_page=10®ion=[id]Services Principais:
DireveService: Eventos e recursos bibliográficosMultimediaService: Recursos multimídiaJournalsService: Periódicos científicosLisService: Literatura em SaúdeEvidenceMapsService: Mapas de evidênciasRegulationAndPolices: Legislações e políticas
Características:
- Fazem requisições para API Routes Next.js (
/api/*) - Não acessam APIs externas diretamente
- Normalizam dados para formato comum (
DefaultResourceDto)
Exemplo:
// Service de API Externa
const service = new DireveService();
const resources = await service.getDireveResources(10, 0, "en", filters);
// Faz requisição POST para: /api/direve
// API Route faz requisição real para API externaAPI Routes Principais:
direve.ts: Proxy para API DIREV (eventos)multimedia.ts: Proxy para API de Multimídiajournals.ts: Proxy para API de Periódicoslis.ts: Proxy para API LISevidencemaps.ts: Proxy para API de Mapas de Evidênciaslegislations.ts: Proxy para API de Legislaçõesbibliographic.ts: Proxy para API Bibliográficarssfeed.ts: Proxy para feeds RSSsubscribe.ts: Integração com Mailchimpproxy-pdf.ts: Proxy para PDFs externospdf-image.ts: Geração de thumbnails de PDFsvideo-thumbnail.ts: Geração de thumbnails de vídeoscheck-thumbnails.ts: Verificação de thumbnails
Exemplo: direve.ts:
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
// 1. Validação de método HTTP
if (req.method !== "POST") {
return res.status(405).json({ message: "Method not permitted" });
}
// 2. Extração de credenciais (servidor apenas)
const apiKey = decryptFromEnv(process.env.BVSALUD_API_KEY);
// 3. Construção da URL da API externa
const url = `${process.env.BVSALUD_URL}event/v1/search/?fq=${query}&count=${count}`;
// 4. Requisição para API externa (server-side)
const response = await axios.get(url, { headers: { apiKey } });
// 5. Retorno de dados (opcionalmente transformados)
return res.status(200).json({ data: response.data, status: true });
}Características Comuns:
- Validação de método HTTP
- Decriptografia de credenciais via
decryptFromEnv - Headers de segurança (
X-Frame-Options) - Tratamento de erros
- Transformação de dados quando necessário
public getDireveResources = async (
count: number,
start: number,
lang: string,
queryItems?: Array<queryType>
): Promise<DefaultResourceDto> => {
// Constrói query string
const query = `thematic_area:"TMGL"&${queryItems.map(...).join("&")}`;
// Faz requisição para API Route (não API externa diretamente)
const { data } = await axios.post<RepositoryApiResponse>("/api/direve", {
query,
count,
start,
q: "*:*",
lang,
});
// Transforma dados da API externa para formato interno
return {
data: data.data.diaServerResponse[0].response.docs.map(...),
totalFound: data.data.diaServerResponse[0].response.numFound,
// ... filtros
};
};export default async function handler(req: NextApiRequest, res: NextApiResponse) {
// Recebe dados do Service
const { query, count, start, q, lang } = req.body;
// Decripta API key (seguro no servidor)
const apiKey = decryptFromEnv(process.env.BVSALUD_API_KEY);
// Faz requisição para API externa (server-side)
const url = `${process.env.BVSALUD_URL}event/v1/search/?fq=${query}&count=${count}`;
const response = await axios.get(url, { headers: { apiKey } });
// Retorna dados para o Service
return res.status(200).json({ data: response.data, status: true });
}- Recebe requisição do servidor Next.js
- Retorna dados em formato próprio
- API key validada no servidor
- Segurança: Credenciais nunca expostas no cliente
- Flexibilidade: Transformação de dados centralizada
- Manutenibilidade: Mudanças na API externa afetam apenas API Route
- Performance: Cache e rate limiting no servidor
- Monitoramento: Logs centralizados
- CORS: Sem problemas de CORS (requisições server-side)
Ver detalhes sobre WordPress Integration
O projeto utiliza uma arquitetura WordPress Multi-Site onde cada região possui seu próprio subsite:
WordPress Multi-Site
├── Site Global (/) - Conteúdo global
├── Site AFRO (/afro/) - Conteúdo da região africana
├── Site AMRO (/amro/) - Conteúdo da região americana
├── Site EMRO (/emro/) - Conteúdo da região do Mediterrâneo Oriental
├── Site EURO (/euro/) - Conteúdo da região europeia
├── Site SEARO (/searo/) - Conteúdo da região do Sudeste Asiático
└── Site WPRO (/wpro/) - Conteúdo da região do Pacífico Ocidental
Quando uma região é detectada na URL, o service é instanciado com o prefixo regional:
// Sem região (global)
const api = new PostsApi();
// Base URL: https://wp-site.com/wp-json/wp/v2/
// Com região AFRO
const api = new PostsApi("afro");
// Base URL: https://wp-site.com/afro/wp-json/wp/v2/Implementação (BaseUnauthenticatedApi):
public constructor(endpoint: string, region?: string) {
const baseUrl = `${process.env.WP_BASE_URL}/${region ? region + "/" : ""}${endpoint}`;
this._api = axios.create({ baseURL: baseUrl });
}Antes de buscar conteúdo, o sistema valida se a região existe:
// src/pages/[region]/index.tsx
const regionExists = globalConfig?.acf.regionais?.find(
(r) => r.rest_api_prefix.toLowerCase() == regionName.toLowerCase()
);
if (!regionExists) {
router.push("/404"); // Região inválida
}As regiões são configuradas no WordPress via globalConfig:
interface RegionalConfig {
rest_api_prefix: string; // "afro", "amro", etc.
region_name: string; // "AFRO", "AMRO", etc.
region_filter: string; // Filtro RSS específico
}O projeto utiliza vários Custom Post Types do WordPress para organizar diferentes tipos de conteúdo:
-
posts- Posts padrão do WordPress (notícias)- Endpoint:
/wp-json/wp/v2/posts - Regional:
/afro/wp-json/wp/v2/posts
- Endpoint:
-
pages- Páginas estáticas- Endpoint:
/wp-json/wp/v2/pages - Usado para: Home, About, etc.
- Endpoint:
-
event- Eventos- Endpoint:
/wp-json/wp/v2/event - Customizado para eventos da região
- Endpoint:
-
dimensions- Dimensões temáticas- Endpoint:
/wp-json/wp/v2/dimensions - Organiza temas de medicina tradicional
- Endpoint:
-
thematic-pages- Páginas temáticas- Endpoint:
/wp-json/wp/v2/thematic-pages - Páginas específicas por tema
- Endpoint:
-
country- Páginas de países- Endpoint:
/wp-json/wp/v2/country - Conteúdo específico de cada país
- Endpoint:
-
featured-stories- Histórias em destaque- Endpoint:
/wp-json/wp/v2/featured-stories - Stories destacadas
- Endpoint:
Método getCustomPost:
const posts = await api.getCustomPost(
"event", // postTypeSlug: tipo de post
10, // perPage: quantidade por página
0, // parent: ID do post pai (opcional)
[regionId], // region: IDs de região (opcional)
"afro", // regionString: slug da região (opcional)
{ // options: opções de filtro
tagId: [123],
excludeTag: true,
countryId: [456]
}
);Query Gerada:
/afro/wp-json/wp/v2/event?
per_page=10&
_embed&
orderby=date&
order=desc&
acf_format=standard&
region=1&
tags_exclude=123&
country=456&
lang=en
O projeto utiliza extensivamente ACF (Advanced Custom Fields) para adicionar campos customizados aos posts.
Todas as requisições incluem acf_format=standard para receber campos ACF:
// Query inclui acf_format=standard
const url = `${postTypeSlug}?slug=${slug}&_embed&acf_format=standard`;Exemplo: Post com ACF:
interface Post {
id: number;
title: { rendered: string };
content: { rendered: string };
acf: {
// Campos customizados variam por tipo de post
[key: string]: any;
};
}1. Home Page (home-global, home):
interface HomeAcf {
search: {
title: string;
subtitle: string;
slider_images: AcfImageArray[];
};
text_trending_topics: string;
events: {
title?: string;
subtitle?: string;
webcast?: string;
meeting?: string;
};
tmd: {
title: string;
subtitle: string;
dimensions?: any;
background_image: AcfImageArray;
};
itens: ItemResource[]; // Recursos/dimensões
manual_media: TmsItem[]; // Mídia manual
embed_content?: string; // Conteúdo embed
collaboration_network_items: AcfImageArray[];
}2. Country Page (country):
interface CountryAcfProps {
layout: string;
disclaimer?: string;
stories_url: string;
content: string;
slide_images: AcfImageArray[];
resources_title: string;
resources: CountryAcfResource[];
key_resources: KeyResource[];
embed_content: string;
rss_filter: string;
multimedia_filter: string;
manual_media: MediaItem[] | false;
tms_items: TmsItem[] | false;
events_title: string;
news_title: string;
// ... mais campos
}3. Thematic Page (thematic-pages):
interface ThematicPageAcfProps {
disclaimer?: string;
search: {
title: string;
subtitle: string;
slider_images: AcfImageArray[];
};
title: string;
content: string;
comunity_initiatives_title: string;
community_iniciatives: CommunityInitiative[];
similar_themes: SimilarTheme[];
news_tag_filter: string;
events_tag_filter: string;
multimedia_items: ACFMultimediaItem[];
rss_filter: string;
resources: ACFMultimediaItem[];
// ... mais campos
}4. Dimensions (dimensions):
interface DimensionsAcf {
content: string; // HTML com tabs
// Outros campos específicos
}5. Events (event):
interface EventsAcf {
title?: string;
subtitle?: string;
webcast?: string;
meeting?: string;
report?: string;
background?: AcfImageArray;
}O WordPress REST API suporta _embed para incluir dados relacionados:
// Query com _embed
const url = `${postTypeSlug}?_embed&acf_format=standard`;Dados Embedados:
wp:featuredmedia: Imagem destacadawp:term: Taxonomias (categorias, tags, países, regiões)author: Autor do postreplies: Comentários
Exemplo de Uso:
// Acessar featured image
const featuredImage = post._embedded?.["wp:featuredmedia"]?.[0];
const imageUrl = featuredImage?.media_details?.sizes?.medium?.source_url;
// Acessar taxonomias
const countries = post._embedded?.["wp:term"]
?.flat()
.filter(term => term.taxonomy === "country");
const regions = post._embedded?.["wp:term"]
?.flat()
.filter(term => term.taxonomy === "region");Taxonomias Customizadas:
region: Regiões da OMS (AFRO, AMRO, EMRO, etc.)country: Paísestm-dimension: Dimensões temáticaspost_tag: Tags padrão (áreas temáticas)category: Categorias padrão
Filtragem:
// Por região
const posts = await api.getCustomPost("posts", 10, 0, [regionId]);
// Por país
const posts = await api.getCustomPost("posts", 10, 0, undefined, undefined, {
countryId: [countryId]
});
// Por tag (excluir)
const posts = await api.getCustomPost("posts", 10, 0, undefined, undefined, {
tagId: [demoTagId],
excludeTag: true
});O WordPress utiliza o plugin WPML ou similar para traduções:
Estrutura de Traduções:
interface Post {
lang: string; // "en", "es", "fr", etc.
translations: {
[lang: string]: number; // ID do post traduzido
};
}Busca com Idioma:
// Busca post com idioma específico
const post = await api.getPost("posts", "slug-example", "en");
// Se post não está no idioma solicitado, busca tradução
if (post.lang !== "en" && post.translations["en"]) {
const translatedId = post.translations["en"];
const translated = await api.getPostById("posts", translatedId);
}Parâmetro lang na Query:
// Query inclui lang quando necessário
const url = `${postTypeSlug}?slug=${slug}&lang=${lang}&acf_format=standard`;Busca de Featured Image:
// Método helper
public findFeaturedMedia(post: Post, size?: string): string {
const fm = post?._embedded?.["wp:featuredmedia"]?.[0];
const sizes = fm?.media_details?.sizes;
// Retorna URL do tamanho solicitado
// Fallback: thumbnail → medium → large → full
return sizes?.[size]?.source_url || fm?.source_url || "";
}Tamanhos Disponíveis:
thumbnail: 150x150pxmedium: 300x300pxlarge: 1024x1024pxfull: Tamanho original
Uso:
const imageUrl = api.findFeaturedMedia(post, "medium");
// Retorna: "https://wp-site.com/wp-content/uploads/image-300x300.webp"Extração de Tags:
public formatTags(item: Post): TagItem[] {
// Extrai países
const countries = item._embedded?.["wp:term"]
?.flat()
.filter(term => term.taxonomy === "country");
// Extrai regiões
const regions = item._embedded?.["wp:term"]
?.flat()
.filter(term => term.taxonomy === "region");
// Extrai dimensões temáticas
const dimensions = item._embedded?.["wp:term"]
?.flat()
.filter(term => term.taxonomy === "tm-dimension");
// Combina todos
return [
...countries.map(c => ({ name: c.name, type: "country" })),
...regions.map(r => ({ name: r.name, type: "region" })),
...dimensions.map(d => ({ name: d.name, type: "descriptor" }))
];
}// 1. Detectar região da URL
const region = router.query.region; // "afro"
// 2. Instanciar API com prefixo regional
const api = new PostsApi(region); // Base URL: /afro/wp-json/wp/v2/
// 3. Buscar term ID da região (se necessário)
const taxApi = new TaxonomiesApi(region);
const regionTerms = await taxApi.getTaxonomies("region");
const afroTerm = regionTerms.find(t => t.slug === "afro");
const regionId = afroTerm.id; // Ex: 5
// 4. Buscar posts com filtro de região
const posts = await api.getCustomPost(
"posts", // CPT
10, // perPage
0, // parent
[regionId], // region filter
undefined, // regionString
{ // options
excludeTag: true,
tagId: [demoTagId]
}
);
// 5. Processar posts
posts.forEach(post => {
// Acessar ACF fields
const acfData = post.acf;
// Acessar featured image
const imageUrl = api.findFeaturedMedia(post, "medium");
// Extrair tags
const tags = api.formatTags(post);
// Acessar taxonomias
const countries = post._embedded?.["wp:term"]
?.flat()
.filter(t => t.taxonomy === "country");
});| Aspecto | Global (/) |
Regional (/afro/) |
|---|---|---|
| Base URL | /wp-json/wp/v2/ |
/afro/wp-json/wp/v2/ |
| Conteúdo | Todos os posts | Apenas posts da região |
| Filtros | Sempre aplicados | Filtros regionais automáticos |
| ACF Fields | home-global |
home (específico da região) |
| RSS Feeds | Filtro global | Filtro regional específico |
| Menus | global-menu |
regional-menu |
- Sempre usar
_embedpara incluir dados relacionados - Sempre usar
acf_format=standardpara campos ACF - Validar região antes de buscar conteúdo
- Usar term IDs ao invés de slugs para filtros
- Tratar traduções quando suporte multi-idioma estiver ativo
- Fallback de imagens quando tamanho específico não disponível
Ver Diagrama de Arquitetura
O diagrama abaixo ilustra a arquitetura completa do sistema, mostrando as relações entre o frontend Next.js, os subsites WordPress e as APIs externas.
graph TB
subgraph "Cliente (Browser)"
UI[Interface do Usuário<br/>Next.js Frontend]
end
subgraph "Next.js Application"
Pages[Pages Routes<br/>/pages/]
API[API Routes<br/>/pages/api/]
Services[Services<br/>/services/]
Components[Components<br/>/components/]
end
subgraph "WordPress Multi-Site"
WP_Global[WordPress Global<br/>/wp-json/wp/v2/]
WP_AFRO[WordPress AFRO<br/>/afro/wp-json/wp/v2/]
WP_AMRO[WordPress AMRO<br/>/amro/wp-json/wp/v2/]
WP_EMRO[WordPress EMRO<br/>/emro/wp-json/wp/v2/]
WP_EURO[WordPress EURO<br/>/euro/wp-json/wp/v2/]
WP_SEARO[WordPress SEARO<br/>/searo/wp-json/wp/v2/]
WP_WPRO[WordPress WPRO<br/>/wpro/wp-json/wp/v2/]
end
subgraph "APIs Externas"
DIREV[DIREV API<br/>Eventos Bibliográficos]
MULTIMEDIA[Multimedia API<br/>Recursos Multimídia]
JOURNALS[Journals API<br/>Periódicos]
LIS[LIS API<br/>Literatura em Saúde]
EVIDENCE[Evidence Maps API<br/>Mapas de Evidências]
REGULATIONS[Regulations API<br/>Legislações]
RSS[RSS Feeds<br/>Agregação de Conteúdo]
end
subgraph "Serviços de Terceiros"
MAILCHIMP[Mailchimp<br/>Newsletter]
HOTJAR[Hotjar<br/>Analytics]
end
%% Fluxo Principal
UI --> Pages
UI --> Components
Pages --> Services
Components --> Services
%% Services para WordPress
Services -->|PostsApi| WP_Global
Services -->|PostsApi 'afro'| WP_AFRO
Services -->|PostsApi 'amro'| WP_AMRO
Services -->|PostsApi 'emro'| WP_EMRO
Services -->|PostsApi 'euro'| WP_EURO
Services -->|PostsApi 'searo'| WP_SEARO
Services -->|PostsApi 'wpro'| WP_WPRO
Services -->|PagesApi| WP_Global
Services -->|PagesApi 'afro'| WP_AFRO
Services -->|PagesApi 'amro'| WP_AMRO
Services -->|PagesApi 'emro'| WP_EMRO
Services -->|PagesApi 'euro'| WP_EURO
Services -->|PagesApi 'searo'| WP_SEARO
Services -->|PagesApi 'wpro'| WP_WPRO
Services -->|MenusApi| WP_Global
Services -->|TaxonomiesApi| WP_Global
Services -->|MediaApi| WP_Global
%% Services para API Routes (Proxy)
Services -->|DireveService| API
Services -->|MultimediaService| API
Services -->|JournalsService| API
Services -->|LisService| API
Services -->|EvidenceMapsService| API
Services -->|RegulationAndPolices| API
%% API Routes para APIs Externas
API -->|/api/direve| DIREV
API -->|/api/multimedia| MULTIMEDIA
API -->|/api/journals| JOURNALS
API -->|/api/lis| LIS
API -->|/api/evidencemaps| EVIDENCE
API -->|/api/legislations| REGULATIONS
API -->|/api/rssfeed| RSS
%% Serviços de Terceiros
Services -->|MailChimpService| MAILCHIMP
UI -->|Script| HOTJAR
%% Estilos
classDef wpSite fill:#21759b,stroke:#135e96,stroke-width:2px,color:#fff
classDef apiExt fill:#ff6b6b,stroke:#c92a2a,stroke-width:2px,color:#fff
classDef nextjs fill:#000,stroke:#0070f3,stroke-width:2px,color:#fff
classDef thirdParty fill:#ffd43b,stroke:#fab005,stroke-width:2px,color:#000
class WP_Global,WP_AFRO,WP_AMRO,WP_EMRO,WP_EURO,WP_SEARO,WP_WPRO wpSite
class DIREV,MULTIMEDIA,JOURNALS,LIS,EVIDENCE,REGULATIONS,RSS apiExt
class Pages,API,Services,Components,UI nextjs
class MAILCHIMP,HOTJAR thirdParty
- Azul Escuro (WordPress): Subsites WordPress por região
- Vermelho (APIs Externas): APIs externas acessadas via proxy
- Preto (Next.js): Componentes da aplicação Next.js
- Amarelo (Terceiros): Serviços de terceiros integrados
Cliente → Pages/Components → Services → WordPress REST API
- Sem proxy: Conexão direta do cliente ao WordPress
- Autenticação: Não requerida (público)
- Uso: Posts, páginas, menus, taxonomias, mídia
Cliente → Pages/Components → Services → API Routes → APIs Externas
- Com proxy: API Routes atuam como intermediário
- Autenticação: Credenciais no servidor (API Routes)
- Uso: Eventos, multimídia, periódicos, literatura, etc.
Cliente → Services → Serviços Externos (Mailchimp)
Cliente → Script Direto → Hotjar
- Mailchimp: Via service e API Route
- Hotjar: Script direto no cliente
Pages Routes (/pages/):
- Rotas da aplicação
- SSR (Server-Side Rendering)
- Gerenciamento de estado de rota
API Routes (/pages/api/):
- Endpoints Next.js server-side
- Proxy para APIs externas
- Processamento seguro de credenciais
Services (/services/):
- Clientes HTTP para WordPress
- Clientes HTTP para API Routes
- Normalização de dados
Components (/components/):
- Componentes React reutilizáveis
- Consomem Services
- Renderização de UI
Estrutura:
- 1 site global + 6 sites regionais
- Cada site tem seu próprio conteúdo
- Compartilham plugins e temas
- REST API independente por site
Acesso:
- Global:
/wp-json/wp/v2/ - Regional:
/{region}/wp-json/wp/v2/
DIREV:
- Eventos bibliográficos
- Base de dados BVSALUD
- Filtros por região, país, área temática
Multimedia:
- Recursos multimídia
- Vídeos, imagens, documentos
- Suporte a múltiplos idiomas
Journals:
- Catálogo de periódicos científicos
- Metadados de publicações
LIS:
- Literatura em Saúde
- Artigos científicos
- Base de dados bibliográfica
Evidence Maps:
- Mapas de evidências científicas
- Visualizações de dados
Regulations:
- Legislações e políticas
- Documentos regulatórios
RSS Feeds:
- Agregação de conteúdo externo
- Feed de notícias e artigos
Por que usar Proxy (API Routes):
-
Credenciais Seguras:
Cliente → API Route (servidor) → API Externa- API keys nunca expostas no cliente
- Armazenadas em variáveis de ambiente no servidor
-
CORS:
- Requisições server-side não têm restrições CORS
- Evita problemas de política de origem cruzada
-
Transformação de Dados:
- Normalização de formatos diferentes
- Validação e sanitização
-
Cache e Rate Limiting:
- Controle de requisições no servidor
- Redução de carga nas APIs externas
Cenário: Usuário acessa /afro e visualiza eventos
1. Cliente acessa /afro
↓
2. Pages/[region]/index.tsx detecta região "afro"
↓
3. Component EventsSection é renderizado
↓
4. EventsSection chama DireveService.getResources()
↓
5. DireveService faz POST para /api/direve
↓
6. API Route /api/direve.ts recebe requisição
↓
7. API Route decripta API key (servidor)
↓
8. API Route faz GET para DIREV API externa
↓
9. DIREV API retorna dados
↓
10. API Route transforma e retorna dados
↓
11. DireveService recebe dados normalizados
↓
12. EventsSection renderiza eventos na UI
Paralelamente:
3. Component NewsSection também é renderizado
↓
4. NewsSection chama PostsApi("afro").getCustomPost("posts")
↓
5. PostsApi faz GET direto para /afro/wp-json/wp/v2/posts
↓
6. WordPress AFRO retorna posts da região
↓
7. NewsSection renderiza notícias na UI