feat: Public Profile Page (#257)#305
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository YAML (base), Central YAML (inherited) Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
💤 Files with no reviewable changes (1)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughRegisters 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
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ 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. Comment |
There was a problem hiding this comment.
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 valueHeader 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
📒 Files selected for processing (5)
app-modules/portal/resources/views/components/layouts/app.blade.phpapp-modules/profile/resources/views/public-profile.blade.phpapp-modules/profile/src/Http/ProfileController.phpapp-modules/profile/src/ProfileServiceProvider.phpapp-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 |
There was a problem hiding this comment.
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.
| @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).
fix: filtra tenant inativo, restringe providers ao tenant e remove duplicatas
Erro de separacao. codigo ia sair sem o espaco e foi corrigido
middleware nao estava sendo usado
remocao de 1 linha para que o teste seja realizado corretamente
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
Contributor Summary
Changes Summary
class="dark"from<html>root.GET /@{username}.