# Planejamento de Dados Estruturados (Schema) - Portal Allminds

A estratégia de dados estruturados do Portal Allminds deverá representar semanticamente as principais entidades existentes na plataforma e, principalmente, **as relações entre elas**.

O objetivo não será criar Schemas isolados em cada URL. O Portal deverá formar um grafo consistente no qual seja possível identificar:

- A Allminds como `Organization`;
- O Portal como `WebSite`;
- Cada URL como `WebPage` ou subtipo correspondente;
- O profissional como `Person`;
- O perfil do profissional como `ProfilePage`;
- A profissão e o registro profissional;
- Os serviços disponibilizados pelo profissional;
- Os artigos escritos pelo profissional;
- As perguntas e respostas existentes no Portal;
- As avaliações dos serviços;
- Os canais pelos quais os serviços podem ser acessados.

A implementação deverá utilizar **JSON-LD**.

---

# 1. Regra estrutural principal do grafo

A relação principal entre página, site e organização deverá seguir:

```text
WebPage / subtipo de WebPage
│
└── isPartOf
     └── WebSite
          │
          └── publisher
               └── Organization
                    └── Allminds
```

A direção dessa relação é importante.

Não utilizar:

```text
Organization
└── publisher → WebSite
```

Utilizar:

```text
WebSite
└── publisher → Organization
```

A propriedade `publisher` é aplicável a `CreativeWork`, categoria da qual `WebSite` faz parte, e recebe `Organization` ou `Person`.

Da mesma forma, cada página do Portal deverá se relacionar ao site através de:

```text
isPartOf → WebSite
```

`isPartOf` é aplicável a `CreativeWork`, incluindo `WebPage` e seus subtipos.

---

# 2. Schema modular e condicional

O Portal terá profissionais com diferentes quantidades de informação e funcionalidades.

Poderá existir:

- Perfil gratuito;
- Perfil com descrição;
- Perfil com especialidades;
- Perfil com endereço;
- Perfil com atendimento online;
- Perfil com horários;
- Perfil com agenda;
- Perfil com avaliações;
- Profissional que publica artigos;
- Profissional que responde perguntas;
- Profissional que utiliza todas essas funcionalidades.

Portanto, **não deverá existir um único JSON-LD rígido de profissional**.

O grafo deverá ser construído conforme os dados realmente disponíveis na página. Essa condição já faz parte do modelo funcional planejado para os perfis.

Exemplo:

```text
se possui redes sociais:
    gerar sameAs

se possui especialidades:
    gerar knowsAbout

se possui serviços:
    gerar hasOfferCatalog

se possui horários:
    gerar hoursAvailable

se possui agenda:
    gerar ScheduleAction

se possui avaliações:
    gerar Review / AggregateRating

se publicou artigos:
    gerar BlogPosting em hasPart

se respondeu perguntas:
    gerar Answer em hasPart
```

A ausência de uma informação deverá resultar simplesmente na ausência da propriedade correspondente.

---

# 3. Identificação das entidades com `@id`

As principais entidades deverão possuir `@id` estável.

Exemplo:

```text
Allminds
https://portal.allminds.app/#organization

Portal Allminds
https://portal.allminds.app/#website

Página atual
https://portal.allminds.app/{url}/#webpage

Perfil
https://portal.allminds.app/{perfil}/#profile

Profissional
https://portal.allminds.app/{perfil}/#person

Blog
https://portal.allminds.app/blog/#blog
```

Isso permitirá que diferentes objetos façam referência à mesma entidade sem recriá-la.

Exemplo:

```text
BlogPosting
└── author → #person
```

```text
Answer
└── author → #person
```

```text
ProfilePage
└── mainEntity → #person
```

---

# 4. `Organization`

A Allminds deverá possuir uma única identidade principal:

```text
Organization
@id → https://portal.allminds.app/#organization
```

Na homepage poderão ser informados os principais dados existentes da organização, como:

```text
Organization
├── name
├── url
├── logo
├── description
├── sameAs
└── contactPoint
```

A entidade completa não precisa ser repetida integralmente em todas as páginas.

O Google recomenda concentrar a marcação principal de `Organization` na homepage ou em uma página institucional apropriada.

---

# 5. `WebSite`

O Portal deverá possuir:

```text
WebSite
@id → https://portal.allminds.app/#website
│
├── name
├── url
├── alternateName
│
└── publisher
     └── Organization #organization
```

Esse será o nó que conecta todas as páginas à Allminds.

---

# 6. Regra geral das páginas

Toda URL deverá possuir um nó de página.

Quando existir um subtipo específico de `WebPage`, deverá ser usado o subtipo.

Exemplos:

```text
/sobre/
→ AboutPage

/contato/
→ ContactPage

/perguntas/{pergunta}/
→ QAPage

/{perfil-profissional}/
→ ProfilePage

páginas de listagem
→ CollectionPage
```

Quando não existir um subtipo específico:

```text
WebPage
```

Estrutura comum:

```text
WebPage / subtipo
│
├── @id → URL/#webpage
├── name
├── url
│
├── isPartOf
│    └── WebSite #website
│
└── breadcrumb
     └── BreadcrumbList
```

Quando a página possuir uma entidade principal claramente identificável:

```text
mainEntity → entidade
```

`mainEntity` existe especificamente para identificar a principal entidade descrita por uma página ou outro `CreativeWork`.

---

# 7. Breadcrumbs

As páginas com trilha de navegação deverão utilizar:

```text
BreadcrumbList
```

Modelo:

```text
BreadcrumbList
├── ListItem
│    ├── position: 1
│    └── item
│
├── ListItem
│    ├── position: 2
│    └── item
│
└── ...
```

Diferentemente dos demais `ItemList`, o breadcrumb possui uma sequência hierárquica real, portanto `position` deverá ser utilizado.

O Google possui suporte específico para `BreadcrumbList`.

---

# 8. Homepage

A homepage deverá estabelecer o grafo principal:

```text
WebPage #webpage
│
└── isPartOf
     └── WebSite #website
          │
          └── publisher
               └── Organization #organization
```

Na mesma página deverão ser definidos de forma completa:

```text
WebSite #website
Organization #organization
```

Nas páginas internas será possível reutilizar esses mesmos `@id`.

---

# 9. Páginas institucionais

As páginas:

```text
/sobre/
/contato/
/politicas-de-privacidade/
/politicas-de-cookies/
/termos-de-uso/
/transparencia/
/disclaimer/
```

deverão seguir:

```text
/sobre/
└── AboutPage

/contato/
└── ContactPage

demais páginas
└── WebPage
```

Todas permanecerão conectadas:

```text
WebPage / AboutPage / ContactPage
│
├── isPartOf → WebSite
│                 └── publisher → Organization
│
└── breadcrumb → BreadcrumbList
```

Para `/sobre/`, como a página descreve diretamente a Allminds:

```text
AboutPage
└── mainEntity → Organization #organization
```

Não será necessário criar tipos específicos inexistentes para Política de Privacidade, Cookies, Transparência ou Disclaimer.

---

# 10. FAQ institucional

A rota:

```text
/faq/
```

deverá utilizar:

```text
FAQPage
│
├── isPartOf → WebSite
├── breadcrumb → BreadcrumbList
│
└── mainEntity
     ├── Question
     │    └── acceptedAnswer → Answer
     │
     ├── Question
     │    └── acceptedAnswer → Answer
     │
     └── ...
```

`FAQPage` é oficialmente um subtipo de `WebPage` para páginas contendo perguntas frequentes.

As perguntas dessa página são controladas pela própria Allminds e não devem ser confundidas com a área comunitária `/perguntas/`.

---

# 11. Página principal do Blog

A rota:

```text
/blog/
```

deverá diferenciar **a página física** do **Blog enquanto entidade editorial**.

Modelo:

```text
CollectionPage #webpage
│
├── isPartOf → WebSite
├── breadcrumb → BreadcrumbList
│
└── mainEntity
     └── Blog #blog
          │
          ├── isPartOf → WebSite
          ├── publisher → Organization
          │
          └── blogPost
               ├── BlogPosting
               ├── BlogPosting
               └── ...
```

`Blog` possui oficialmente `blogPost → BlogPosting`.

Assim:

```text
CollectionPage = página /blog/
Blog = publicação editorial
BlogPosting = artigo individual
```

---

# 12. Hub de tópicos do Blog

A rota:

```text
/topicos/
```

deverá utilizar:

```text
CollectionPage
│
├── isPartOf → WebSite
├── breadcrumb → BreadcrumbList
│
└── mainEntity
     └── ItemList
          ├── WebPage → /topicos/ansiedade/
          ├── WebPage → /topicos/tdah/
          ├── WebPage → /topicos/depressao/
          └── ...
```

Se a listagem não representar ranking nem sequência semântica:

```text
itemListOrder → ItemListUnordered
```

Não será necessário usar `ListItem.position`.

---

# 13. Página de tópico do Blog

A rota:

```text
/topicos/{categoria-do-blog}/
```

deverá utilizar:

```text
CollectionPage
│
├── isPartOf → WebSite
├── breadcrumb → BreadcrumbList
│
└── mainEntity
     └── ItemList
          ├── BlogPosting
          ├── BlogPosting
          └── ...
```

Cada `BlogPosting` da lista poderá conter apenas os dados realmente apresentados no loop, como:

```text
BlogPosting
├── headline
├── url
├── image
├── datePublished
└── author
```

O artigo completo será estruturado em sua página individual.

---

# 14. Artigo individual

A rota:

```text
/blog/{slug-do-post}/
```

deverá possuir duas entidades:

```text
WebPage #webpage
└── mainEntity → BlogPosting #article
```

Modelo:

```text
WebPage #webpage
│
├── isPartOf → WebSite
├── breadcrumb → BreadcrumbList
│
└── mainEntity → BlogPosting #article
```

```text
BlogPosting #article
│
├── headline
├── image
├── datePublished
├── dateModified
│
├── isPartOf
│    └── Blog #blog
│
├── author
│    └── Person #person
│
├── publisher
│    └── Organization #organization
│
└── copyrightHolder
     └── Organization #organization
```

`BlogPosting` é um tipo oficial de artigo e o Google recomenda propriedades como `author`, `datePublished`, `dateModified` e imagens.

---

# 15. Autoria dos artigos

Quando um profissional escrever o conteúdo:

```text
BlogPosting
└── author → Person #person
```

O `Person` deverá ser o mesmo profissional representado em seu `ProfilePage`.

O Google recomenda que o URL do autor identifique exclusivamente o autor e, quando for uma página interna de perfil, recomenda `ProfilePage`.

Modelo:

```text
BlogPosting
└── author
     └── Person
          ├── @id → perfil/#person
          ├── name
          └── url → perfil
```

---

# 16. Publisher e direitos dos artigos

A Allminds será a publicação responsável:

```text
BlogPosting
└── publisher → Organization #organization
```

Caso juridicamente a Allminds detenha os direitos:

```text
copyrightHolder → Organization #organization
```

O status comercial do profissional não deverá alterar automaticamente a autoria.

Se o artigo continuar exibindo:

> Por Cristina Linhares

o grafo continuará:

```text
author → Cristina Linhares
publisher → Allminds
copyrightHolder → Allminds
```

Caso futuramente a autoria visível também seja alterada, o Schema deverá acompanhar o conteúdo exibido.

---

# 17. Área principal de Perguntas

A rota:

```text
/perguntas/
```

contém várias perguntas e não deverá utilizar `QAPage`.

Modelo:

```text
CollectionPage
│
├── isPartOf → WebSite
├── breadcrumb → BreadcrumbList
│
└── mainEntity
     └── ItemList
          ├── Question
          ├── Question
          └── ...
```

Cada pergunta poderá possuir:

```text
Question
├── name
├── url
└── answerCount
```

Somente as informações efetivamente exibidas deverão ser adicionadas.

---

# 18. Hub de temas das perguntas

A rota:

```text
/perguntas/temas/
```

deverá utilizar:

```text
CollectionPage
│
├── isPartOf → WebSite
├── breadcrumb → BreadcrumbList
│
└── mainEntity
     └── ItemList
          ├── WebPage → /perguntas/temas/ansiedade/
          ├── WebPage → /perguntas/temas/tdah/
          └── ...
```

Se não existir uma ordenação semântica:

```text
itemListOrder → ItemListUnordered
```

---

# 19. Página de tema das perguntas

A rota:

```text
/perguntas/temas/{categoria-da-pergunta}/
```

deverá utilizar:

```text
CollectionPage
│
├── isPartOf → WebSite
├── breadcrumb → BreadcrumbList
│
└── mainEntity
     └── ItemList
          ├── Question
          ├── Question
          └── ...
```

Cada pergunta poderá apontar para sua respectiva página:

```text
Question
├── name
├── url
└── answerCount
```

---

# 20. Página individual de uma pergunta

A rota:

```text
/perguntas/{slug-da-pergunta}/
```

deverá utilizar:

```text
QAPage
```

`QAPage` já é um subtipo de `WebPage`.

Modelo:

```text
QAPage #webpage
│
├── isPartOf → WebSite
├── breadcrumb → BreadcrumbList
│
└── mainEntity
     └── Question
          ├── name
          ├── text
          ├── datePublished
          ├── dateModified
          ├── answerCount
          └── Answers
```

O Google determina que a página `QAPage` deve ser focada em uma única pergunta e suas respostas.

---

# 21. Múltiplos profissionais respondendo

Uma pergunta poderá possuir uma ou várias respostas.

Exemplo com seis:

```text
Question
├── answerCount: 6
│
├── suggestedAnswer → Answer A
├── suggestedAnswer → Answer B
├── suggestedAnswer → Answer C
├── suggestedAnswer → Answer D
├── suggestedAnswer → Answer E
└── suggestedAnswer → Answer F
```

Cada resposta será independente:

```text
Answer
├── text
├── datePublished
└── author → Person
```

O `answerCount` deverá representar **quantidade real de respostas**, não quantidade de profissionais.

Se seis psicólogos forneceram uma resposta cada:

```text
answerCount = 6
```

---

# 22. Melhor resposta

Se a Allminds implementar uma funcionalidade formal de **Melhor resposta**, a resposta selecionada deverá ser representada como:

```text
acceptedAnswer
```

As demais permanecem:

```text
suggestedAnswer
```

Schema.org define `acceptedAnswer` como uma resposta aceita como melhor, inclusive através de opinião da comunidade ou do autor da pergunta.

### Uma resposta sem melhor resposta

```text
Question
└── suggestedAnswer → Answer
```

### Uma resposta marcada como melhor

```text
Question
└── acceptedAnswer → Answer
```

### Várias respostas sem melhor resposta

```text
Question
├── suggestedAnswer → Answer A
├── suggestedAnswer → Answer B
└── suggestedAnswer → Answer C
```

### Várias respostas com uma melhor

```text
Question
├── acceptedAnswer → Answer B
├── suggestedAnswer → Answer A
└── suggestedAnswer → Answer C
```

Regra da Allminds:

> Cada pergunta poderá possuir no máximo uma Melhor resposta.

Uma resposta marcada como `acceptedAnswer` não deverá ser repetida simultaneamente em `suggestedAnswer`.

Ordenação por data ou simples destaque visual não deve ser interpretada automaticamente como Melhor resposta. O Google também diferencia respostas aceitas de outras formas de ordenação.

---

# 23. Estrutura de `Answer`

Quando disponível:

```text
Answer
├── @id
├── text
├── datePublished
├── dateModified
├── url
├── upvoteCount
│
├── author
│    └── Person #person
│
└── parentItem
     └── Question
```

`upvoteCount` somente deverá existir se houver efetivamente um sistema de votos.

Não deverá ser utilizado para representar visualizações ou qualquer tipo genérico de engajamento.

`Answer` possui oficialmente `parentItem` e `upvoteCount`.

Caso cada resposta possua anchor:

```text
/perguntas/pergunta/#resposta-123
```

essa URL poderá ser usada em `Answer.url`.

---

# 24. Perfil profissional

A rota:

```text
/{cond-profissional}/{nome-do-profissional}/{cidade}/{uf}/
```

deverá utilizar:

```text
ProfilePage
```

A estrutura de perfil já é parte central do planejamento atual.

Modelo:

```text
ProfilePage #profile
│
├── isPartOf → WebSite
├── breadcrumb → BreadcrumbList
├── dateCreated
├── dateModified
│
└── mainEntity
     └── Person #person
```

`ProfilePage` é oficialmente um subtipo de `WebPage` e tem como foco uma pessoa ou organização.

---

# 25. Perfil - Núcleo do `Person`

O profissional deverá ser:

```text
Person #person
│
├── name
├── url
├── image
├── description
│
├── hasOccupation
└── hasCertification
```

As propriedades adicionais dependerão das informações disponíveis no perfil.

---

# 26. Perfil com profissão

A profissão será representada por:

```text
Person
└── hasOccupation
     └── Occupation
          └── name
```

Exemplo:

```text
Occupation
└── name: Psicólogo
```

ou:

```text
Occupation
└── name: Psiquiatra
```

`Occupation` representa oficialmente uma profissão e `hasOccupation` é aplicável a `Person`.

---

# 27. Perfil com registro profissional — CRP / CRM

O registro profissional deverá utilizar:

```text
Person
└── hasCertification
     └── Certification
```

O Schema.org define `Certification` como uma declaração oficial e autoritativa emitida por uma organização independente, incluindo certificações regulatórias, credenciais e licenças profissionais.

### Psicólogo

```text
Person
└── hasCertification
     └── Certification
          ├── name → Registro profissional de Psicólogo
          ├── certificationIdentification → CRP 03/1896
          │
          └── issuedBy
               └── Organization
                    └── Conselho Regional de Psicologia
```

### Psiquiatra

```text
Person
└── hasCertification
     └── Certification
          ├── name → Registro profissional médico
          ├── certificationIdentification → CRM ...
          │
          └── issuedBy
               └── Organization
                    └── Conselho Regional de Medicina
```

`certificationIdentification` existe especificamente para o identificador registrado junto à entidade certificadora.

`issuedBy` identifica a organização emissora.

### Perfil com status da certificação

Somente utilizar:

```text
certificationStatus
```

se a Allminds efetivamente possuir informação confiável e atualizada sobre o status do registro.

A simples existência do número cadastrado não deverá ser usada para declarar:

```text
Active
```

sem verificação.

---

# 28. Perfil com especialidades, áreas e abordagens

Áreas, temas e abordagens informados pelo profissional poderão utilizar:

```text
knowsAbout
```

Exemplo:

```text
Person
├── knowsAbout → Ansiedade
├── knowsAbout → TDAH
├── knowsAbout → Psicanálise
└── knowsAbout → Terapia Cognitivo-Comportamental
```

Essas informações são diferentes do registro profissional.

```text
CRP / CRM
→ hasCertification

Profissão
→ hasOccupation

Áreas / abordagens / conhecimentos
→ knowsAbout
```

---

# 29. Perfil com informações de contato e redes sociais

Quando disponíveis:

```text
Person
├── contactPoint
│    └── ContactPoint
│         ├── telephone
│         └── email
│
└── sameAs
     ├── Instagram
     ├── LinkedIn
     └── site próprio
```

Somente contatos e perfis realmente públicos deverão ser incluídos.

`sameAs` deverá ser reservado para páginas que identifiquem inequivocamente o profissional.

---

# 30. Perfil com local de trabalho

Quando houver endereço profissional:

```text
Person
└── workLocation
     └── Place
          └── address
               └── PostalAddress
                    ├── streetAddress
                    ├── addressLocality
                    ├── addressRegion
                    ├── postalCode
                    └── addressCountry
```

`workLocation` é oficialmente aplicável a `Person`.

---

# 31. Perfil com serviços oferecidos

Se o profissional cadastrar serviços:

```text
Person
└── hasOfferCatalog
     └── OfferCatalog
```

O `OfferCatalog` conterá:

```text
OfferCatalog
└── itemListElement
     ├── Offer
     │    └── itemOffered
     │         └── Service
     │
     └── Offer
          └── itemOffered
               └── Service
```

Esse é o modelo documentado pelo Schema.org para catálogos de serviços.

`Person` pode utilizar oficialmente `hasOfferCatalog`.

---

# 32. Perfil com provider do serviço

Cada `Service` deverá apontar para o profissional:

```text
Service
└── provider → Person #person
```

Assim:

```text
Person
└── hasOfferCatalog
     └── Offer
          └── Service
               └── provider → Person
```

---

# 33. Perfil com atendimento presencial

Quando o serviço for presencial:

```text
Service
└── availableChannel
     └── ServiceChannel
          └── serviceLocation
               └── Place
```

O local poderá reutilizar o mesmo `Place` usado em `Person.workLocation`.

---

# 34. Perfil com atendimento online

Quando o profissional atender online:

```text
Service
└── availableChannel
     └── ServiceChannel
          └── serviceUrl
               ↓
          URL do perfil
```

Será utilizado o próprio perfil como URL estável de acesso ao serviço:

```text
https://portal.allminds.app/{cond-profissional}/{nome-do-profissional}/{cidade}/{uf}/
```

Essa condição é apropriada quando o perfil disponibilizar meio real de acesso ao atendimento, como:

- Contato;
- WhatsApp;
- Agenda;
- Outro recurso para iniciar atendimento.

`ServiceChannel.serviceUrl` é definido pelo Schema.org como o website usado para acessar o serviço.

Links privados de Meet ou URLs individuais de sessão não deverão entrar no grafo.

---

# 35. Perfil com horários de atendimento

Quando os horários gerais estiverem disponíveis:

```text
Service
└── hoursAvailable
     └── OpeningHoursSpecification
```

Exemplo:

```text
OpeningHoursSpecification
├── dayOfWeek → Monday
├── opens → 08:00
└── closes → 18:00
```

Essa informação representa o **horário geral em que o atendimento pode ocorrer**.

Não representa os slots livres da agenda.

---

# 36. Perfil com agendamento

Quando existir recurso real de agendamento:

```text
Service
└── potentialAction
     └── ScheduleAction
          └── target
               ↓
          URL da agenda
```

Se o fluxo estiver dentro do próprio perfil, poderá ser utilizado o URL ou anchor correspondente.

Se o profissional não possuir agenda:

```text
não gerar ScheduleAction
```

---

# 37. Perfil com avaliações

As avaliações deverão estar relacionadas ao serviço, e não diretamente ao `Person`.

```text
Service
├── review → Review
└── aggregateRating → AggregateRating
```

`Service` suporta oficialmente `review` e `aggregateRating`.

---

# 38. Perfil com avaliação individual

Quando houver nota e comentário:

```text
Review
│
├── itemReviewed → Service
│
├── reviewRating
│    └── Rating
│         ├── ratingValue
│         ├── bestRating: 5
│         └── worstRating: 1
│
├── reviewBody
├── datePublished
└── author
```

Se houver apenas nota:

```text
Review
└── reviewRating
```

Não deverá ser criado `reviewBody` vazio.

---

# 39. Perfil com nota agregada

Quando existirem avaliações:

