Skip to content

feat: Public Profile Page (#257)#305

Open
GustavoSimao wants to merge 6 commits into
4.xfrom
feat/public-profile-page
Open

feat: Public Profile Page (#257)#305
GustavoSimao wants to merge 6 commits into
4.xfrom
feat/public-profile-page

Conversation

@GustavoSimao
Copy link
Copy Markdown
Member

@GustavoSimao GustavoSimao commented Jun 3, 2026

Description

Adds a public profile page accessible at /@{username}. Implements ProfileController to load tenant-scoped user/profile data, builds connected accounts, registers a public route, and introduces a responsive Blade view rendering avatar, headline, social links, badges and recruiter summary. Feature tests cover successful displays and error cases.

References

Dependencies & Requirements

  • No new composer/npm dependencies added.
  • No environment variables or configuration changes required.
  • Compatible with existing tenant-scoped profile structures and provider enum mappings.

Contributor Summary

Contributor Lines Added Lines Removed Files Changed
GustavoSimao 529 1 5

Changes Summary

File Path Change Description
app-modules/portal/resources/views/components/layouts/app.blade.php Removed default class="dark" from <html> root.
app-modules/profile/resources/views/public-profile.blade.php New public profile Blade view: avatar, headline, about, social links, badges, connected accounts, responsive layout.
app-modules/profile/src/Http/ProfileController.php New controller: tenant/user/profile lookup, buildConnectedAccounts logic, renders public profile.
app-modules/profile/src/ProfileServiceProvider.php Registered profile views namespace and public route GET /@{username}.
app-modules/profile/tests/Feature/PublicProfileTest.php New feature tests for public profile display, minimal profiles, and 404 scenarios.

@GustavoSimao GustavoSimao requested a review from a team June 3, 2026 18:58
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 3, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Pro

Run ID: 7017ac71-3b62-4f7e-ab2a-bdf46de649a4

📥 Commits

Reviewing files that changed from the base of the PR and between c86da12 and caa81ee.

📒 Files selected for processing (3)
  • app-modules/profile/resources/views/public-profile.blade.php
  • app-modules/profile/src/ProfileServiceProvider.php
  • app-modules/profile/tests/Feature/PublicProfileTest.php
💤 Files with no reviewable changes (1)
  • app-modules/profile/tests/Feature/PublicProfileTest.php
🚧 Files skipped from review as they are similar to previous changes (2)
  • app-modules/profile/src/ProfileServiceProvider.php
  • app-modules/profile/resources/views/public-profile.blade.php

📝 Walkthrough

Walkthrough

Registers GET /@{username}, adds ProfileController::show to resolve tenant by host, load user/profile with relations, and build normalized connected accounts. Introduces a responsive Blade view rendering avatar, headline, availability badge, social links, about/recruiter summaries (mobile/desktop), community level, badges, and connected accounts. Removes the root HTML dark-mode class in the portal layout. Adds feature tests covering successful rendering and 404/error scenarios.

Possibly related PRs

  • he4rt/heartdevs.com#204: Modifies the shared portal layout app-modules/portal/resources/views/components/layouts/app.blade.php (dark-mode/lang handling), which overlaps the layout change in this PR.

Suggested reviewers

  • Clintonrocha98
  • gvieira18
  • Luisnadachi
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: Public Profile Page' directly and clearly describes the main change—adding a public profile page feature. It aligns with the changeset which includes the ProfileController, view template, service provider configuration, and tests for this feature.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (2)
app-modules/profile/tests/Feature/PublicProfileTest.php (1)

26-42: ⚡ Quick win

"Minimal profile" test doesn't actually create a minimal profile.

Profile::factory()->create([...]) populates all factory defaults (headline, about, etc.), so this doesn't exercise the null-field rendering path the test name implies. To truly validate the "render only filled fields" requirement, null out the optional attributes explicitly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app-modules/profile/tests/Feature/PublicProfileTest.php` around lines 26 -
42, The test PublicProfileTest::it('renders minimal profile without crashing')
currently uses Profile::factory()->create(...) which fills optional fields with
defaults; update the Profile creation in this test to explicitly set optional
attributes to null (e.g., headline, about, location, social links) when calling
Profile::factory()->create so the profile truly has minimal/null fields and the
assertions for not rendering 'null'/'undefined' exercise the null-field
rendering path.
app-modules/profile/resources/views/public-profile.blade.php (1)

18-52: 💤 Low value

Header markup is largely duplicated across the availability branches.

The @if ($profile->available_for_proposals) ... @else ... blocks repeat the name/headline/seniority markup with only the availability badge and a few margin classes differing. Extracting the shared name/headline portion and conditionally rendering just the badge would reduce drift risk.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app-modules/profile/resources/views/public-profile.blade.php` around lines 18
- 52, The header markup is duplicated between the `@if`
($profile->available_for_proposals) branches; refactor by rendering the shared
elements (the <h1> that uses $profile->nickname ?? $user->name and the <p> that
outputs $profile->headline / seniority_level / $profile->years_experience) once,
apply the differing margin classes conditionally (e.g., compute class string
based on $profile->available_for_proposals) and move the availability badge
(<span> with the "Disponível para propostas" text and <x-filament::icon
icon="heroicon-s-check-circle" />) into a small conditional that only renders
when $profile->available_for_proposals is true; remove the duplicated blocks so
only one header block exists and the badge + margin adjustments are
conditionally applied.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app-modules/profile/resources/views/public-profile.blade.php`:
- Line 29: The experience label lacks spacing; update the Blade template
occurrences that render the years (referenced by the $profile->years_experience
usage) to include spaces around the separator and between the number and the
unit so it reads "· {{ $profile->years_experience }} anos de experiência" (fix
both occurrences, including the one around line 49).

In `@app-modules/profile/src/Http/ProfileController.php`:
- Around line 60-84: The current logic can emit duplicate accounts and produce
malformed URLs when external_account_id is null; update the handling in
ProfileController so that when building $accounts you (1) track seen provider
keys (e.g. use a local array or collection) and skip adding a provider if its
key already came from $profile->social_links or a previous provider entry, and
(2) when iterating $user->providers skip any provider whose
$provider->external_account_id is null before calling sprintf with
self::PROVIDER_URLS[$name]; ensure you still normalize the provider name
resolution (the $name computed from IdentityProvider) and mark that provider as
seen after successfully adding it.
- Around line 27-29: In ProfileController, the Tenant lookup uses
Tenant::query()->where('domain', $request->getHost())->firstOrFail() but does
not check the Tenant.active flag; update the query to also filter for active
tenants (e.g. add ->where('active', true)) before calling firstOrFail() so
deactivated tenants are not resolved; make this change where the
Tenant::query(...) call appears to ensure inactive tenants return a 404.
- Around line 44-46: ProfileController@show is eager-loading $user->providers
which can return ExternalIdentity rows from other tenants; update the
$user->load call (or the relationship query) so that 'providers' is constrained
to the resolved $tenant->id (e.g., eager-load via a closure or use a scoped
relationship) so buildConnectedAccounts only iterates provider records for that
tenant; additionally, change the Tenant::query()->where('domain',
...)->firstOrFail() lookup to include an active filter (e.g., where('active',
true)) to avoid resolving inactive tenants; finally, replace the PHP 8.3-only
private const array PROVIDER_URLS with an alternative compatible declaration
(e.g., a private static array or a protected constant compatible with your PHP
target) so the code remains portable.

In `@app-modules/profile/src/ProfileServiceProvider.php`:
- Around line 25-26: The public profile route declaration
Route::get('/@{username}', [ProfileController::class,
'show'])->name('profile.public') lacks the web middleware; update that route to
attach ->middleware('web') (e.g., chain ->middleware('web') on the Route::get
call for that same route) so it receives the session/cookie/locale stack like
other routes.

In `@app-modules/profile/tests/Feature/PublicProfileTest.php`:
- Around line 63-79: The test in PublicProfileTest contains a tautological
assertion assertDontSee('Indisponível') which should be removed; update the test
(the it(...) block in PublicProfileTest) to delete the redundant
assertDontSee('Indisponível') so only assertDontSee('Disponível') remains, then
add two new focused tests: one that verifies tenant isolation by creating the
same username on a different Tenant and asserting a 404 when requesting the
profile on the original domain (use the same get(...) pattern and
assertStatus(404)), and another that seeds a Profile with connected accounts and
asserts the public profile page contains the expected connected-account markers
(using get(...) and assertSee for the connected-account labels).

---

Nitpick comments:
In `@app-modules/profile/resources/views/public-profile.blade.php`:
- Around line 18-52: The header markup is duplicated between the `@if`
($profile->available_for_proposals) branches; refactor by rendering the shared
elements (the <h1> that uses $profile->nickname ?? $user->name and the <p> that
outputs $profile->headline / seniority_level / $profile->years_experience) once,
apply the differing margin classes conditionally (e.g., compute class string
based on $profile->available_for_proposals) and move the availability badge
(<span> with the "Disponível para propostas" text and <x-filament::icon
icon="heroicon-s-check-circle" />) into a small conditional that only renders
when $profile->available_for_proposals is true; remove the duplicated blocks so
only one header block exists and the badge + margin adjustments are
conditionally applied.

In `@app-modules/profile/tests/Feature/PublicProfileTest.php`:
- Around line 26-42: The test PublicProfileTest::it('renders minimal profile
without crashing') currently uses Profile::factory()->create(...) which fills
optional fields with defaults; update the Profile creation in this test to
explicitly set optional attributes to null (e.g., headline, about, location,
social links) when calling Profile::factory()->create so the profile truly has
minimal/null fields and the assertions for not rendering 'null'/'undefined'
exercise the null-field rendering path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Pro

Run ID: e3fb3dd0-58e4-416e-b50a-5d6ee8f6c9fa

📥 Commits

Reviewing files that changed from the base of the PR and between 01f60a2 and 00fae1b.

📒 Files selected for processing (5)
  • app-modules/portal/resources/views/components/layouts/app.blade.php
  • app-modules/profile/resources/views/public-profile.blade.php
  • app-modules/profile/src/Http/ProfileController.php
  • app-modules/profile/src/ProfileServiceProvider.php
  • app-modules/profile/tests/Feature/PublicProfileTest.php

'profile::enums.seniority_level.' . $profile->seniority_level->value,
)
}}
@if ($profile->years_experience) ·{{ $profile->years_experience }}anos de experiência @endif
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Spacing in experience label.

·{{ $profile->years_experience }}anos de experiência renders without a space after the separator and before "anos" (e.g. "·5anos de experiência"). Same issue at Line 49.

✏️ Suggested fix
-                                `@if` ($profile->years_experience) ·{{ $profile->years_experience }}anos de experiência `@endif`
+                                `@if` ($profile->years_experience) · {{ $profile->years_experience }} anos de experiência `@endif`
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@if ($profile->years_experience) ·{{ $profile->years_experience }}anos de experiência @endif
`@if` ($profile->years_experience) · {{ $profile->years_experience }} anos de experiência `@endif`
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app-modules/profile/resources/views/public-profile.blade.php` at line 29, The
experience label lacks spacing; update the Blade template occurrences that
render the years (referenced by the $profile->years_experience usage) to include
spaces around the separator and between the number and the unit so it reads "·
{{ $profile->years_experience }} anos de experiência" (fix both occurrences,
including the one around line 49).

Comment thread app-modules/profile/src/Http/ProfileController.php
Comment thread app-modules/profile/src/Http/ProfileController.php Outdated
Comment thread app-modules/profile/src/Http/ProfileController.php Outdated
Comment thread app-modules/profile/src/ProfileServiceProvider.php Outdated
Comment thread app-modules/profile/tests/Feature/PublicProfileTest.php
fix: filtra tenant inativo, restringe providers ao tenant e remove duplicatas
Erro de separacao. codigo ia sair sem o espaco e foi corrigido
GustavoSimao and others added 3 commits June 3, 2026 16:32
middleware nao estava sendo usado
remocao de 1 linha para que o teste seja realizado corretamente
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants