Skip to content

feat(panel-admin): talent discovery page (/admin/discover) com filtro → navegação 1-a-1 #425

Description

@stherzada

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)

  1. seniority_level (Select)
  2. skills (multi-select do catálogo Skill)
  3. available_for_proposals (Toggle)
  4. 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

  1. Tela de filtros (estado inicial).
  2. Clica Buscar → query Profile join User + Character com os filtros acima, ordenada por maior XP → entra em modo card, cursor=0.
  3. 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".

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions