chore(squads): sync 4.x into feat/squads#404
Merged
Merged
Conversation
## Contexto A página inicial da comunidade reúne diversos acessos importantes, porém o link para a documentação (`/docs`) não estava em destaque, o que pode dificultar que novos membros descobrissem um dos principais recursos da plataforma. Este PR adiciona um **Call to Action (CTA)** para a documentação na landing page da comunidade, tornando o acesso mais visível e incentivando os usuários a explorarem os guias, tutoriais e demais conteúdos disponíveis. ### Arquivos afetados - `app-modules/portal/resources/views/sections/hero.blade.php` — adiciona um CTA para `/docs` na landing page da comunidade. ### Alterações - Novo botão/CTA direcionando para `/docs`. - Ajuste da interface para destacar a documentação como um dos principais pontos de entrada da comunidade. ### ANTES <img width="1919" height="866" alt="image" src="https://github.com/user-attachments/assets/0a40dd21-f36e-4721-93cc-38fba48b6281" /> ### DEPOIS <img width="1919" height="868" alt="image" src="https://github.com/user-attachments/assets/4f68c4ac-ab6d-4771-8dc0-7ac157fbabf2" />
## Summary - Add a caller workflow that reuses `he4rt/.github/actions/dependabot-automerge` to approve and squash-merge Dependabot **github-actions** patch/minor PRs. Majors and other ecosystems stay manual. - Mirrors how this repo already consumes `he4rt/.github/actions/auto-assign`. ## Requires - Org/repo setting **Allow GitHub Actions to create and approve pull requests** must be enabled (used by the approve step).
Members can attach skills to their profile, each with a proficiency level and years of experience, chosen from a curated catalog. - skills catalog (global, seeded inline in the create migration) + profile_skills pivot (tenant-scoped via profile) with UNIQUE(profile_id, skill_id) and INDEX(skill_id, proficiency) - SkillCategory and SkillProficiency enums with pt_BR/en labels - Skill/ProfileSkill models, Profile relations, per-request memoized label lookup - SyncProfileSkills action + ProfileSkillDTO (validates catalog, dedup, range) - skills repeater on the profile page with a server-side searched select (getSearchResultsUsing over name/slug, capped) + preview card rendering - ADR-0001 documenting the data-modeling decision --------- Signed-off-by: Daniel Reis <danielhe4rt@gmail.com> Co-authored-by: Daniel Reis <danielhe4rt@gmail.com> Co-authored-by: Gabriel Vieira <48625433+gvieira18@users.noreply.github.com>
Signed-off-by: Daniel Reis <danielhe4rt@gmail.com> Co-authored-by: danielhe4rt <danielhe4rt@gmail.com>
## Contexto O componente de terminal iniciava com uma altura reduzida e aumentava gradualmente conforme a animação dos comandos era executada. Esse comportamento causava mudanças de layout (layout shift), prejudicando a experiência visual da página. Este PR corrige a altura inicial do componente para que ela permaneça estável durante toda a animação. ### Arquivos afetados - `app-modules/portal/resource/view/components/terminal.blade.php` — ajuste da altura inicial do componente de terminal. ### Alterações - Definida uma altura inicial consistente para o terminal. - Evitado o aumento progressivo da altura durante a animação dos comandos. - Reduzido o layout shift, proporcionando uma experiência visual mais estável. ### ANTES <img width="1280" height="579" alt="agora" src="https://github.com/user-attachments/assets/88b02cf5-c854-4073-bc85-31f2e91559aa" /> ### DEPOIS <img width="1280" height="579" alt="fix_terminal" src="https://github.com/user-attachments/assets/942aa745-4780-49cb-bd37-fb61219333a5" />
Signed-off-by: Clinton Rocha <50300876+Clintonrocha98@users.noreply.github.com> Co-authored-by: danielhe4rt <danielhe4rt@gmail.com>
…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
Merge origin/4.x to pick up #402 (conventions, tooling, tests). Kept onboarding ServiceProvider boot() with loadMigrationsFrom (squads adds real onboarding migrations); took 4.x composer.lock (no new external deps on this branch).
Named-argument attributes (ExplicitAttributeNamedArgsRector) on the squads and onboarding models, per the stricter tooling merged from 4.x.
Source stub referenced by config/app-modules.php for the placeholder files internachi/modular writes into every new module's tests and database dirs. #402 removed it on 4.x, which breaks make:module.
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.
Regenerate the shard timing manifest so every discovered test class has an entry, clearing the '[tests/.pest/shards.json] is out of date' warning introduced when the branch's new tests merged with 4.x's shard plugin.
Clintonrocha98
approved these changes
Jul 9, 2026
Contributor
|
lgtm |
stherzada
approved these changes
Jul 9, 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.
Why
#402landed on4.x(conventions, tooling, tests). This brings thefeat/squadslong-lived integration branch up to date so sub-work PR'dinto it inherits the current standards.
What
origin/4.xintofeat/squads.4.x(correct^1.0.0moduleconstraints + package bumps); squads adds no new root dependency.
4.x's lock — no new external deps on thisbranch (squads/onboarding module
composer.jsonunchanged in deps).4.xrelocated it tosrc/root perthe module convention. Kept squads'
boot()withloadMigrationsFrombecause this branch adds real onboarding migrations
(
create_onboardings_table,create_onboarding_steps_table), which4.x's emptyboot()would fail to load.Notes
composer validatepasses; no conflict markers remain. CI runs on this PR.