chore(events): merge 4.x e remove tenancy do módulo de eventos - #482
Merged
Conversation
…hi) (#388) ## Summary - Adiciona selects dinâmicos de **país**, **estado** e **cidade** na página de perfil (`ProfilePage`), com busca assíncrona e dependência entre os campos - Países e estados usam **Sushi** com cache Laravel sob demanda (sync lazy via jobs); cidades ficam em **Postgres** (`geo_cities`) e são buscadas na API apenas quando necessário - Integração com a [World API](https://world.bmbc.cloud/api) (`WorldApiClient` + `config/geo.php`), sem seedar o mundo inteiro no banco - Normaliza colunas de endereço existentes: ISO3 → ISO2, UF brasileira → nome completo do estado, `state` expandido para 100 chars - Tooltip na cidade orientando o usuário a pesquisar caso a cidade não apareça na listagem ## Arquitetura | Camada | País / Estado | Cidade | |--------|---------------|--------| | Modelo | `GeoCountry`, `GeoState` (Sushi) | `GeoCity` (Eloquent/Postgres) | | Cache | `GeoSushiStore` (Laravel cache) | tabela `geo_cities` | | Sync | `SyncWorldCountriesJob`, `SyncWorldStatesJob` | `SyncWorldCitiesJob` | | UI | `GeoSelect` (helpers para Filament selects) | busca local + API com `GeoSearch` (ignora acentos) | ## Migrations - `2026_07_07_180000_create_geo_tables.php` — cria `geo_cities` - `2026_07_07_180001_normalize_address_location_columns.php` — normaliza dados legados em `addresses` ## Config ```env GEO_WORLD_API_URL=https://world.bmbc.cloud/api Test plan - [ ] Abrir perfil e verificar preload de países - [ ] Selecionar país → estados carregam; selecionar estado → cidades do cache aparecem - [ ] Buscar cidade com/sem acento (ex.: sao → São Paulo) - [ ] Salvar perfil e confirmar persistência em addresses (country ISO2, state nome, city nome) - [ ] Rodar php artisan test tests/Feature/Geo tests/Feature/AddressTest.php app-modules/panel-app/tests/Feature/ProfilePageTest.php --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: danielhe4rt <danielhe4rt@gmail.com> Co-authored-by: BrunaDomingues <bruna@users.noreply.github.com>
## Problema
O feed da timeline retorna **500** com `Attempt to read property "name"
on null` em
`panel-app/resources/views/components/timeline/header.blade.php` sempre
que o autor de um post resolve para `null`. Como o feed renderiza todos
os posts, **um único post órfão derruba a página inteira** para todos.
## Causa raiz — merge de contas
Quando duas contas são mergeadas, `MergeAccountsAction` reatribui
`ExternalIdentity` e tenants para a conta sobrevivente e faz **hard
delete** da conta perdedora — mas **nunca reatribui
`activity_timeline.user_id`**. Não existe FK nessa coluna, então os
posts da conta mergeada ficam órfãos e o feed quebra.
```
MergeAccountsAction (identity) activity module
reatribui ExternalIdentity + tenants
event(AccountsMerged(survivor, merged)) ──► ReassignTimelineOwnership (listener)
currentUser->delete() UPDATE activity_timeline
SET user_id = survivor
WHERE user_id = merged
```
## Fix — 3 camadas (defense-in-depth)
1. **Merge (causa raiz):** novo evento `AccountsMerged` disparado dentro
da transação do merge; listener em `activity` reatribui as linhas da
timeline para o sobrevivente. **Preserva os posts** e previne órfãos
futuros. Feito via evento para não criar dependência circular entre
módulos (`activity` já depende de `identity`). `activity_reactions` não
tem `user_id`, então a timeline é a única tabela a reatribuir.
2. **Filtro do feed:** `TimelineFeed` usa `whereHas('user')` para
blindar o feed contra os **órfãos que já existem em prod** (o mapeamento
id-antigo→novo se perdeu no hard delete e não dá pra reatribuir
automaticamente).
3. **View guard:** `header`/`post-show` toleram autor null e mostram
"Usuário removido" em vez de quebrar.
## Testes
22/22 verdes (3 novos + os existentes de timeline/merge):
- `MergeAccountsActionTest` — merge reatribui os posts ao sobrevivente
(exercita evento→listener ponta a ponta).
- `TimelineFeedQueryTest` — feed exclui posts de autor inexistente.
- `ThreadPageTest` — `PostShow` renderiza sem quebrar quando o autor foi
deletado.
## Observação
Os órfãos **já existentes** em produção continuam no banco — o filtro só
os esconde do feed. Uma limpeza/reatribuição retroativa precisaria de
uma fonte pro mapeamento (logs / `ExternalIdentity`) e fica fora deste
PR.
## Summary - Corrige visibilidade do círculo de iniciais no dark mode (header e preview card do perfil) - Evita que o hover de upload da capa fique por cima do avatar - Melhora visibilidade do avatar no menu lateral no dark mode - Adiciona borda discreta na sidebar, no mesmo peso das bordas dos inputs do form **Antes:** no dark mode, o círculo das iniciais quase sumia no perfil; o hover de upload da capa cobria parte do avatar; o avatar do menu lateral ficava sem contorno visível; e não havia separação clara entre sidebar e conteúdo. <img width="1906" height="907" alt="Captura de tela 2026-07-08 131101" src="https://github.com/user-attachments/assets/bfa3d0d3-0975-4d3d-b1d6-f02cc1ff067e" /> **Depois:** iniciais com borda/ring visível no header e no preview card; avatar sempre acima do hover da capa; contorno discreto no avatar do menu lateral; e borda suave na sidebar, no mesmo peso das bordas dos inputs do form. <img width="1906" height="911" alt="Captura de tela 2026-07-08 130813" src="https://github.com/user-attachments/assets/5faa3fc0-7819-4aa6-9d91-552868f72c5f" /> ## Commits 1. `fix(profile): keep initials avatar visible in dark mode preview card` 2. `fix(profile): keep initials avatar visible in dark mode header` 3. `fix(profile): keep avatar above cover upload hover overlay` 4. `fix(panel): keep sidebar user avatar visible in dark mode` 5. `style(panel): add subtle sidebar border` *(fácil de reverter isoladamente se necessário)* ## Test plan - [ ] Abrir `/app/{tenant}/profile` em light e dark mode - [ ] Sem foto: conferir se o círculo com iniciais aparece no header e no preview card - [ ] Passar o mouse na capa: hover não deve cobrir o avatar - [ ] No menu lateral: avatar do usuário visível no dark mode - [ ] Conferir se a borda da sidebar está discreta (similar aos inputs do form) --------- Co-authored-by: Cursor <cursoragent@cursor.com>
> 📄 <strong><a href="https://waifuvault.moe/f/b2e0aef7-16c7-4c95-9dcf-7e51626f7ddb.html" target="_blank" rel="noopener noreferrer">Visual overview of this PR</a></strong> — one-screen interactive summary of every change. > [!NOTE] > **Why `tests/.pest/shards.json` is committed — it's intentional.** Pest v4.6.0 balances the suite across shards using recorded per-test timings. Committing this baseline (instead of regenerating it every run) makes the split **deterministic and evenly balanced from the first CI run** — no warm-up pass and no cross-run artifact upload to wire up. It's refreshed automatically as the suite grows. See [Time-based sharding in Pest v4.6.0](https://laravel-news.com/time-based-sharding-in-pest-v4-6-0). ## Summary - **Guidelines** — sync AI guidelines to he4rt conventions (namespace/naming, issue-tracker repo `he4rt/heartdevs.com`, added typed-JSON-cast + enum-contract guidance); drop 3 stale `docs/agents/*` mirrors. - **Modular** — fix `make:module` stubs + `config/app-modules.php` so a module's ServiceProvider scaffolds at `src/` root (not `Providers/`); migrate `squads` and `onboarding` to match. - **Testing DX** — add tracked `.env.testing.example`, untrack the per-dev `.env.testing`, bootstrap it via `post-root-package-install`; use `LazilyRefreshDatabase` for feature tests. - **CI** — run the Pest suite in 2 balanced shards (committed timings baseline); gate the pipeline on canonical branch prefixes (`feature/**`, `bugfix/**`, `chore/**`, `story/**`, `*.x`). - **Deps** — pin intra-repo `he4rt/*` modules to `^1.0.0`; bump dependencies. - **Refactor** — apply the Rector named-argument convention across models, commands, jobs and Livewire components. - **Cleanup** — remove 24 redundant `.gitkeep` placeholders. ## Test plan - [ ] `composer check` — Rector, Pint, PHPStan all clean - [ ] `php artisan test` — 881 pass (539 feature + 342 unit, 2515 assertions) - [ ] Fresh clone: `make setup` creates `.env.testing`; `make setup-test-db` migrates it - [ ] Scaffolding a new module places its ServiceProvider at `src/` root
Arquivo-fonte referenciado por config/app-modules.php para os placeholders .gitkeep que o internachi/modular copia para cada novo módulo (tests e database). O #402 removeu o arquivo, o que quebrava o comando make:module ao tentar copiar um stub inexistente.
Escrito pelo 7k galera
…nadas (#400) Closes #394 ## Summary No repeater de skills do perfil, uma skill já escolhida em outra linha continuava aparecendo no dropdown das demais linhas, podendo ser selecionada de novo. Agora ela é removida dos resultados da busca — cada skill só pode ser escolhida uma vez. ## What Changed - `Skill::search` ganhou o parâmetro `exclude`, que filtra os IDs direto na query (`whereNotIn`, antes do `limit`). - `ProfilePage::skillIdsInSiblingRows` coleta os IDs já selecionados nas outras linhas do repeater (via `$get`) e os passa ao `Skill::search` no `getSearchResultsUsing` do campo `skill_id`. ## Tests - Novo `SkillSearchTest` cobrindo o `exclude` do `Skill::search`. --------- Co-authored-by: Elves Santos <168841287+sirelves@users.noreply.github.com> Co-authored-by: Daniel Reis <danielhe4rt@gmail.com>
## Summary - Add a `make libdave` target that symlinks the libdave (DAVE E2EE) install into the `.cache/libdave` probe path, so the Discord bot boots without the `failed to initialize voice class / LibDaveNotFoundException` error (DAVE is mandatory for voice since 2026-03-01). - Symlinking the probe path avoids relying on `DISCORDPHP_DAVE_LIBRARY`, which the Forge daemon (Supervisor ignores shell rc) and `config:cache` (skips `.env`) both defeat. - gitignore `/.cache` (machine-local, like `/vendor`). Deploy: run `make libdave` after `composer install` in the Forge deploy script.
## Decisões de arquitetura
### Renderer do mapa: `chartjs-chart-geo` (MIT), não Highcharts
O mockup inicial usava Highcharts, mas ele exige **licença comercial
paga** para
uso não-pessoal. Trocamos pelo `chartjs-chart-geo` (MIT, grátis), que
reusa o
Chart.js que os dashboards já carregam. Resultado visual equivalente,
sem custo
de licença.
### Geometria: GeoJSON do IBGE versionado (não uma API em runtime)
Fronteira de estado é **dado estático de referência** — como uma fonte
ou um
SVG. Por isso versionamos um arquivo (padrão que o módulo já usa em
`resources/fonts/`) em vez de buscar de uma API a cada request (que
adicionaria
dependência de rede/CORS numa página core, sem ganho, já que a geometria
não
muda). Origem: **IBGE** (dado aberto, oficial), simplificado offline
(topojson-simplify + arredondamento) de ~251 KB para ~83 KB, com as
propriedades
reescritas para `{ name, uf }`. Não usamos a malha da
Highsoft/Highcharts
(proprietária). Os anéis foram reorientados para **CW** para casar com a
convenção esférica do d3-geo (senão o mapa preenche a tela inteira).
### Nomes e contagens: domínio, não o arquivo do mapa
- **Nomes canônicos dos 27 estados** vêm de `GeoLocation` (World API) —
a mesma
fonte que o perfil usa —, cacheados via Redis (`Cache::remember`, 30
dias),
padrão idêntico ao `App\Geo\Models\GeoCountry`.
- **Contagens** vêm da tabela `addresses` (domínio identity).
- A reconciliação entre o valor salvo em `addresses.state` e o mapa é
feita por
**nome normalizado, sem acento** (mesma normalização no PHP e no JS).
Medimos:
os nomes do World API batem 27/27 com os do GeoJSON.
- O GeoJSON é usado **só para desenhar** — se apagado, só o mapa quebra;
os
números continuam corretos.
### Widgets nativos do Filament, não grid manual
A divisão da tela usa a grade nativa de widgets do Filament
(`getHeaderWidgetsColumns()` + `$columnSpan`), não um grid Tailwind na
Blade.
Motivo: as classes de grid custom dependiam de rebuild do CSS (davam
purge e
quebravam o layout); a grade do Filament emite CSS inline via custom
properties,
imune a isso.
### Retornos tipados com DTOs, não array shapes
As queries retornam **value objects** (`readonly`) em vez de
`array{...}`. Segue
o guideline (`prefer DTOs/VOs over associative arrays`), dá autocomplete
e mata a
chave-string mágica. Os valores derivados (`webTrend()`,
`discordTrend()`,
`coverage()`) ficam **dentro** do `CommunityActivitySnapshot`, tirando
lógica de
apresentação do widget. (`BrazilStatesGeometry` segue retornando array —
é o
GeoJSON cru consumido pelo `@js`, onde array é o formato natural.)
### Definição das métricas
- **Ativos na web**: `user_id` distintos com **post ou reply** em
`activity_timeline` nos últimos 30 dias.
- **Ativos no Discord**: `external_identity_id` distintos com mensagem
em
`messages` nos últimos 30 dias.
- **vs período anterior**: rolling comparison — janela atual (últimos
30d) contra
a janela imediatamente anterior (30–60d atrás), mesmo padrão do Discord
Dashboard.
- **Membros com localização** / **Estados alcançados**: derivados de
`addresses`.
## Test plan
- [ ] `php artisan test app-modules/panel-admin/tests` — verde (Pest)
- [ ] Pint, Rector (dry) e PHPStan (level 7) sem erros
- [ ] `/admin/{tenant}/location` renderiza: 4 stat cards, mapa por
estado e top-5
- [ ] Mapa: hover mostra tooltip `Estado (UF)` + nº e %, siglas
centralizadas,
tooltip não corta nas bordas (mobile)
- [ ] Layout responsivo: cards empilham no mobile, dividem 3/2 no
desktop
https://github.com/user-attachments/assets/cc3440c8-fff4-467d-945c-f5030cc389f1
---------
Co-authored-by: Daniel Reis <danielhe4rt@gmail.com>
## Contexto Esta PR **remove por completo o multi-tenancy** da plataforma. O modelo de tenants (guilds do Discord / orgs de evento) adicionava complexidade de escopo em praticamente todos os módulos sem entregar valor real para o produto atual, que opera como uma única comunidade. ## O que muda - **`feat(tenancy)!` — BREAKING**: dropa colunas `tenant_id`, colapsa índices únicos que dependiam do tenant e remove as tabelas de tenant. - Remove o model `Tenant` e limpa `tenant_id` dos domain models. - Tira a tenancy dos painéis Filament e as rotas de tenant. - Desacopla o fluxo de OAuth e o bot do Discord do tenant. - Passa a ler credenciais do bot/Twitch da **config** em vez do tenant. - Remove scaffolding morto (`GenerateDiscordTenant`, `GuestTenantIdentifier`, etc). - Ajusta suíte de testes: remove `TenantFactory`, bootstrap de tenant e `tenant_id` das factories. ## Migrations - `2026_07_07_000000_drop_multi_tenancy` — drop de colunas/índices/tabelas. - `2026_05_01_200459_delete_orphaned_tenant_records` e `2026_03_21_192332_deduplicate_external_identities` — limpeza de dados órfãos antes do drop. ## Impacto - **239 arquivos**, `+822 / -2808` (remoção líquida de ~2k linhas). - Suíte verde localmente (805/805) com a migration aplicada. ##⚠️ Atenção para review / deploy - Migration **destrutiva e irreversível** — exige backup antes de rodar em produção. - Credenciais do bot/Twitch agora vêm de `config` (`config/services.php`, `config/he4rt.php`) — conferir env vars no ambiente antes do deploy. ## Checklist - [x] Testes passando localmente - [x] Migration aplicada local - [ ] Revisão da migration destrutiva - [ ] Backup de produção antes do merge/deploy
## Resumo Refina o cluster Discord do painel admin, que estava no estado cru do scaffold do CLI. Todos os 5 resources viram **somente leitura** (List + View — os dados são espelho do Discord sincronizado pelo bot/ETL; edições seriam sobrescritas no próximo sync), ganham tabelas calibradas com os payloads reais de produção, i18n en/pt_BR e um dashboard com widgets. Plano completo (com as decisões tomadas): `app-modules/panel-admin/docs/plans/2026-07-12-refinamento-cluster-discord.md`. ## Destaques - **Event logs (237k linhas)**: índice novo em `created_at`, badges por categoria de evento, filtros de tipo/período, payload JSON pretty na view com link cruzado pro membro. Mantém cursor pagination de propósito (evita `COUNT(*)`). - **Channels**: agrupados por categoria espelhando a sidebar do Discord (categorias viram títulos de grupo), tipo como badge via contratos Filament no enum `DiscordChannelType`. - **Members (26k)**: ativos por padrão (filtro ternário em `left_at`), avatares via CDN com fallback derivado do snowflake, busca global corrigida para username/global_name/nickname. - **Roles**: cor decimal → swatch hex, ordenação pela hierarquia do Discord (`position desc`). - **Guild**: relation managers de Channels/Roles/Members com links cruzados. - **Dashboard**: stats (membros ativos, entradas/saídas 7d, eventos 24h, boosters, canais), eventos por dia e crescimento de membros (fonte `discord_members`, sobrevive a prune futuro de event logs). ## Fix notável O painel configura `PaginationMode::Cursor` **globalmente**; cursor pagination quebra com `ORDER BY` por expressão (grouping por relacionamento e sorts por agregado) — 500 real reproduzido em dev com dados de prod. Channels/Members/Roles fazem override para length-aware, com teste de regressão de >1 página em tabela agrupada. ## Como foi feito Workflow multi-agente: plano por Fable, implementação paralela por Sonnet (fundações → resources → drill-down → testes), revisão adversarial final por Fable (4 issues encontradas e corrigidas, incl. `formatStateUsing` morto em `CodeEntry` → `jsonFlags()` nativo). ## Testes - Suite completa: **876 passed (2512 assertions)** com `--parallel --update-shards`. - 14 smoke tests novos em `app-modules/panel-admin/tests/Feature/Discord/`. - Pint ✅ · PHPStan 0 erros ✅ · migration aplicada e índice confirmado ✅
#415) ## Contexto Ao dar setup da Twitch em produção (foco: EventSub pra detectar lives), o fluxo de conectar/registrar esbarrou em bugs latentes do módulo `integration-twitch` — o módulo nunca tinha sido exercido com env incompleta. Esta branch adiciona um botão de conexão em um clique, corrige os crashes e remove a necessidade do `TWITCH_BROADCASTER_ID`. ## Commits ### `feat(panel-admin)` — botão "Connect Twitch" (1 clique) Header action na página **Twitch → Subscriptions** que inicia o OAuth link flow apontando pra `route('oauth.redirect', panel: admin, provider: twitch)`. Como o admin já está logado, vincula a Twitch ao usuário atual (intent `Link`) com o scope set de admin. Rótulo dinâmico: **Connect** / **Reconnect (login)**. ### `fix(integration-twitch)` — hardening contra env incompleta Três crashes pré-existentes: - **`TypeError` no redirect** — `TwitchHelixConnector` buscava o app token no construtor; só montar a URL de redirect já forçava o fetch. Agora o token é resolvido **lazy** no `defaultAuth()`. - **`getToken()` retornava `null`** — closure tipada `: string`. Passa a lançar `OAuthFlowException` (capturada pelo `OAuthController`) + protege `expires_in`. - **`config()->string()` estourava em `null`** — `TWITCH_EVENTSUB_CALLBACK` unset. Defaults `''` no config + leitura null-safe. ### `feat(integration-twitch)` — broadcaster vem do OAuth do admin Remove a dependência de `TWITCH_BROADCASTER_ID`. O broadcaster passa a ser a conta Twitch conectada ao admin autenticado (via `ExternalIdentity`), eliminando a divergência entre o que o modal exibia (ExternalIdentity) e o que de fato registrava (config). - `RegisterTwitchSubscriptionsAction` recebe o broadcaster id como argumento; a presentation resolve o "quem". - `RegisterSubscriptionsAction` resolve a Twitch do admin logado e bloqueia com aviso quando não há nenhuma. - `twitch:link-channel` passa a imprimir o comando `twitch:subscribe` em vez do env removido. ## Testes Suíte Twitch verde (integration-twitch + panel-admin). Testes de regressão de UI do modal `RegisterSubscriptions` com o broadcaster do usuário autenticado ficaram de fora por ora (issue de resolução de componente Livewire em teste) — a lógica de domínio está coberta em `RegisterTwitchSubscriptionsActionTest`. ## Setup de produção pendente (fora do código) - `TWITCH_OAUTH_CLIENT_SECRET` válido (a Twitch retornou `403 invalid client secret` em dev). - Registrar o redirect `https://<dominio>/auth/oauth/twitch` no dev console da Twitch. - Conectar a Twitch pelo novo botão → `twitch:subscribe <id> --type=stream.online` (o id agora vem do OAuth).
## Summary - Esconde da retrospectiva os repos sem PR no recorte (só review/issue/comentário): o card some, mas as contribuições seguem contando em `meta`/`people`/`highlights`. - Remove o empty-state morto "Sem PRs nesse recorte…" do card de repo. - Panorama passa a mostrar "em N repositório(s)" (repos com PR = cards exibidos) no lugar de "N arquivos tocados"; plural via `@choice`. ## Test plan - [x] `php artisan test --compact --filter=CommunityRetrospective` (30 passed) - [x] Abrir `/comunidade/retrospectiva` num recorte com repo só de review/issue/comentário (ex.: `gvieira18/sycorax`) → card sumiu, stats gerais iguais - [x] Panorama: contagem de repositórios bate com o nº de cards e pluraliza (1 → "repositório", N → "repositórios")
## Contexto O `drop_multi_tenancy` (#413) colapsou corretamente os índices **únicos** tenant-scoped, mas dropou ~17 índices **secundários** (de performance) sob o comentário _"no collapsed replacement needed"_ — sem nunca recriar o equivalente sem `tenant_id`. Resultado: as páginas de **Marketing** do painel admin rodam 7+ agregações por load como *sequential scans* sobre as maiores tabelas do banco (`messages`, `voice_messages`, `activity_timeline`). Dois achados extras da investigação: 1. **Bug no drop**: `idx_github_contributions_type_time (type, occurred_at)` nunca teve `tenant_id` (a integração GitHub nunca teve tenancy), mas foi dropado mesmo assim — o comentário na migration o rotula errado como índice de tenant. 2. **Colunas quentes nunca indexadas**: `voice_messages (occurred_at, state)` e `addresses (state)` nunca tiveram índice, mas os dashboards de marketing filtram por elas em todo load. ## O que muda Quatro migrations puramente aditivas (uma por módulo dono da tabela): | Módulo | Tabela | Índice novo | |---|---|---| | activity | messages | `(sent_at)`, `(channel_id, sent_at)`, `(kind)` | | activity | voice_messages | parcial `(occurred_at) WHERE state = 'joined'` | | activity | activity_timeline | `(created_at)`, `(parent_id, is_ignored, created_at)` | | activity | activity_reactions | `(emoji_key, created_at)` | | activity | message_mentions | `(mentioned_identity_id)`, `(mentioned_provider_account_id)` | | activity | message_embeds | `(source_domain)`, `(kind)` | | activity | membership_events | `(kind, occurred_at)` | | activity | moderation_events | `(type, occurred_at)` | | activity | interactions | `(occurred_at)` | | moderation | moderation_cases | `(status, priority, created_at)` | | moderation | moderation_rules | `(is_active)` | | moderation | moderation_audit_log | `(created_at)` | | integration-github | github_contributions | `(type, occurred_at)` — restaura o drop indevido | | (app root) | addresses | `(addressable_type, state)` | Não recriados: `twitch_subscriptions_tenant_id_index` (só tenant, nada sobra) e o composto de `external_identities` (já coberto pelo standalone `(model_type, model_id)`). ## Verificação - `php artisan migrate` local: 19 índices criados, confirmados em `pg_indexes`; re-run é no-op (`IF NOT EXISTS`). - `EXPLAIN` em `github_contributions` (única tabela local com volume): `Index Only Scan using idx_github_contributions_type_time`. - Pint, PHPStan e Rector limpos na raiz; suíte `--filter=Marketing` verde (5 testes, 35 assertions).
## Contexto Este PR implementa a solução da issue #430. Atualmente, a seção **Skills** da página de Perfil (`/profile`) não disponibiliza frameworks e ferramentas de automação de testes entre as opções cadastráveis. Isso dificulta que profissionais de Quality Assurance (QA) representem corretamente suas competências técnicas na plataforma. Para resolver esse problema, este PR adiciona ao seed de skills as principais ferramentas utilizadas pelo mercado de QA, permitindo que elas passem a ser selecionáveis no perfil dos usuários. ## Alterações Adiciona novas skills relacionadas à área de Quality Assurance, incluindo ferramentas de: - **Testes Web (E2E/UI)** - Selenium - Cypress - Playwright - WebdriverIO - TestCafe - Puppeteer - **Testes Mobile** - Appium - Espresso - XCUITest - Detox - **Testes de API** - Rest Assured - Postman - Newman - Karate - Supertest - **BDD** - Cucumber - SpecFlow - Behave - Robot Framework - **Testes Unitários e de Integração** - JUnit - TestNG - PyTest - Jest - Mocha - Chai - NUnit - xUnit - **Testes de Performance** - JMeter - K6 - Gatling - Locust ## Motivação - Permite que profissionais de QA representem melhor seu stack técnico. - Amplia o catálogo de skills disponíveis na plataforma. - Facilita a busca por profissionais com conhecimentos específicos em ferramentas de testes. - Mantém o catálogo de skills centralizado e versionado por meio dos seeds, facilitando futuras atualizações. ## Test Plan - Executar os seeders da aplicação. - Verificar que as novas skills foram inseridas corretamente no banco de dados. - Confirmar que as ferramentas aparecem como opções na seção **Skills** da página de Perfil (`/profile`). - Validar que o seeder pode ser executado novamente sem criar registros duplicados (quando aplicável).
## Contexto Melhora a interação com quem entra na comunidade. A mensagem pública no canal `auto-report` **continua igual** — a mudança toca só a **DM enviada ao novo membro**, que antes era uma cópia byte-a-byte do post do canal. > Sugestão da @kalanedev ## O que muda DM com a marca He4rt e um **CTA único** pra ativar o `/apresentar`: - Embed roxo He4rt (`#782bf1`), thumbnail = ícone do servidor, footer `© He4rt Developers` + timestamp. - Descrição de acolhimento **personalizada com o nome** do membro + o que é a He4rt. - `field` dedicado **🙋 Comece se apresentando** com o passo-a-passo do `/apresentar`. - 3 botões: **✍️ Me apresentar** (deep-link pro canal de apresentações) · **🌐 Portal** · **🔗 Nossas redes**. ## Resiliência (DM fechada) Muitos usuários bloqueiam DM de membros do servidor → a promise do `sendTo()` **rejeita**. Agora tratamos com `->catch()`: - Loga `warning` no canal de log `bot-discord`. - Posta um **follow-up público no `#geral`** com o mesmo embed + botões e uma menção que **notifica** a pessoa (via `body()`, acima do embed). - Se `HE4RT_GERAL_CHANNEL_ID` não estiver setado, degrada silenciosamente pra só-logar. ## Detalhes técnicos - Corrige a semântica `content()` (dentro do embed) × `body()` (texto acima, onde menção pinga) da API do Laracord. - Extrai `WelcomeMember::guildIconUrl()` (puro) pra montar a URL do ícone do servidor no CDN do Discord (trata `a_…` → `.gif`), com teste unitário. ##⚠️ Config necessária Adicionar ao `.env` de produção: \`\`\` HE4RT_GERAL_CHANNEL_ID=<id do canal #geral> \`\`\` ## Testes - `WelcomeMemberGuildIconTest` (4 casos: png, gif animado, sem ícone, sem guild) — verdes. - Pint + PHPStan passam nos arquivos tocados. - Camada de transporte (envio Discord) não é testada — segue a convenção do módulo (o `IntroductionCommandTest` também só testa o lado de domínio). ## Como testar manualmente 1. Setar `HE4RT_GERAL_CHANNEL_ID`. 2. Entrar no servidor com DM aberta → recebe a DM rica. 3. Entrar com DM fechada → recebe o follow-up no `#geral` com menção.
O login com qualquer OAuth (Discord/GitHub/Twitch) não guardava a
informação de login porque a variável remember não era passada no
controller. Assim a sessão expirava com base no SESSION_LIFETIME — não
sei qual o valor em prod, mas no .env.example está 120, ou seja, 2 horas
após o login — e o usuário era deslogado, com o painel pedindo para
reconectar o Discord.
Resolvi isso passando o remember como true por padrão no OAuth
- Auth::login($result->user);
+ Auth::login($result->user, remember: true);
Testei localmente deletar a tabela de sessões
(php artisan tinker --execute="DB::table('sessions')->delete();") para
reproduzir as informações do perfil desaparecendo, mas não consegui
reproduzir essa parte. Ainda assim, o remember por padrão deve resolver
esse comportamento.
Closes #409
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Daniel Reis <danielhe4rt@gmail.com>
#435) ## Contexto Este PR implementa a solução da issue #428 . Atualmente, o upload de avatar e capa na página de Perfil (`/app/profile`) não oferece nenhuma opção de recorte antes do salvamento. Como consequência, imagens com proporções diferentes das esperadas podem ser exibidas de forma distorcida, esticada ou com cortes indesejados. Para melhorar a experiência do usuário e garantir consistência visual entre os perfis, o fluxo de upload foi atualizado para permitir o recorte e redimensionamento das imagens antes do envio. ## Alterações - Substitui o componente de upload do avatar pelo `FileUpload` do Filament. - Adiciona suporte ao editor de imagens para permitir o recorte (crop) antes do upload. - Configura proporções específicas para cada tipo de imagem: - Avatar com proporção quadrada. - Capa/banner com proporção widescreen. - Configura redimensionamento automático das imagens para as dimensões esperadas pela aplicação. - Exibe o avatar do usuário no feed quando houver uma imagem de perfil cadastrada. - Adiciona traduções ausentes para os novos textos e componentes utilizados, garantindo suporte aos idiomas disponíveis na plataforma. ## O que mudou 🧐 ### Removido - Propriedades públicas `$avatarUpload` e `$coverUpload` com `#[Validate]` - Método privado `saveMedia()` (chamado dentro de `save()`) - Chamada `$this->saveMedia()` no fluxo de salvamento do perfil - Lógica de `temporaryUrl()` nos computed `avatarPreviewUrl` / `coverPreviewUrl` - Reset das propriedades (`$this->avatarUpload = null`) nos métodos `removeAvatar` / `removeCover` - Import de `Livewire\Attributes\Validate` ### Adicionado - `editAvatarAction(): Action` — abre modal com `FileUpload` configurado com editor circular (1:1), `storeFiles(false)`, e persiste imediatamente via `replaceMedia()` - `editCoverAction(): Action` — abre modal com `FileUpload` configurado com editor de imagem (3:1), redimensionamento automático para 1800×600, e persiste imediatamente via `replaceMedia()` - `replaceMedia(string $collection, TemporaryUploadedFile $file): void` — método unificado que limpa a coleção e adiciona a nova mídia com fallback de extensão (`getClientOriginalExtension() ?: guessExtension() ?: 'jpg'`) - Import de `Filament\Forms\Components\FileUpload` ## Motivações - Melhora a experiência de upload de imagens. - Garante maior consistência visual entre os perfis. - Evita imagens achatadas, esticadas ou cortadas de forma inesperada. - Permite ao usuário visualizar e ajustar o enquadramento da imagem antes de salvá-la. - Torna o feed mais personalizado ao exibir o avatar dos usuários. ## ANTES <img width="1280" height="579" alt="antes" src="https://github.com/user-attachments/assets/001f550e-beb4-4e6a-9fe6-c8cf1435f4e4" /> ## DEPOIS <img width="1280" height="579" alt="chrome-capture-2026-07-18" src="https://github.com/user-attachments/assets/26f8e3b1-81cc-47fd-94bd-0490641b88cd" />
## Summary - Configure the Vite `build` block: terser JS minification, CSS minification, and per-package `manualChunks` (via `rolldownOptions`, since this repo runs Vite 8 + rolldown) for better long-term vendor caching. - Add `terser` as an explicit build dependency required by the terser minifier. - Bump ambient JS and PHP dependencies that were pending in the working tree. ## Test plan - [ ] `npm run build` completes without error and does not emit `terser not found` - [ ] Buildable output lands in `public/build/` and main screens still render
#445) Closes #436 ## feat(portal): Adicionar botão de redirecionamento para loja da He4rt Adiciona um botão na navbar do portal que redireciona para a loja da He4rt (`https://loja.heartdevs.com/he4rt/`), tanto na versão desktop quanto mobile. ### Mudanças **Desktop (navbar principal):** Ícone de sacola de compras (`heroicon-s-shopping-bag`) com `aria-label="Loja"`, exibido apenas como ícone ao lado dos ícones de redes sociais e Discord. **Mobile (menu hamburguer):** Botão com ícone + texto "Compre na Loja", dentro da lista de links do menu mobile. ## Preview Desktop: - Novo ícone de sacola entre "Redes sociais" e "Área do Usuário" <img width="1063" height="91" alt="Screenshot 2026-07-22 at 23 00 46" src="https://github.com/user-attachments/assets/7c44555a-fdc8-4542-a4fa-069f32385f25" /> Mobile: - Novo item "Compre na Loja" entre "Redes sociais" e "Área do Usuário" <img width="333" height="268" alt="image" src="https://github.com/user-attachments/assets/2a5f7989-3548-4d3b-a6c4-e51e14d9488f" /> Redes: - Novo item "Loja" no final da página <img width="539" height="626" alt="image" src="https://github.com/user-attachments/assets/1e8f8972-b49a-46b2-8273-421564e93a20" />
Update Filament (5.7.3), Laravel framework (13.21.1), Sanctum, Telescope, medialibrary, phiki, and npm dev deps (lint-staged, prettier).
## Summary - Bump Filament and media-library plugin to `^5.7.3` - Bump Laravel framework to `^13.21.1`, Sanctum `^4.3.3`, Telescope `^5.21.0` - Bump `spatie/laravel-medialibrary` `^11.23.3` and `phiki/phiki` `^2.2.1` - Bump npm dev deps: `lint-staged` `17.2.0`, `prettier` `3.9.6` - Lockfiles (`composer.lock`, `package-lock.json`) updated to match
Bumps [actions/checkout](https://github.com/actions/checkout) from 7.0.0 to 7.0.1. <details> <summary>Release notes</summary> <p><em>Sourced from <a href="https://github.com/actions/checkout/releases">actions/checkout's releases</a>.</em></p> <blockquote> <h2>v7.0.1</h2> <h2>What's Changed</h2> <ul> <li>skip running unsafe pr check if input is default by <a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2518">actions/checkout#2518</a></li> <li>trim only ascii whitespace for branch by <a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2521">actions/checkout#2521</a></li> <li>escape values passed to --unset by <a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2530">actions/checkout#2530</a></li> <li>Various dependency updates</li> </ul> <p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v7...v7.0.1">https://github.com/actions/checkout/compare/v7...v7.0.1</a></p> </blockquote> </details> <details> <summary>Changelog</summary> <p><em>Sourced from <a href="https://github.com/actions/checkout/blob/main/CHANGELOG.md">actions/checkout's changelog</a>.</em></p> <blockquote> <h1>Changelog</h1> <h2>v7.0.1</h2> <ul> <li>Skip running unsafe pr check if input is default by <a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2518">actions/checkout#2518</a></li> <li>Trim only ascii whitespace for branch by <a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2521">actions/checkout#2521</a></li> <li>Escape values passed to --unset by <a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2530">actions/checkout#2530</a></li> <li>Various dependency updates</li> </ul> <h2>v7.0.0</h2> <ul> <li>Block checking out fork PR for pull_request_target and workflow_run by <a href="https://github.com/aiqiaoy"><code>@aiqiaoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2454">actions/checkout#2454</a></li> <li>Various dependency updates</li> </ul> <h2>v6.0.3</h2> <ul> <li>Fix checkout init for SHA-256 repositories by <a href="https://github.com/yaananth"><code>@yaananth</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2439">actions/checkout#2439</a></li> <li>fix: expand merge commit SHA regex and add SHA-256 test cases by <a href="https://github.com/yaananth"><code>@yaananth</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2414">actions/checkout#2414</a></li> </ul> <h2>v6.0.2</h2> <ul> <li>Fix tag handling: preserve annotations and explicit fetch-tags by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2356">actions/checkout#2356</a></li> </ul> <h2>v6.0.1</h2> <ul> <li>Add worktree support for persist-credentials includeIf by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2327">actions/checkout#2327</a></li> </ul> <h2>v6.0.0</h2> <ul> <li>Persist creds to a separate file by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2286">actions/checkout#2286</a></li> <li>Update README to include Node.js 24 support details and requirements by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2248">actions/checkout#2248</a></li> </ul> <h2>v5.0.1</h2> <ul> <li>Port v6 cleanup to v5 by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2301">actions/checkout#2301</a></li> </ul> <h2>v5.0.0</h2> <ul> <li>Update actions checkout to use node 24 by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2226">actions/checkout#2226</a></li> </ul> <h2>v4.3.1</h2> <ul> <li>Port v6 cleanup to v4 by <a href="https://github.com/ericsciple"><code>@ericsciple</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2305">actions/checkout#2305</a></li> </ul> <h2>v4.3.0</h2> <ul> <li>docs: update README.md by <a href="https://github.com/motss"><code>@motss</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1971">actions/checkout#1971</a></li> <li>Add internal repos for checking out multiple repositories by <a href="https://github.com/mouismail"><code>@mouismail</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1977">actions/checkout#1977</a></li> <li>Documentation update - add recommended permissions to Readme by <a href="https://github.com/benwells"><code>@benwells</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2043">actions/checkout#2043</a></li> <li>Adjust positioning of user email note and permissions heading by <a href="https://github.com/joshmgross"><code>@joshmgross</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2044">actions/checkout#2044</a></li> <li>Update README.md by <a href="https://github.com/nebuk89"><code>@nebuk89</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2194">actions/checkout#2194</a></li> <li>Update CODEOWNERS for actions by <a href="https://github.com/TingluoHuang"><code>@TingluoHuang</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2224">actions/checkout#2224</a></li> <li>Update package dependencies by <a href="https://github.com/salmanmkc"><code>@salmanmkc</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/2236">actions/checkout#2236</a></li> </ul> <h2>v4.2.2</h2> <ul> <li><code>url-helper.ts</code> now leverages well-known environment variables by <a href="https://github.com/jww3"><code>@jww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1941">actions/checkout#1941</a></li> <li>Expand unit test coverage for <code>isGhes</code> by <a href="https://github.com/jww3"><code>@jww3</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1946">actions/checkout#1946</a></li> </ul> <h2>v4.2.1</h2> <ul> <li>Check out other refs/* by commit if provided, fall back to ref by <a href="https://github.com/orhantoy"><code>@orhantoy</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1924">actions/checkout#1924</a></li> </ul> <!-- raw HTML omitted --> </blockquote> <p>... (truncated)</p> </details> <details> <summary>Commits</summary> <ul> <li><a href="https://github.com/actions/checkout/commit/3d3c42e5aac5ba805825da76410c181273ba90b1"><code>3d3c42e</code></a> prep v7.0.1 release (<a href="https://redirect.github.com/actions/checkout/issues/2531">#2531</a>)</li> <li><a href="https://github.com/actions/checkout/commit/28802689a136bfcdb721715abd713740beecbe07"><code>2880268</code></a> escape values passed to --unset (<a href="https://redirect.github.com/actions/checkout/issues/2530">#2530</a>)</li> <li><a href="https://github.com/actions/checkout/commit/12cd2235efa0937479335606d7c3ac9f6c0973b1"><code>12cd223</code></a> trim only ascii whitespace for branch (<a href="https://redirect.github.com/actions/checkout/issues/2521">#2521</a>)</li> <li><a href="https://github.com/actions/checkout/commit/62661c4e71a304b2823ed026347b8d34c3eac541"><code>62661c4</code></a> skip running unsafe pr check if input is default (<a href="https://redirect.github.com/actions/checkout/issues/2518">#2518</a>)</li> <li><a href="https://github.com/actions/checkout/commit/e8d4307400f9427dba7cb98e488d6ab85f1cec5f"><code>e8d4307</code></a> Bump the minor-actions-dependencies group with 2 updates (<a href="https://redirect.github.com/actions/checkout/issues/2499">#2499</a>)</li> <li><a href="https://github.com/actions/checkout/commit/631c942040754b6e095e929c1677c07e10ed4f87"><code>631c942</code></a> eslint 9 (<a href="https://redirect.github.com/actions/checkout/issues/2474">#2474</a>)</li> <li><a href="https://github.com/actions/checkout/commit/4f1f4aec02e41874fa0262ea8ff5172d7978ad1e"><code>4f1f4ae</code></a> Bump actions/upload-artifact from 4 to 7 (<a href="https://redirect.github.com/actions/checkout/issues/2476">#2476</a>)</li> <li><a href="https://github.com/actions/checkout/commit/ba097532fb203f7e88c9c3c0b899b49469908a92"><code>ba09753</code></a> Bump actions/checkout from 6 to 7 (<a href="https://redirect.github.com/actions/checkout/issues/2488">#2488</a>)</li> <li><a href="https://github.com/actions/checkout/commit/b9e0990d219a03df7633c93f6f005a8fecbcab22"><code>b9e0990</code></a> Bump docker/login-action from 3.3.0 to 4.2.0 (<a href="https://redirect.github.com/actions/checkout/issues/2479">#2479</a>)</li> <li><a href="https://github.com/actions/checkout/commit/e8cb398be4a550817e382abf69e4c12c76fce1f2"><code>e8cb398</code></a> Bump docker/build-push-action from 6.5.0 to 7.2.0 (<a href="https://redirect.github.com/actions/checkout/issues/2478">#2478</a>)</li> <li>Additional commits viewable in <a href="https://github.com/actions/checkout/compare/9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0...3d3c42e5aac5ba805825da76410c181273ba90b1">compare view</a></li> </ul> </details> <br /> [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself) </details> Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
## Summary - Add `/sala-empresarial empresa:<slug>`, converting a `/sala`-tracked voice room into a private company room by stamping Discord permission overwrites — `deny @everyone` and `allow` the selected Empresa Parceira's Partner Role for `connect`, `speak`, `use_vad`, `send_messages` and `read_message_history` (voice + the room's built-in text chat). `VIEW_CHANNEL` stays untouched (visible-but-locked) and `MENTION_EVERYONE` keeps category inheritance. - Put the decision (guards + role resolution + overwrite plan) in a pure, unit-tested `ConfigureEmpresarialRoomAction`; the command edge only performs the `setPermissions` I/O. - Gate by Partner Role membership, **not** room ownership — any member of the selected company can privatize the room (rejects `not-in-tracked-room` / `unknown-company` / `missing-partner-role`, all ephemeral). - Add a flat `bot-discord.roles.partners` registry (`slug => role id`), seeded with `brd` and `3p`; onboarding a new partner is one env + one config line. - Ship the module glossary (CONTEXT) entries and ADR-0001 documenting the design. ## Notes - Deviates slightly from the original #448 permission list: `send_messages` was added after a live server test showed outsiders could still post in the room's text chat. - Stateless by design — only `/sala`-tracked rooms are eligible and `DynamicVoiceTask` auto-deletes them when empty, so overwrites die with the channel. No revert command. ## Test plan - [ ] Member holding the partner role, inside their `/sala` room → `/sala-empresarial empresa:brd` privatizes it; outsiders can't connect, speak, or post in chat, but the room stays visible. - [ ] Same room, run by a teammate who didn't create it → still succeeds (role-gated, not owner-gated). - [ ] Pick a company whose role you don't hold → ephemeral rejection, no overwrites stamped. - [ ] Run outside any `/sala` room (permanent channel or no voice) → ephemeral rejection. - [ ] Re-run on an already-configured room → harmless re-stamp, no error. Closes #448 ## Screenshots _Ephemeral replies, captured on a test guild (`/sala-empresarial`)._ **Company picker — choices built from the `partners` registry (`BRD`, `3P`):** <img width="418" height="244" alt="Screenshot from 2026-07-24 09-30-16" src="https://github.com/user-attachments/assets/68ff734a-18f6-45f4-ad93-ef3cfc540371" /> **Success — room privatized for BRD:** <img width="418" height="187" alt="Screenshot from 2026-07-24 09-42-33" src="https://github.com/user-attachments/assets/e98f104c-6a12-4e4a-9d40-b8e02dc5e550" /> **Rejected — caller doesn't hold the selected company's Partner Role (`missing-partner-role`):** <img width="418" height="187" alt="Screenshot from 2026-07-24 09-30-39" src="https://github.com/user-attachments/assets/ecdca69f-b90c-4ef6-8e26-79ff33cb483a" /> **Rejected — caller isn't inside a `/sala`-tracked room (`not-in-tracked-room`):** <img width="444" height="201" alt="Screenshot from 2026-07-24 09-29-14" src="https://github.com/user-attachments/assets/74397524-73f7-4289-bd3f-f1abfe9863ee" />
## Summary
- Resolve GitHub avatars in the portal hero section via the stable
numeric `external_account_id` (`avatars.githubusercontent.com/u/{id}`)
instead of parsing the `metadata` username/name, which could be stale or
renamed.
- Filter to identities with a numeric `external_account_id` present,
dropping the ad-hoc metadata handle extraction.
- Add `HeroAvatarsTest` feature coverage for the active-user +
GitHub-identity avatar resolution.
## Coverage (GitHub identities, non-deleted)
| Strategy | Resolvable avatars |
| --- | --- |
| By user id (`external_account_id`, new) | **2643** |
| By username (`metadata.username\|name`, old) | 2643 |
| Resolvable by user id **only** | 0 |
| Resolvable by username **only** | 0 |
Coverage is identical (all 2643 identities carry both), so no users lose
their avatar. The change is about **stability**: numeric user-id URLs
survive GitHub username renames, whereas the old username-based URLs
break on rename.
## Test plan
- [ ] `php artisan test --compact --filter=HeroAvatars`
- [ ] Visit the portal homepage and confirm hero avatars render for
active users
---------
Co-authored-by: Daniel Reis <danielhe4rt@gmail.com>
Closes #441 ## Contexto O modal de confirmação de unificação era renderizado dentro da coluna lateral sticky. Isso criava um contexto de empilhamento próprio e fazia os campos Apelido e Data de Nascimento aparecerem por cima do modal quando o topo do perfil estava visível. ## O que mudou O modal agora usa o teleport do Livewire para ser renderizado diretamente no body. A lógica de confirmação e cancelamento continua no mesmo componente, mas o modal deixa de ficar preso ao contexto de camadas da coluna lateral. ## Testes 1. `php artisan view:cache` 2. `vendor/bin/pest app-modules/panel-app/tests/Feature/ProfilePageTest.php --compact` com 16 testes e 92 assertions 3. `vendor/bin/pint --test --parallel` 4. `npm run build` ## Validação manual Testei o modal no topo da página e depois de rolar 700 pixels. A camada escura ficou acima de todo o perfil nos dois casos. O botão Cancelar fechou o modal normalmente e o botão Unificar manteve o binding com `confirmMerge`.
## Summary No layout mobile, o menu de navegação está posicionado na parte inferior da tela, o que reduz sua visibilidade e dificulta o acesso aos principais recursos da plataforma. Esta alteração reposiciona o menu para uma área mais visível e reorganiza seus itens para destacar as funcionalidades de maior importância. A nova ordem de exibição passa a ser: 1. Loja 2. Entrar no Discord 3. Área do Usuário 4. Redes Sociais O objetivo é melhorar a usabilidade da navegação em dispositivos móveis, tornando o acesso às principais ações mais intuitivo. ### Alterações - Alterado o arquivo navbar.blade.php removendo margin-auto para os links exibirem no topo. ## Plano de Testes - [x] Acessar a aplicação em um dispositivo mobile ou utilizando o modo responsivo do navegador. - [x] Verificar se o menu de navegação é exibido na parte superior da tela. - [x] Confirmar o menu inicia na superior e não mais na inferior na seguinte ordem: - Loja - Entrar no Discord - Área do Usuário - Redes Sociais - [x] Validar que todos os links permanecem funcionando corretamente após a reorganização. --- ## Evidências (Adicione o comportamento anterior, como prints de tela ou GIFs.) ### Antes <img width="450" height="768" alt="image" src="https://github.com/user-attachments/assets/679e47f0-54d9-4068-a463-cfd820032462" /> ### Depois (Opcional) <img width="451" height="766" alt="image" src="https://github.com/user-attachments/assets/601ffe43-2268-4870-a1c1-ab0f21912588" /> ## Issues Relacionadas Closes [#464](#464)
…ect (#443) ## Contexto Este PR implementa a solução da issue #427 e traz melhorias adicionais relacionadas à experiência de edição do perfil. Atualmente, a seção **Skills** exige múltiplos cliques para adicionar várias habilidades e bloqueia o salvamento do perfil quando existem linhas de skills completamente vazias. Esses comportamentos tornam o preenchimento mais lento e geram fricção desnecessária durante a edição do perfil. Além disso, foram realizadas melhorias complementares na experiência de visualização e gerenciamento de links sociais. ## Alterações ### Skills - Permite salvar o perfil quando existirem linhas de skills completamente vazias. - Ajusta a validação para ignorar registros vazios que não possuem dados preenchidos. - Mantém as validações para skills efetivamente preenchidas. - Implementa comportamento reativo utilizando `live()` e `afterStateUpdated()` do Filament para monitorar alterações nos campos da skill. - Adiciona automaticamente uma nova linha de skill quando o último campo da skill atual é preenchido. - A nova linha é criada automaticamente apenas quando: - A skill possui um valor selecionado. - O nível de proficiência foi informado. - O usuário está preenchendo a última linha disponível do repeater. - Utiliza geração automática de UUIDs para manter a consistência dos itens adicionados dinamicamente ao repeater. ### Social Media - Adiciona suporte ao LinkedIn na seção de links sociais do perfil. - Implementa tratamento para aceitar tanto: - URLs completas (`https://linkedin.com/in/usuario`) - Apenas o username/handler (`usuario`) - Normaliza automaticamente os dados para gerar o link correto. ### Preview do Perfil - Adiciona links de redirecionamento nos ícones e informações exibidas no card de preview do perfil. - Permite acessar diretamente os perfis sociais a partir da visualização do usuário. ## Benefícios - Reduz significativamente a quantidade de cliques necessários para cadastrar múltiplas skills. - Torna o fluxo de edição mais rápido e intuitivo. - Evita bloqueios de salvamento causados por linhas vazias sem dados. - Melhora a experiência de configuração de redes sociais. - Facilita a navegação a partir do preview do perfil. ## Issue relacionada Closes #427 ## Links com redirect no preview profile <img width="441" height="619" alt="adiiconar links com redirect no profile preview" src="https://github.com/user-attachments/assets/a3b91d12-e82f-400d-a83b-a3415b13df31" /> ## Linkedin adicionado no socio media <img width="988" height="493" alt="adicionado linkedin na socials links" src="https://github.com/user-attachments/assets/7727402c-5ca9-406d-ae8b-78d382e43bd5" /> ## Permite salvar com campos em branco ainda não preenchidos <img width="1280" height="579" alt="adicionar skill com dados em branco" src="https://github.com/user-attachments/assets/bc8671d4-1e00-43fc-8cba-3259d11715b7" /> ## Adiciona novo campo automático quando a última coluna é preenchida <img width="1280" height="579" alt="adicionar novo campo automatico" src="https://github.com/user-attachments/assets/6c7de681-8520-4417-b232-564557e63cf1" /> --------- Co-authored-by: Daniel Reis <danielhe4rt@gmail.com>
## Contexto Este PR implementa a solução da issue [461](#461) e remove a opção de fixar post do feed da aplicação ## Descrição do problema Atualmente existe a função de fixar disponível na timeline, porém ela pode ser utilizada por qualquer usuário e não possui um propósito concreto definido. Como não há uma funcionalidade real por trás dessa ação, ela acaba disponível sem trazer valor claro e pode gerar confusão no uso. A sugestão é desativar essa função até que exista uma proposta concreta e bem definida para ela. Alterações - Adicionado feature flag com o valor **false** como default para desabilitar a função de fixar - Desabilita do engament.blade.php a opção de fixar/desfixar ## ANTES <img width="1206" height="191" alt="image" src="https://github.com/user-attachments/assets/b93b0da4-de84-4109-9e44-57ec33606c97" /> ## DEPOIS <img width="1158" height="614" alt="image" src="https://github.com/user-attachments/assets/26515963-b63e-4aeb-91cc-157a27d9eadc" /> ## Issue relacionada Closes #461 Co-authored-by: Daniel Reis <danielhe4rt@gmail.com>
#462) ## Descrição Corrige a exibição do avatar nas threads. Antes, as respostas exibiam apenas as iniciais do usuário, mesmo quando ele possuía uma imagem de perfil cadastrada. Agora, o comportamento é o mesmo do feed, exibindo o avatar quando disponível e utilizando as iniciais apenas como fallback. ## Mudanças * Exibe o avatar do autor nas respostas da thread, seguindo o mesmo padrão do feed. * Exibe o avatar do usuário logado no composer de respostas. * Adiciona eager loading de `user.media` nas replies para evitar consultas N+1. * Adiciona testes para garantir a renderização correta do avatar nas replies. ## Como testar 1. Faça login com um usuário que tenha um avatar cadastrado. 2. Acesse uma thread que contenha respostas desse usuário. 3. Verifique que o avatar é exibido tanto nas respostas quanto ao lado do campo de resposta. > **Nota (teste local):** > O Spatie Media Library gera URLs absolutas a partir do `APP_URL`. Como o `php artisan serve` roda em `localhost:8000`, é necessário configurar: > > ```env > APP_URL=http://localhost:8000 > ``` > > Em seguida, limpe o cache de configuração: > > ```bash > php artisan config:clear > ``` > > Sem isso, as URLs dos avatares apontam para a porta `80`, fazendo com que as imagens apareçam quebradas. Esse comportamento também afeta o feed e não é específico deste PR. Closes #459 Co-authored-by: Daniel Reis <danielhe4rt@gmail.com>
## Descrição Após salvar o perfil, a página agora rola automaticamente para o topo para que o usuário visualize imediatamente a notificação de sucesso. ## Mudanças - `app-modules/panel-app/src/Pages/ProfilePage.php` - Dispara o evento `scroll-to-top` após o salvamento do perfil. - `app-modules/panel-app/resources/views/pages/profile.blade.php` - Adiciona um listener para o evento `scroll-to-top`, realizando o scroll da página até o topo. ## Como testar 1. Acessar `/profile`. 2. Alterar qualquer informação do perfil. 3. Salvar o formulário. 4. Verificar que, após o salvamento, a página rola automaticamente para o topo e a mensagem de sucesso fica visível. ## Testes Testado manualmente seguindo os passos acima. --------- Co-authored-by: Daniel Reis <danielhe4rt@gmail.com>
## O que foi feito
Adiciona um contador de caracteres no campo **Sobre** da página de
perfil, exibindo o progresso no formato `atual/500`.
A mudança mantém o limite já existente de 500 caracteres e apenas
melhora o feedback visual para o usuário durante o preenchimento do
perfil.
## Detalhes técnicos
- Adicionado contador no `Textarea::make('about')`.
- Mantido `maxLength(500)`.
- Trocado `live(onBlur: true)` por `live()` para atualizar o campo
durante a digitação.
- Usado `mb_strlen()` para respeitar caracteres multibyte.
- O contador visual é atualizado no cliente para evitar atraso/perda de
sincronia durante re-renders do Livewire.
## Evidência visual
<img width="992" height="462" alt="image"
src="https://github.com/user-attachments/assets/8ee8bb26-877d-46fe-82f9-01af298a42f5"
/>
## Testes
```bash
vendor\bin\pest app-modules\panel-app\tests\Feature\ProfilePageTest.php --compact
```
## Teste manual
1. Acesse a página de perfil.
2. Digite no campo **Sobre**.
3. Verifique se o contador acompanha a digitação no formato `atual/500`.
Closes #426
---------
Co-authored-by: Daniel Reis <danielhe4rt@gmail.com>
## Problema Atualmente, o usuário não consegue identificar qual provedor OAuth utilizou anteriormente para acessar a plataforma. ## Solução Foi implementado o armazenamento do último provider OAuth utilizado no localStorage, exibindo um badge de “Último acesso” no botão correspondente na tela de login. A preferência é salva somente após um login OAuth concluído com sucesso e não exige alterações no banco de dados (salvei na localStorage que normalmente é o mais usado pra esse tipo de badge). ## Evidências ### Antes <img width="1409" height="647" alt="Screenshot From 2026-07-14 09-16-06" src="https://github.com/user-attachments/assets/42fc6a54-6881-4868-88c2-74551e2e5b60" /> ### Depois <img width="1409" height="647" alt="Screenshot From 2026-07-14 09-20-27" src="https://github.com/user-attachments/assets/0d1bb8bc-aee9-4bf6-b81a-49be7c010963" /> ## Testes - Teste de login OAuth concluído no painel `app`, validando o provider na URL de retorno. - Teste de OAuth negado, garantindo que nenhum provider seja salvo. - `OAuthControllerTest.php`: 2 testes e 5 asserções aprovadas. - Rector e Pint validados dentro do container PHP. - Build do frontend validado. --------- Co-authored-by: Daniel Reis <danielhe4rt@gmail.com>
## O que mudou Este PR melhora o texto do modal de mesclagem de contas para deixar mais claro o que acontece antes do usuário confirmar a ação. Agora o modal informa explicitamente que: - a conta existente será mantida como principal; - a conta atual será absorvida/removida; - o histórico associado à conta mantida será preservado; - após finalizar, o usuário será autenticado novamente na conta mantida. A regra de mesclagem não foi alterada. A mudança é apenas na mensagem exibida para reduzir ambiguidade no fluxo. ## Observação Não configurei o OAuth real localmente para este PR, porque o fluxo depende de credenciais e callbacks cadastrados nos provedores. O teste cobre o estado pós-OAuth simulando o `oauth_merge_pending`, que é exatamente o ponto em que o modal de confirmação é exibido. Também fiz uma validação visual local forçando temporariamente a abertura do modal apenas para gerar o screenshot abaixo. Essa alteração temporária foi desfeita antes do commit. ## Screenshots <img width="1212" height="872" alt="image" src="https://github.com/user-attachments/assets/2208f06a-8562-4e77-9464-421c91a5c28e" /> ## Testes - `php artisan test tests\Feature\ConnectionHubMergeModalTest.php` Closes #429 Signed-off-by: Daniel Reis <danielhe4rt@gmail.com> Co-authored-by: Daniel Reis <danielhe4rt@gmail.com>
This pull request introduces a small user experience improvement to the profile save process. After saving the profile, the page will now automatically scroll to the top, ensuring users see any relevant notifications or messages. * Added a call to dispatch the `scroll-to-top` event after saving profile skills in the `save` method of `ProfilePage.php`.
## Summary Atualmente, o repositório não possui um padrão definido para a descrição de Pull Requests. Como consequência, cada PR é estruturado de forma diferente, dificultando a revisão, a reprodução dos testes e a identificação das alterações realizadas. Este PR adiciona um template de Pull Request para padronizar a abertura de novas contribuições, orientando os colaboradores a documentarem: - O contexto e objetivo da alteração; - As principais modificações realizadas; - O plano de testes utilizado para validação; - Evidências visuais, quando aplicável; - As issues relacionadas. Além de tornar o processo de revisão mais consistente, este template também serve como um guia para novos contribuidores. Como a comunidade incentiva a participação de pessoas com diferentes níveis de experiência, o template fornece uma orientação clara sobre quais informações devem ser incluídas em um Pull Request, tornando o processo de contribuição mais simples, organizado e acolhedor. ### Alterações - Adicionado template padrão para Pull Requests. - Incluída seção **Summary** para contextualização da alteração. - Incluída seção **Alterações** para listar as modificações realizadas. - Incluído **Plano de Testes** em formato de checklist para documentar os passos de validação. - Incluída seção **Evidências** para anexar prints, GIFs ou vídeos quando aplicável. - Incluída seção **Issues Relacionadas** para vincular automaticamente o PR às issues correspondentes.
## Summary - Substitui o `<input type="date">` nativo do perfil por um `DatePicker` do Filament (`native(false)`), na seção Pessoal, junto com o nickname. - Destaca o dia atual no calendário para melhorar o contraste, especialmente no dark mode. - Adiciona ícone de calendário no campo e abre o picker ao clicar nele. Fixes #397 ## Test plan - [ ] Abrir `/app/profile` e confirmar que "Data de Nascimento" usa o DatePicker do Filament (não o nativo do browser) - [ ] Abrir o calendário e confirmar que dias de meses adjacentes não aparecem como números ambíguos - [ ] No dark mode, confirmar que o dia de hoje fica visualmente distinto - [ ] Clicar no ícone de calendário e confirmar que o painel abre - [ ] Salvar uma data de nascimento e confirmar que persiste após reload Mergir o PR #401 antes e resolver conflitos!!!! --------- Co-authored-by: Cursor <cursoragent@cursor.com>
Closes #471 `make check`, `make pint` e `make format` estavam quebrados. O `CLAUDE.md` manda rodar `make check` antes de commitar — quem segue a doc bate num erro que não é dele. ## O bug ```make pint: @export XDEBUG_MODE=off # morre aqui: não alcança a linha de baixo @$(CURDIR)/vendor/bin/pint --parallel @Unset XDEBUG_MODE # ← e esta linha mata o target ``` Cada linha de receita do make roda no **próprio shell**, então o `export` nunca teve efeito nenhum sobre o Pint. E o `unset` derruba o target: o make executa a linha direto, sem shell, e `unset` é builtin, não binário — `make: unset: No such file or directory` → `Error 1`. Reproduzi isolado: acontece **mesmo com a sintaxe correta** (`unset XDEBUG_MODE`). O `=off` sobrando no `test-pint` era só o segundo erro na mesma linha, não a causa. Por isso `make pint` e `make format` caíam junto, não só o `test-pint`. A variável agora vai inline no comando, que é onde ela precisa valer: ```make test-pint: @XDEBUG_MODE=off $(CURDIR)/vendor/bin/pint --test --parallel ``` ## Também neste PR - **`test-phpstan` ganha `--memory-limit=2G`**, que o target `phpstan` já tinha. Sem ele o `make check` estoura os 128M padrão do PHP CLI e morre com fatal de memória — ou seja, `make check` tinha *dois* motivos independentes pra falhar. - **Help do `env-down`** dizia "Start the development environment" (cópia do `env-up`). ## Por que o CI não pegou Os workflows chamam `vendor/bin/pint`, `vendor/bin/phpstan` e `vendor/bin/pest` direto — nenhum passa pelo Makefile. O pipeline seguia verde com o gate local quebrado. Vale considerar se o CI deveria consumir os mesmos targets, pra não haver duas definições de "checar o projeto" que podem divergir de novo. ## Verificação Todos exit 0, e nenhum arquivo tocado além do Makefile: | | | |---|---| | `make check` | ✅ 0 | | `make test-pint` | ✅ 0 | | `make test-phpstan` | ✅ 0 | | `make pint` | ✅ 0 | | `make format` | ✅ 0 | ## Pergunta — `env-down` é destrutivo de propósito? ```make @docker compose --file docker-compose.yml down --rmi all --volumes ``` `--volumes` apaga o banco de desenvolvimento e `--rmi all` remove as imagens. Quem digita `make env-down` esperando só parar os containers perde o Postgres local e paga o download/build de novo no próximo `env-up`. Se for intencional, o help novo já avisa e tá resolvido. Se não for, eu proporia `env-down` = `down` puro e um `env-destroy` separado pro comportamento atual — mas não mudei nada aqui porque é decisão de vocês.
) ## Contexto O Meeting Showcase podia encontrar a mensagem no período informado e ainda assim não identificar corretamente o participante. Isso acontecia porque os dados do Discord chegam em formatos diferentes e a tela só tratava parte deles. Também existia um corte no último minuto escolhido. Ao informar 23:40 como horário final, por exemplo, mensagens enviadas depois de 23:40:00 ficavam fora da consulta. Este PR corrige a leitura e exibição dos participantes que já foram ingeridos. O fluxo de ingestão das mensagens não foi alterado. ### Alterações - Suporte aos formatos de metadata na raiz, em `metadata.user` e em `metadata.author` - Fallback para o usuário vinculado quando a identidade não possui metadata suficiente - Inclusão de identidades desconectadas que ainda possuem mensagens históricas - Inclusão de todo o último minuto selecionado no filtro de período - Testes cobrindo os formatos de metadata, fallback de usuário, identidade desconectada e mensagens dentro e fora do limite final --- ## Plano de Testes - [x] Rector nos arquivos alterados sem sugestões - [x] Pint completo aprovado - [x] PHPStan completo sem erros - [x] Testes de Marketing com 8 testes e 69 assertions - [x] Teste da importação de `metadata.author` com 1 teste e 4 assertions - [x] Testes da persistência de mensagens com 3 testes e 9 assertions - [x] Validação local da tela com participantes nos diferentes formatos de metadata A suíte completa local executou 933 testes. Foram 931 aprovados e 2 falhas preexistentes em `MergeDuplicateDiscordProfilesTest`, causadas pelo tratamento de caminho absoluto do Windows no parâmetro `--pairs-file`. Esses testes ficam fora dos arquivos e do fluxo alterado neste PR. O CI em Linux fará a validação completa novamente. --- ## Issues Relacionadas Closes #477
## Contexto No login do painel (`/app/login`), a logo mobile da He4rt ficava preta sobre fundo escuro no dark mode, com contraste baixo e quase invisível. Isso acontecia porque o SVG é carregado via `<img>` com `fill="currentColor"`, e classes como `text-purple-500` não alteram a cor nesse caso. A alteração aplica filtro CSS apenas no dark mode (`dark:brightness-0 dark:invert`), tornando a logo branca no dark e mantendo a preta no light. O impacto esperado é melhorar a legibilidade da marca na tela de login sem mudar o comportamento no tema claro. ### Alterações - Ajuste da classe CSS da logo mobile em `app-modules/panel-app/resources/views/auth/login.blade.php` - Troca de `text-purple-500` por `dark:brightness-0 dark:invert` - Light mode preservado; dark mode passa a exibir a logo com contraste adequado --- ## Plano de Testes - [ ] Executar `make check` - [ ] Executar `make test` - [ ] Abrir `/app/login` em viewport mobile (< `lg`) no **dark mode** e confirmar que a logo fica visível (branca) - [ ] Abrir `/app/login` em viewport mobile (< `lg`) no **light mode** e confirmar que a logo preta original permanece - [ ] Confirmar no desktop (`lg+`) que o painel esquerdo com a logo branca não foi alterado --- ## Evidências ### Antes <img width="940" height="946" alt="image" src="https://github.com/user-attachments/assets/05fe5557-3eb3-466c-ac98-cde54594aa8e" /> ### Depois <img width="667" height="837" alt="image" src="https://github.com/user-attachments/assets/adc125f3-c971-457a-8885-57c854745d4c" /> --- ## Issues Relacionadas Fixes #468
Closes #467 Neste PR estou adicionando templates para as principais frentes que hoje abordados, que são features, bug's e PRD. --------- Signed-off-by: Sther <72408918+stherzada@users.noreply.github.com> Co-authored-by: Tiago Góes <tiago.goes2009@gmail.com>
…481) ## Contexto Esta alteração visa corrigir a omissão do texto "Entrar" na tela de login para usuários que usam light theme. Foi aplicado suporte para o texto ser visivél tanto em light/dark theme ### Alterações - O login.blade.php foi alterado garantindo suporte para os dois temas. - Ajustado background para o span "ou" que também não estava de acordo para usuários light theme ## Plano de Testes - [x] Executar `make check` - [x] Executar `make test` - [x] Abrir /app/login usando light theme e confirmar que o texto "Entrar" é exibido ## Evidências ### Antes <img width="1919" height="964" alt="image" src="https://github.com/user-attachments/assets/e940a6f0-f24d-4176-a59f-28b4a0742055" /> ### Depois <img width="1919" height="959" alt="image" src="https://github.com/user-attachments/assets/3c94bef6-0825-4808-878f-d4e515549b15" /> ## Issues Relacionadas Fixes #480
Resolve conflitos e adapta o módulo de eventos à remoção do multi-tenancy (#413), que entrou na 4.x depois que esta branch saiu. Conflitos resolvidos: - composer.json / package.json / theme.css / shards.json: versões da 4.x, mantendo `bacon/bacon-qr-code` do módulo de eventos. - Lockfiles regenerados a partir da 4.x. - PanelAdmin/PanelApp ServiceProviders e AppPanelProvider: mantidos os dois lados (registros de eventos + DiscordCluster/FilamentAsset da 4.x). - Factories antigas do módulo events, `GenerateDiscordTenant` e `TenantUser`: mantida a remoção. - Testes de timeline/perfil: versão da 4.x, que já contém as correções de autor removido e avatar. De-tenancy do módulo de eventos: - `events.tenant_id` removido da migration; índices viram `slug` único e `starts_at`. - `Event` perde a relação `tenant()` e o campo do PHPDoc/fillable. - `CheckInRequested` perde `tenantId`; `HandleBotCheckIn` deixa de filtrar por tenant. - Painéis admin/app deixam de escopar eventos por tenant. - Seeder, factory e suíte de testes ajustados; testes de isolamento por tenant que perderam sentido foram removidos ou reescritos (o de 404 agora cobre evento em rascunho). `ProfilePage` volta à implementação da 4.x — o upload de avatar/capa da branch foi substituído pelo fluxo com FileUpload do Filament (#435).
fernanduandrade
approved these changes
Aug 13, 2026
gvieira18
approved these changes
Aug 14, 2026
stherzada
approved these changes
Aug 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Contexto
A PR #309 (
feat/events→4.x) estava com 23 arquivos em conflito. A causaraiz não é textual: a
4.xrecebeu ofeat(tenancy)!: remove multi-tenancy(#413) depois que a branch de eventos saiu, e o módulo
eventsainda era todoescopado por tenant.
Esta PR mergeia a
4.xdentro defeat/events, resolve todos os conflitos eadapta o módulo de eventos ao mundo sem tenancy. O push direto na
feat/eventsé bloqueado por ruleset, por isso vem como sub-PR.
Alterações
Conflitos resolvidos (23 arquivos)
composer.json,package.json,theme.css,shards.json4.x; mantidobacon/bacon-qr-codedo módulo de eventoscomposer.lock,package-lock.json4.xPanelAdminServiceProvider,PanelAppServiceProviderDiscordCluster/FilamentAsset)AppPanelProviderTenantremovidoEventAgendaFactory,EventSubmissionFactory,SponsorFactory,GenerateDiscordTenant,TenantUserTimelineFeedQueryTest,ThreadPageTest,ProfilePageTest,CommunityRetrospectiveTest,Feed,timeline/header4.x(já contém autor removido + avatar)LinkTwitchChannelCommand4.x(#415)EventFactoryDe-tenancy do módulo
eventsevents.tenant_idsai da migration;idx_events_tenant_slug→idx_events_slug(único em
slug),idx_events_tenant_window→idx_events_window.Eventperde a relaçãotenant(), o@propertye o campo em$fillable.CheckInRequestedperdetenantId;HandleBotCheckIndeixa de filtrarevento e
ExternalIdentitypor tenant.EventForm,EventInfolist,EventsTable,EventsList,EventDetail,MyEventsList,EventPage) — inclusive a coluna/labeltenantdas traduções.EventsSeedereEventFactorysem tenant.foram removidos, e o de 404 virou "evento em rascunho não é visível".
Fora do escopo de eventos
ProfilePagevolta à implementação da4.x: o upload de avatar/capa da branchde eventos foi substituído pelo fluxo com
FileUploaddo Filament (feat(profile): adicionar avatar no feed, redimensão do avatar e cover… #435), e omerge tinha deixado só as propriedades órfãs.
MergeAccountsActionTestperde um teste duplicado que o merge introduziu.Plano de Testes
vendor/bin/pest --parallel— 1108 passed / 0 failedvendor/bin/phpstan analyse— 0 errosvendor/bin/pint --test --parallel— passouvendor/bin/rector process --dry-run— 0 mudançasphp artisan migrate:freshem banco limpotests/.pest/shards.jsonregenerado com--update-shardsIssues Relacionadas
Desbloqueia #309.