Context
O módulo profile é rico (seniority_level, skills + proficiência, available_for_proposals) e existe Character (level/XP) — mas não há forma de descobrir pessoas por esses critérios. Esta issue cria uma Filament Page dedicada em /admin/discover (fora do UserResource): filtros iniciais → botão Buscar → navegação card a card (◀️ Anterior / Próximo ▶️), um perfil por tela. Sem ação de match/curtir/DM nesta fase — só visualização e paginação.
Onde
app-modules/panel-admin/src/Filament/Pages/Discover.php, slug discover.
Usar CommunityRetrospectivePage como referência de filtros com #[Url] + DTO imutável.
Filtros (MVP)
- seniority_level (Select)
- skills (multi-select do catálogo Skill)
- available_for_proposals (Toggle)
- Faixa de level/XP (via Character, min–max)
- Mínimo de 1 filtro, máximo N — não há busca global (sem filtros). Isso mantém os edge cases de "filtros muito restritivos" coerentes e evita busca quase-global sem querer.
- ⚠️ Atenção de performance: um único filtro amplo pode se aproximar de uma busca global (ex.: faixa de XP min 1 / max N retorna praticamente todos os perfis). Combinar com a ordenação por XP + paginação por cursor mitiga, mas confirmar limites/índices (ver Detalhes).
Deferidos para depois (mesma query, plugar quando fizer sentido): localização (país/UF), expected_salary, preferences (remoto/relocação/tipo de contrato), badges, roles Discord, years_experience, busca textual.
Card de navegação
Reaproveita a composição agregada da Task 1 em versão compacta: avatar, headline, seniority, skills, disponibilidade — mais um resumo de gamificação (level, XP, contagem de badges).
Ordenação
- Maior XP primeiro. Destaca membros realmente ativos na comunidade — coerente com a ideia de talent discovery (talentos são quem já contribuiu, em qualquer nível).
- ⚠️ Consideração: ordenar sempre por XP faz perfis de XP alto ficarem sempre no topo, abrindo espaço para o problema clássico de "XP farm" só por status. Não bloqueia o MVP, mas fica registrado como risco a mitigar depois (ex.: desempate/rotação, decaimento, ou fator de recência).
Permissão
- Atrelada a role/policy de quem usa a busca para o propósito real (discovery de talentos — ex.: recrutadores), não liberada a qualquer staff do admin.
- Alinha com a policy de View da Task 1 (recrutadores/captains): mesmo público que pode ver perfis, mas aqui via a página de descoberta.
Fluxo
- Tela de filtros (estado inicial).
- Clica Buscar → query
Profile join User + Character com os filtros acima, ordenada por maior XP → entra em modo card, cursor=0.
- Modo card → Anterior/Próximo navegam de 1 em 1; mostra posição ("N de Total").
Detalhes de implementação
- Query global sobre
user_profiles (sem tenant scoping — já removido).
- Perfil sem contribuição fica fora do resultado: perfis sem Character, ou com Character em level 0 / XP 0, não são "descobertos" (talent discovery pressupõe alguma contribuição). Sub-ponto a confirmar: o threshold exato do level 0 → level 1 (level 0 não é necessariamente zero contribuição, mas level 0 + 0 pontos = ainda não pontuou no sistema → excluído).
- Estado de navegação: especificar explicitamente o mecanismo antes de implementar — a lista de IDs resultante da busca não deve ir inteira para
#[Url] (URL explode em buscas com muitos resultados). Preferir: filtros no #[Url] (compartilhável) + lista de IDs e cursor mantidos em propriedade de estado do componente Livewire (ou cache de sessão), reexecutando a query só quando os filtros mudam. Confirmar Livewire state vs cache antes de codar.
- Performance: o join
user_profiles + User + Character + skills precisa de índices adequados (seniority_level, available_for_proposals, XP/level e chaves de join) — confirmar antes de codar se já existem ou se entram como migration desta issue. Evitar N+1 no card (eager-load de skills/Character por item, não por navegação).
- Registrar a página em
PanelAdminServiceProvider (->pages([...])/->discoverPages(...)) e adicionar em defaultNavigation().
Critérios de aceite (Gherkin)
- Buscar: dado que estou em
/admin/discover, quando aplico ao menos 1 filtro e clico Buscar, então vejo o primeiro perfil como card único com contador 1/N, ordenado por maior XP.
- Filtro mínimo: dado que não apliquei nenhum filtro, quando clico Buscar, então a busca não é executada (busca global não é suportada nesta fase).
- Navegar: dado um resultado com N perfis no perfil i, quando clico Próximo, então vejo i+1 e o contador atualiza; Anterior nunca vai abaixo de 1.
- Edge case — sem resultado: filtros muito restritivos → "Nenhum perfil encontrado — ajuste os filtros", card não renderiza.
- Edge case — fim da lista: no último perfil (N/N), Próximo → "Fim dos resultados", oferece refazer busca; cursor não passa de N.
- Edge case — perfil sem contribuição: perfil sem Character ou com level 0 / XP 0 não aparece no resultado (filtrado fora).
- Nova busca com filtros diferentes reinicia a navegação (cursor volta a 0).
Dependência
#424
Open questions (residuais para triagem)
- Estado de navegação: confirmar o mecanismo de persistência do cursor/lista de IDs (Livewire state vs cache de sessão) antes de implementar.
- Índices: confirmar se as colunas de filtro (
seniority_level, available_for_proposals, XP/level, chaves de join) já têm índice ou se entram como migration nesta issue.
- Threshold level 0 → level 1: definir o corte exato que separa "sem contribuição" (excluído) de "descoberto".
Context
O módulo profile é rico (seniority_level, skills + proficiência, available_for_proposals) e existe Character (level/XP) — mas não há forma de descobrir pessoas por esses critérios. Esta issue cria uma Filament Page dedicada em◀️ Anterior / Próximo ▶️ ), um perfil por tela. Sem ação de match/curtir/DM nesta fase — só visualização e paginação.
/admin/discover(fora doUserResource): filtros iniciais → botão Buscar → navegação card a card (Onde
app-modules/panel-admin/src/Filament/Pages/Discover.php, slugdiscover.Usar
CommunityRetrospectivePagecomo referência de filtros com#[Url]+ DTO imutável.Filtros (MVP)
Deferidos para depois (mesma query, plugar quando fizer sentido): localização (país/UF), expected_salary, preferences (remoto/relocação/tipo de contrato), badges, roles Discord, years_experience, busca textual.
Card de navegação
Reaproveita a composição agregada da Task 1 em versão compacta: avatar, headline, seniority, skills, disponibilidade — mais um resumo de gamificação (level, XP, contagem de badges).
Ordenação
Permissão
Fluxo
ProfilejoinUser+Charactercom os filtros acima, ordenada por maior XP → entra em modo card,cursor=0.Detalhes de implementação
user_profiles(sem tenant scoping — já removido).#[Url](URL explode em buscas com muitos resultados). Preferir: filtros no#[Url](compartilhável) + lista de IDs e cursor mantidos em propriedade de estado do componente Livewire (ou cache de sessão), reexecutando a query só quando os filtros mudam. Confirmar Livewire state vs cache antes de codar.user_profiles+User+Character+skillsprecisa de índices adequados (seniority_level,available_for_proposals, XP/level e chaves de join) — confirmar antes de codar se já existem ou se entram como migration desta issue. Evitar N+1 no card (eager-load de skills/Character por item, não por navegação).PanelAdminServiceProvider(->pages([...])/->discoverPages(...)) e adicionar emdefaultNavigation().Critérios de aceite (Gherkin)
/admin/discover, quando aplico ao menos 1 filtro e clico Buscar, então vejo o primeiro perfil como card único com contador 1/N, ordenado por maior XP.Dependência
#424
Open questions (residuais para triagem)
seniority_level,available_for_proposals, XP/level, chaves de join) já têm índice ou se entram como migration nesta issue.