Context
Não existe UserResource no painel admin. Staff/moderação precisa de uma tela única pra ver e editar um membro por inteiro — hoje os dados de identidade, perfil profissional, gamificação, atividade e moderação estão espalhados entre Character, ExternalIdentity, Profile, Address e ModerationCase.
Esta issue entrega List, Edit e View para os dados que fazem sentido serem editados pelo admin, mantendo como seções agregadas somente-leitura os dados que vêm de outros domínios — respeitando a fronteira presentation/core. Create fica fora de escopo (o fluxo de conta é via OAuth, que deve ser o caminho único e universal de ingresso; criação manual fere esse gate e, como fallback, seria admitir débito técnico).
Onde
app-modules/panel-admin/src/Filament/Resources/Users/
(seguir o padrão em pastas de ExternalIdentities/:
UserResource.php + Schemas/UserForm.php + Schemas/UserInfolist.php + Tables/UsersTable.php + Pages/{List,Edit,View}User.php + RelationManagers/).
Sem CreateUser.php (Create fora de escopo).
Escopo — editável
- Identidade (users): username, name, email, is_donator.
- Perfil (user_profiles): nickname, headline, about, birthdate, seniority_level, years_experience, available_for_proposals, start_availability, expected_salary_min/max, social_links, preferences (remoto/relocação/tipo de contrato/deficiência).
- Skills: RelationManager sobre skills() (pivot proficiency + years_experience).
- Experiências: RelationManager sobre workExperiences().
- Endereço: país/estado/cidade (address()).
Motivação: correção de dado errado informado pelo membro (sobretudo campos únicos/imutáveis), sempre com análise por trás — alternativa auditável ao acesso direto ao banco.
Escopo — agregado / somente leitura
- Gamificação (Character): level, XP, reputação, wallet, badges. Somente leitura — ajuste manual de wallet/XP fere a economia do ecossistema e dá permissibilidade demais ao admin (valores derivados).
- Atividade (ExternalIdentity/DiscordMember→DiscordRole): conexões (Discord/GitHub/Twitch/devto), contagem de mensagens, horas de voice, roles do Discord.
- Moderação (ModerationCase): casos como autor/assignee, suspended_until/banned_at.
Ações
- Soft delete (default): ação de exclusão padrão faz soft delete, impedindo recadastro com os mesmos acessos (não impede criar outra conta). Alinha com a moderação existente: "Ban" ≈ soft delete; "Suspend" ≠ delete (restringe ações por X período).
- Hard delete (restrito): exclusão física existe por compliance/lei (inegociável), mas nunca é o default — ação distinta, gated por permissão específica de compliance, com confirmação explícita.
Detalhes de implementação
- Model do resource =
User::class.
- Delete: habilitar
SoftDeletes no fluxo do Resource; ação padrão faz soft delete, hard delete é ação separada gated por permissão de compliance.
- Autorização: definir Policy (ou Filament Shield) para o UserResource. Edit e Delete (soft) → apenas staff; hard delete → permissão de compliance específica (subconjunto ainda mais restrito); View → pode ser mais amplo (ex.: recrutadores, captains de squad), com a seção de Moderação e demais dados sensíveis ocultos para esses papéis. Campos sensíveis (email, is_donator) só editáveis por staff. "Staff" sozinho não basta como critério da tela inteira — a diferença de permissíveis por seção precisa ser explícita.
- Tabela (index): username, name, seniority_level, available_for_proposals, cidade, level;
->searchable() em username/name/email; ->paginated([25,50,100]); SelectFilter de seniority; TernaryFilter de available_for_proposals.
- Registrar em
PanelAdminServiceProvider::register() e adicionar ...UserResource::getNavigationItems() em defaultNavigation().
- Seções agregadas: ler via relacionamento (
character(), providers(), caso por author/assignee) — sem duplicar lógica de escrita desses domínios dentro do panel-admin.
Critérios de aceite (Gherkin)
- Listagem: dado que sou staff, quando acesso Admin ▸ Usuários, então vejo tabela buscável por name/username/email com level e status (ativo/suspenso/banido/donator).
- Editar: dado que abro um usuário, quando altero campos de identidade/perfil/endereço e salvo, então os dados persistem corretamente (inclusive preferences e social_links).
- Visualizar agregado: dado que estou na tela do usuário, então vejo Gamificação/Atividade/Moderação preenchidas via relacionamento, e nenhum campo dessas seções é editável por aqui.
- Edge case — sem Character/Profile: dado um usuário sem Character ou sem user_profiles, quando abro a tela, então as seções correspondentes mostram estado vazio em vez de quebrar (usar
Profile::ensureExists). Obs.: as 3 formas de acesso atuais (OAuth) já criam ao menos o user_profile, então esse estado é majoritariamente defensivo.
- Soft delete é o default: dado que sou staff, quando excluo pela ação padrão, então o usuário é soft-deleted e não consegue se recadastrar com os mesmos acessos.
- Hard delete é restrito: dado que não tenho a permissão de compliance, então a ação não aparece; dado que tenho, quando a executo, então preciso confirmar explicitamente antes da exclusão física.
- Autorização: dado um usuário sem policy adequada, então não consegue editar (staff-only) nem — quando não autorizado — ver o Resource; papéis de View sem permissão de moderação não veem a seção de Moderação.
- Resource aparece na sidebar do
/admin.
Cobertura de teste
- Save do profile (seguir os testes existentes do módulo profile).
- RelationManager de Skills: attach/detach e edição do pivot (proficiency, years_experience).
- RelationManager de Experiências: create/edit/delete.
- Estado vazio das seções agregadas (Gamificação/Atividade/Moderação) para usuário sem Character/Profile — garantir que a tela não quebra.
- Soft delete: exclusão padrão marca
deleted_at e bloqueia recadastro com os mesmos acessos.
- Hard delete: indisponível sem permissão de compliance; disponível e efetivo (com confirmação) com a permissão.
- Concessão de badge: bloqueada sem evento atrelado; concede via action do domínio quando há evento.
- Autorização: usuário sem policy adequada não deve conseguir editar (ou nem ver) o Resource; papel de View sem permissão de moderação não enxerga a seção de Moderação.
Context
Não existe UserResource no painel admin. Staff/moderação precisa de uma tela única pra ver e editar um membro por inteiro — hoje os dados de identidade, perfil profissional, gamificação, atividade e moderação estão espalhados entre Character, ExternalIdentity, Profile, Address e ModerationCase.
Esta issue entrega List, Edit e View para os dados que fazem sentido serem editados pelo admin, mantendo como seções agregadas somente-leitura os dados que vêm de outros domínios — respeitando a fronteira presentation/core. Create fica fora de escopo (o fluxo de conta é via OAuth, que deve ser o caminho único e universal de ingresso; criação manual fere esse gate e, como fallback, seria admitir débito técnico).
Onde
app-modules/panel-admin/src/Filament/Resources/Users/(seguir o padrão em pastas de
ExternalIdentities/:UserResource.php+Schemas/UserForm.php+Schemas/UserInfolist.php+Tables/UsersTable.php+Pages/{List,Edit,View}User.php+RelationManagers/).Sem
CreateUser.php(Create fora de escopo).Escopo — editável
Motivação: correção de dado errado informado pelo membro (sobretudo campos únicos/imutáveis), sempre com análise por trás — alternativa auditável ao acesso direto ao banco.
Escopo — agregado / somente leitura
Ações
Detalhes de implementação
User::class.SoftDeletesno fluxo do Resource; ação padrão faz soft delete, hard delete é ação separada gated por permissão de compliance.->searchable()em username/name/email;->paginated([25,50,100]);SelectFilterde seniority;TernaryFilterde available_for_proposals.PanelAdminServiceProvider::register()e adicionar...UserResource::getNavigationItems()emdefaultNavigation().character(),providers(), caso porauthor/assignee) — sem duplicar lógica de escrita desses domínios dentro do panel-admin.Critérios de aceite (Gherkin)
Profile::ensureExists). Obs.: as 3 formas de acesso atuais (OAuth) já criam ao menos o user_profile, então esse estado é majoritariamente defensivo./admin.Cobertura de teste
deleted_ate bloqueia recadastro com os mesmos acessos.