```text
AggregateRating
├── itemReviewed → Service
├── ratingValue
├── ratingCount
├── reviewCount
├── bestRating: 5
└── worstRating: 1
```

`ratingCount` representa o total de avaliações.

`reviewCount` representa o total de reviews.

Se ainda não existirem avaliações:

```text
não gerar AggregateRating
```

---

# 40. Artigos exibidos no perfil

Quando o profissional tiver artigos publicados e eles forem exibidos no perfil:

```text
ProfilePage
└── hasPart
     ├── BlogPosting
     ├── BlogPosting
     └── ...
```

Cada artigo deverá apontar novamente ao profissional:

```text
BlogPosting
└── author → Person #person
```

O Google documenta explicitamente `ProfilePage.hasPart` para representar atividade recente do criador.

Se o perfil não tiver artigos:

```text
não gerar BlogPosting em hasPart
```

---

# 41. Respostas exibidas no perfil

Quando o perfil exibir respostas:

```text
ProfilePage
└── hasPart
     ├── Answer
     ├── Answer
     └── ...
```

Cada resposta poderá conter:

```text
Answer
├── author → Person #person
├── url → pergunta/#resposta
│
└── parentItem
     └── Question
```

`Answer` é um `CreativeWork`, portanto pode participar de `hasPart`. `hasPart` aceita `CreativeWork`.

---

# 42. Paginação da atividade do perfil

Se o perfil apresentar 10 artigos ou respostas por página, cada renderização deverá representar somente os elementos exibidos naquele momento.

Exemplo:

```text
Página 1
├── BlogPosting 1
├── ...
└── BlogPosting 10
```

```text
Página 2
├── BlogPosting 11
├── ...
└── BlogPosting 20
```

Não será necessário colocar toda a atividade histórica dentro do JSON-LD da primeira página.

---

# 43. MAPAS CONDICIONAIS DO PERFIL

## Cenário A — Cadastro mínimo

```text
ProfilePage
│
├── isPartOf → WebSite
├── breadcrumb
│
└── mainEntity
     └── Person
          ├── name
          ├── url
          │
          ├── hasOccupation
          │    └── Occupation
          │
          └── hasCertification
               └── Certification
                    └── CRP / CRM
```

Não gerar estruturas inexistentes.

---

# 44. Cenário B — Perfil enriquecido

```text
ProfilePage
└── Person
     ├── name
     ├── image
     ├── description
     ├── url
     ├── hasOccupation
     ├── hasCertification
     │
     ├── sameAs
     │    ├── Instagram
     │    ├── LinkedIn
     │    └── site
     │
     ├── contactPoint
     │    └── ContactPoint
     │
     └── workLocation
          └── Place
               └── PostalAddress
```

---

# 45. Cenário C — Perfil com especialidades

```text
ProfilePage
└── Person
     ├── núcleo básico
     │
     ├── knowsAbout → Ansiedade
     ├── knowsAbout → TDAH
     ├── knowsAbout → Psicanálise
     └── knowsAbout → TCC
```

---

# 46. Cenário D — Perfil com serviços

```text
ProfilePage
└── Person
     ├── núcleo básico
     │
     └── hasOfferCatalog
          └── OfferCatalog
               ├── Offer
               │    └── itemOffered
               │         └── Service
               │              └── provider → Person
               │
               └── Offer
                    └── itemOffered
                         └── Service
                              └── provider → Person
```

---

# 47. Cenário E — Perfil com atendimento presencial e online

```text
Person
└── hasOfferCatalog
     └── Offer
          └── Service
               ├── provider → Person
               │
               └── availableChannel
                    ├── ServiceChannel
                    │    └── serviceLocation → Place
                    │
                    └── ServiceChannel
                         └── serviceUrl → Perfil
```

---

# 48. Cenário F — Perfil com agenda e horários

```text
ProfilePage
└── Person
     └── hasOfferCatalog
          └── Offer
               └── Service
                    ├── hoursAvailable
                    │    └── OpeningHoursSpecification
                    │
                    └── potentialAction
                         └── ScheduleAction
                              └── target → Agenda
```

---

# 49. Cenário G — Perfil com avaliações

```text
ProfilePage
└── Person
     └── hasOfferCatalog
          └── Offer
               └── Service
                    ├── aggregateRating
                    │    └── AggregateRating
                    │
                    └── review
                         ├── Review
                         ├── Review
                         └── ...
```

---

# 50. Cenário H — Profissional autor

```text
ProfilePage
│
├── mainEntity → Person
│
└── hasPart
     ├── BlogPosting
     │    └── author → Person
     │
     └── BlogPosting
          └── author → Person
```

---

# 51. Cenário I — Profissional respondente

```text
ProfilePage
│
├── mainEntity → Person
│
└── hasPart
     ├── Answer
     │    ├── author → Person
     │    └── parentItem → Question
     │
     └── Answer
          ├── author → Person
          └── parentItem → Question
```

---

# 52. Cenário J — Autor e respondente

```text
ProfilePage
│
├── mainEntity → Person
│
└── hasPart
     ├── BlogPosting
     ├── BlogPosting
     ├── Answer
     └── Answer
```

Cada item deverá manter sua própria relação de autoria.

---

# 53. Cenário K — Perfil completo

```text
ProfilePage
│
├── isPartOf → WebSite
├── breadcrumb
├── dateCreated
├── dateModified
│
├── mainEntity
│    └── Person
│         ├── name
│         ├── image
│         ├── description
│         ├── url
│         ├── sameAs
│         │
│         ├── hasOccupation
│         │    └── Occupation
│         │
│         ├── hasCertification
│         │    └── Certification
│         │         ├── certificationIdentification
│         │         └── issuedBy
│         │
│         ├── knowsAbout
│         ├── contactPoint
│         ├── workLocation
│         │
│         └── hasOfferCatalog
│              └── OfferCatalog
│                   └── Offer
│                        └── itemOffered
│                             └── Service
│                                  ├── provider → Person
│                                  ├── availableChannel
│                                  ├── hoursAvailable
│                                  ├── aggregateRating
│                                  ├── review
│                                  └── potentialAction
│                                       └── ScheduleAction
│
└── hasPart
     ├── BlogPosting
     ├── BlogPosting
     ├── Answer
     └── Answer
```

---

# 54. Regra de implementação do perfil

A implementação deverá responder aos dados disponíveis, e não ao nome do plano comercial.

Não utilizar:

```text
if plano == premium:
    gerar schema completo
```

Utilizar:

```text
se possui imagem:
    image

se possui redes:
    sameAs

se possui áreas:
    knowsAbout

se possui serviços:
    hasOfferCatalog

se atende presencial:
    ServiceChannel.serviceLocation

se atende online:
    ServiceChannel.serviceUrl

se possui horários:
    hoursAvailable

se possui agenda:
    ScheduleAction

se possui reviews:
    Review

se possui média:
    AggregateRating

se possui posts:
    ProfilePage.hasPart → BlogPosting

se possui respostas:
    ProfilePage.hasPart → Answer
```

Isso mantém a implementação independente das regras comerciais futuras.

---

# 55. Páginas de listagem de profissionais

As páginas:

```text
/{profissional}/
/{profissional}/online/
/{profissional}/{especialidade}/
/{profissional}/{cidade}/{uf}/
/{profissional}/{estado}/
/{profissional}/{regiao}/{cidade}/{uf}/
/{profissional}/{especialidade}/{cidade}/{uf}/
/{profissional}/{convenio}/
/{profissional}/{convenio}/{cidade}/{uf}/
```

renderizam conjuntos de profissionais.

Deverão utilizar:

```text
CollectionPage
│
├── isPartOf → WebSite
├── breadcrumb → BreadcrumbList
│
└── mainEntity
     └── ItemList
```

Cada profissional exibido poderá ser representado diretamente como `Person`:

```text
ItemList
├── Person
├── Person
├── Person
└── ...
```

Cada `Person` deverá utilizar pelo menos:

```text
Person
├── @id → perfil/#person
├── name
├── url → perfil
└── image → quando exibida
```

Não será necessário reproduzir o Schema completo de cada profissional dentro da listagem.

---

# 56. Regra especial para loops dinâmicos e anunciantes

A ordem dos profissionais **não deverá ser interpretada semanticamente como ranking**.

O Portal poderá aplicar regras comerciais de anunciantes e os profissionais poderão aparecer primeiro de forma randômica.

Exemplo:

```text
Carregamento 1:
Psicólogo A
Psicólogo B
Psicólogo C

Carregamento 2:
Psicólogo C
Psicólogo A
Psicólogo B
```

Essa situação deverá utilizar:

```text
ItemList
├── itemListOrder → ItemListUnordered
│
└── itemListElement
     ├── Person
     ├── Person
     └── Person
```

Schema.org define `ItemListUnordered` como uma lista sem ordem explícita.

Além disso, a documentação oficial recomenda o uso direto das entidades em `itemListElement` para listas simples e não ordenadas; `ListItem` é mais adequado quando existe uma ordem real que precisa de contexto ou posição.

Portanto, nesses loops:

```text
NÃO gerar:
ListItem.position
nextItem
previousItem
ItemListOrderAscending
ItemListOrderDescending
```

A posição visual de um anunciante não deverá declarar semanticamente:

> este é o profissional número 1

Ela deverá apenas indicar:

> estes são profissionais exibidos nesta página.

### Se futuramente existir ranking real

Somente se a Allminds criar um ranking explícito e estável, será possível alterar para:

