Parent
Parte da PRD #250 — Módulo Profile Fase 1.
Contexto
O perfil de um membro é público — qualquer pessoa (incluindo recrutadores sem conta) pode visualizar acessando a URL do domínio do tenant. É a vitrine do desenvolvedor pra comunidade e pro mercado.
O tenant é resolvido via domain routing do Laravel. Cada tenant tem uma coluna domain na tabela tenants. Quando alguém acessa he4rtdevs.com/@danielhe4rt, o Laravel resolve o tenant pelo domínio e busca o profile daquele user naquele tenant.
A página agrega dados de múltiplos módulos
A view pública não mostra só dados do user_profiles. Ela compõe informações de:
- Profile (módulo profile): headline, bio, senioridade, anos de experiência, disponibilidade, social links, nickname
- Identity (módulo identity): nome do User, username, avatar (media do User)
- Gamification (módulo gamification): Character (level, XP), Badges conquistados
- Identity (ExternalIdentity): provedores OAuth conectados (GitHub, Discord) — derivar URLs automaticamente
- Shared (Address polimórfico): localização (país, estado, cidade)
Não é Filament
Esta página é uma rota web pública, fora do Filament. Similar ao módulo landing que serve o GET /. Usa Blade + Tailwind (+ Livewire se necessário pra interatividade).
Sem autenticação
A rota não requer login. Todos os dados exibidos são públicos. A disponibilidade (available_for_proposals) também é pública — decisão da PRD.
O que construir
1. Rota com domain routing
Configurar rota que usa o domínio do tenant:
Route::domain('{tenant:domain}')
->get('/@{username}', ProfileController@show)
->name('profile.public');
(A implementação exata depende de como o Laravel resolve tenants por domínio — consultar docs do Laravel sobre domain routing.)
A rota recebe o username do User e o tenant é resolvido pelo domínio.
2. Controller / Livewire component
Um controller (ou Livewire full-page component) que:
- Resolve o tenant pelo domínio
- Busca o User pelo username
- Busca o Profile daquele User naquele Tenant
- Eager-load:
character, character.badges, providers (ExternalIdentity), address (polimórfico)
- Retorna 404 se User não existe ou não tem Profile naquele tenant
3. View Blade
Página com layout público (sem sidebar de Filament). Seções:
Header:
- Cover image (se existir, via media do User) ou gradient default
- Avatar do User (media ou GitHub fallback)
- Nome, username (@handle), headline
- Badge de "Disponível" (se
available_for_proposals = true)
- Localização (cidade, estado, país — se preenchido)
Sobre:
- Bio (
about) — texto completo
- Senioridade + anos de experiência
Links:
- Social links manuais (do JSONB
social_links) — com ícones por plataforma
- Provedores OAuth conectados (GitHub, Discord, etc.) — URLs derivadas do ExternalIdentity
Gamificação:
- Level do Character
- Badges conquistados (com imagem, se tiver media)
Disponibilidade:
- Se ativo: badge "Disponível" + prazo de início
- Se inativo: nada (não mostrar "Indisponível")
4. Tratamento de casos especiais
- User não existe: 404
- User existe mas não tem Profile no tenant: 404
- Profile existe mas campos vazios: renderizar só o que foi preenchido, sem mostrar "null" ou placeholders quebrados
- User sem Character: seção de gamificação não aparece
- User sem Address: seção de localização não aparece
Cenários BDD
Funcionalidade: Página pública de perfil
Cenário: Visitante acessa perfil completo
Dado que "danielhe4rt" tem Profile no tenant com domínio "he4rtdevs.com"
E headline é "Backend Developer", seniority "senior", about "Dev PHP com 8 anos"
E available_for_proposals é true, start_availability "immediate"
E Character com level 15 e 3 badges
E Address com country "BR", state "SP", city "São Paulo"
E ExternalIdentity conectado: GitHub "danielhe4rt"
E social_links: {"instagram": "@danielhe4rt"}
Quando um visitante anônimo acessa "he4rtdevs.com/@danielhe4rt"
Então a página exibe nome, username, headline "Backend Developer"
E exibe badge "Disponível"
E exibe localização "São Paulo, SP, BR"
E exibe bio completa
E exibe senioridade "Sênior" e "8 anos de experiência"
E exibe link do Instagram
E exibe link do GitHub (derivado do ExternalIdentity)
E exibe level 15 e 3 badges
E não requer autenticação (status 200)
Cenário: Visitante acessa perfil mínimo (quase vazio)
Dado que "novato" tem Profile no tenant mas nunca preencheu nada
E não tem Character nem Address
Quando um visitante acessa o perfil
Então exibe o nome do User (da tabela users)
E não exibe seção de bio, senioridade, gamificação, localização ou links
E não exibe "null" ou placeholders quebrados
Cenário: Username inexistente retorna 404
Quando um visitante acessa "he4rtdevs.com/@fantasma"
Então recebe status 404
Cenário: User existe mas não tem Profile no tenant
Dado que "danielhe4rt" existe como User
Mas não é membro do tenant com domínio "outro-dominio.com"
Quando um visitante acessa "outro-dominio.com/@danielhe4rt"
Então recebe status 404
Cenário: Disponibilidade inativa não mostra badge
Dado que "danielhe4rt" tem available_for_proposals false
Quando um visitante acessa o perfil
Então não exibe badge "Disponível"
E não exibe "Indisponível"
Cenário: Domínio resolve o tenant correto
Dado que "danielhe4rt" tem Profile em dois tenants:
- tenant A (domínio "he4rtdevs.com") com headline "PHP Dev"
- tenant B (domínio "outra.com") com headline "Rust Dev"
Quando visitante acessa "he4rtdevs.com/@danielhe4rt"
Então headline exibido é "PHP Dev"
Quando visitante acessa "outra.com/@danielhe4rt"
Então headline exibido é "Rust Dev"
Acceptance Criteria
Blocked by
Parent
Parte da PRD #250 — Módulo Profile Fase 1.
Contexto
O perfil de um membro é público — qualquer pessoa (incluindo recrutadores sem conta) pode visualizar acessando a URL do domínio do tenant. É a vitrine do desenvolvedor pra comunidade e pro mercado.
O tenant é resolvido via domain routing do Laravel. Cada tenant tem uma coluna
domainna tabelatenants. Quando alguém acessahe4rtdevs.com/@danielhe4rt, o Laravel resolve o tenant pelo domínio e busca o profile daquele user naquele tenant.A página agrega dados de múltiplos módulos
A view pública não mostra só dados do
user_profiles. Ela compõe informações de:Não é Filament
Esta página é uma rota web pública, fora do Filament. Similar ao módulo
landingque serve oGET /. Usa Blade + Tailwind (+ Livewire se necessário pra interatividade).Sem autenticação
A rota não requer login. Todos os dados exibidos são públicos. A disponibilidade (
available_for_proposals) também é pública — decisão da PRD.O que construir
1. Rota com domain routing
Configurar rota que usa o domínio do tenant:
(A implementação exata depende de como o Laravel resolve tenants por domínio — consultar docs do Laravel sobre domain routing.)
A rota recebe o
usernamedo User e o tenant é resolvido pelo domínio.2. Controller / Livewire component
Um controller (ou Livewire full-page component) que:
character,character.badges,providers(ExternalIdentity),address(polimórfico)3. View Blade
Página com layout público (sem sidebar de Filament). Seções:
Header:
available_for_proposals = true)Sobre:
about) — texto completoLinks:
social_links) — com ícones por plataformaGamificação:
Disponibilidade:
4. Tratamento de casos especiais
Cenários BDD
Acceptance Criteria
/@{username}funciona com domain routing (tenant resolvido pelo domínio)available_for_proposals = truevendor/bin/pint --dirty --format agentpassa sem errosBlocked by