```text
ItemList
└── ListItem
     ├── position
     └── item → Person
```

Essa condição não deverá ser aplicada aos loops randômicos de anunciantes.

---

# 57. Paginação dos loops profissionais

Caso as listagens possuam paginação, cada página deverá estruturar apenas os profissionais renderizados naquele conjunto.

Exemplo:

```text
Página 1
ItemList
├── Person
├── Person
└── ...

Página 2
ItemList
├── Person
├── Person
└── ...
```

Se `numberOfItems` for utilizado, ele poderá representar o total da lista completa quando esse total for conhecido de forma confiável. O Schema.org permite que `numberOfItems` represente o total mesmo quando apenas parte dos itens está descrita em uma página paginada.

A propriedade poderá ser omitida quando esse total não for confiável ou pertinente.

---

# 58. Hub de Especialidades

A rota:

```text
/especialidades/
```

deverá utilizar:

```text
CollectionPage
│
├── isPartOf → WebSite
├── breadcrumb → BreadcrumbList
│
└── mainEntity
     └── ItemList
          ├── CollectionPage → /psicologo/psicanalista/
          ├── CollectionPage → /psicologo/analise-do-comportamento/
          └── ...
```

Se a listagem não representar ranking:

```text
itemListOrder → ItemListUnordered
```

Cada `WebPage` deverá apontar para sua respectiva página de profissionais por especialidade.

Não será necessário criar entidades médicas adicionais dentro desse hub.

---

# 59. Regiões de atendimento

A rota:

```text
/regioes-de-atendimento/
```

deverá seguir:

```text
CollectionPage
│
├── isPartOf → WebSite
├── breadcrumb → BreadcrumbList
│
└── mainEntity
     └── ItemList
          ├── CollectionPage → /psicologo/dois-corregos/sp/
          ├── CollectionPage → /psiquiatra/dois-corregos/sp/
          ├── CollectionPage → /psicologo/sao-paulo/
          └── ...
```

Como a página é um hub de URLs do diretório, não será necessário criar `City` ou `State` como entidades adicionais dentro desse loop.

---

# 60. Atendimento por convênio

A rota:

```text
/atendimento-por-convenio/
```

deverá seguir:

```text
CollectionPage
│
├── isPartOf → WebSite
├── breadcrumb → BreadcrumbList
│
└── mainEntity
     └── ItemList
          ├── CollectionPage → /psicologo/unimed/
          ├── CollectionPage → /psiquiatra/unimed/
          └── ...
```

Os itens apontarão para páginas como:

```text
/psicologo/unimed/
/psiquiatra/unimed/
```

Não será necessário criar uma entidade adicional de plano de saúde apenas a partir do nome ou slug do convênio.

---

# 61. Visão geral do grafo

```text
                             Organization
                               Allminds
                                  ▲
                                  │ publisher
                                  │
                               WebSite
                                  ▲
                                  │ isPartOf
                                  │
          ┌───────────────────────┼────────────────────────┐
          │                       │                        │
      QAPage                CollectionPage           ProfilePage
          │                       │                        │
     mainEntity               mainEntity               mainEntity
          │                       │                        │
      Question                 ItemList                  Person
          │                       │                        │
       Answers                  Person                   ├── hasOccupation
          │                                               ├── hasCertification
        author                                            ├── knowsAbout
          │                                               ├── contactPoint
          └──────────────────────────────────────────────►Person
                                                          ├── workLocation
                                                          ├── sameAs
                                                          │
                                                          └── hasOfferCatalog
                                                               │
                                                               ▼
                                                          OfferCatalog
                                                               │
                                                             Offer
                                                               │
                                                          itemOffered
                                                               │
                                                             Service
                                                               │
                  ┌────────────────────────────────────────────┼─────────────┐
                  │                                            │             │
             ServiceChannel                                  Review     ScheduleAction
                  │
             serviceUrl
                  │
                  ▼
             ProfilePage
```

Camada editorial:

```text
CollectionPage /blog/
│
├── isPartOf → WebSite
│
└── mainEntity
     └── Blog
          │
          ├── publisher → Organization
          │
          └── blogPost
               └── BlogPosting
                    ├── isPartOf → Blog
                    ├── author → Person
                    ├── publisher → Organization
                    └── copyrightHolder → Organization
```

Perfil e atividade:

```text
ProfilePage
│
├── mainEntity → Person
│
└── hasPart
     ├── BlogPosting
     └── Answer
```

A estrutura resultante mantém o profissional como uma entidade central conectada à sua identidade, profissão, registro profissional, serviços, localização, canais de atendimento, artigos, respostas e avaliações, enquanto todas as páginas permanecem conectadas ao mesmo `WebSite` e à mesma `Organization`.