Skip to content

Implement custom links editing in ProfileView#137#182

Open
mspandey wants to merge 3 commits into
PRODHOSH:mainfrom
mspandey:patch-3
Open

Implement custom links editing in ProfileView#137#182
mspandey wants to merge 3 commits into
PRODHOSH:mainfrom
mspandey:patch-3

Conversation

@mspandey

@mspandey mspandey commented Jun 23, 2026

Copy link
Copy Markdown

Added functionality to edit and save custom links for the profile view, including input validation and database update.Added the Supabase client import and implemented the handleSaveLinks function to update the profiles table. Wired the function to the Save Changes button within the Edit Links modal to successfully mutate the database and reload the view.

Summary by CodeRabbit

  • New Features
    • Profile owners can now add and edit “Custom Links” shown in the profile header.
    • Custom links render as clickable outbound links.
    • When viewing your own profile, an edit control opens a modal to update link label and URL.
    • Profile pages now load the saved custom links and automatically determine whether the viewer is the profile owner to enable owner-only editing.

Added functionality to edit and save custom links for the profile view, including input validation and database update.Added the Supabase client import and implemented the handleSaveLinks function to update the profiles table. Wired the function to the Save Changes button within the Edit Links modal to successfully mutate the database and reload the view.
@github-actions github-actions Bot added frontend Related to UI / Next.js UI Visual / design changes labels Jun 23, 2026
@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

ProfilePage now loads profile custom links and owner state from Supabase, and ProfileView renders those links with an owner-only edit modal that saves updates back to profiles.custom_links.

Changes

Custom Links on Profile

Layer / File(s) Summary
Profile data loading and prop wiring
src/app/[username]/page.tsx
Fetches custom_links and the current user during profile rendering, computes isOwner, and passes both values into ProfileView.
Prop contract and save handler
src/components/profile/ProfileView.tsx
Extends ProfileExtras, defaults isOwner and customLinks, adds modal state, and validates then persists edited links to Supabase.
Custom link rendering and edit modal
src/components/profile/ProfileView.tsx
Renders the links in the profile header and adds an owner-only pencil action that opens the edit modal with Cancel and Save controls.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Poem

🐇 A profile grew links, neat and bright,
Owners can edit them just right.
A pencil, a modal, a save, and a hop,
Supabase smiles as the changes don’t stop.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.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 clearly summarizes the main change: adding custom link editing in ProfileView.
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.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 9

🤖 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 `@src/components/profile/ProfileView.tsx`:
- Around line 139-146: The newLinks variable in the ProfileView component is
hard-coded as a single-element array containing only the newly entered link,
which replaces all existing custom_links in the database causing data loss.
Instead of creating a one-element array for newLinks, first fetch the existing
custom_links array from the profiles table for the current user, then merge or
upsert the new link entry into that existing array before updating the database.
This way, previously saved links are preserved and the new/edited link is added
or modified within the complete list.
- Around line 295-337: The modal dialog in the isEditLinksOpen block lacks
essential accessibility attributes and keyboard handling. Add role="dialog" and
aria-modal="true" to the outer fixed-position div, provide an accessible name
using aria-labelledby pointing to the h3 "Edit Custom Links" heading, and add an
id to that heading. Implement focus management by using useEffect to move focus
into the modal (preferably to the first input with id "edit-link-label") when
isEditLinksOpen becomes true, and restore focus to the previously focused
element when the modal closes. Add an onKeyDown handler to the modal to detect
the Escape key and call setIsEditLinksOpen(false) to close the modal, enabling
keyboard users to escape without using the Cancel button.
- Around line 64-67: The ProfileExtras interface is closed on line 66 with a
closing brace, but line 67 contains a stray `updatedAt: string | null;`
declaration at module scope that is syntactically invalid and causes a compile
error. Remove the stray `updatedAt` declaration on line 67 since the field
already exists as part of the ProfileExtras interface definition above.
- Around line 245-268: The custom links anchor element in the customLinks.map
block is missing an aria-label attribute to indicate the link opens in a new
tab, which impacts screen reader accessibility, and uses an index-based key
(key={idx}) which is fragile if links are reordered or removed. Add an
aria-label attribute to the anchor tag that describes the action (e.g.,
mentioning it opens externally), and change the key from idx to a stable
identifier like link.url.
- Around line 130-156: The handleSaveLinks function references
setIsEditLinksOpen which lacks a corresponding useState declaration, causing a
compilation error. Add a useState hook near the existing state declarations
around line 118 to initialize the isEditLinksOpen state variable with a boolean
initial value, providing both the isEditLinksOpen variable and
setIsEditLinksOpen setter function used throughout the ProfileView component.
- Around line 130-156: In the handleSaveLinks function, replace the
document.getElementById calls for "edit-link-label" and "edit-link-url" with
state variables managed via useState hooks for controlled inputs. Replace the
window.location.reload() call with useRouter().refresh() imported from
next/navigation to refresh the page without a hard reload. Add URL scheme
validation before saving to ensure the URL only contains safe protocols like
http or https and reject unsafe schemes like javascript or data. Additionally,
ensure the parent component fetches customLinks from Supabase and passes them as
props to ProfileView, and consider wrapping the mutation in a Server Action that
uses revalidatePath() after a successful update to re-fetch the server data.
- Line 7: Replace the broken import statement from the non-existent module
`@/utils/supabase/client` with the correct import from the project's singleton:
change `import { createClient } from "`@/utils/supabase/client`";` to `import {
supabase } from "`@/lib/supabase`";`. Then locate the line in ProfileView.tsx
where `const supabase = createClient();` is called (around line 140) and remove
it, using the imported supabase singleton directly instead of trying to create a
new instance.
- Around line 94-96: In the ProfileView component invocation within the page
component, add logic to detect ownership by comparing the authenticated user's
identity with the profile username being viewed, and fetch the custom_links from
the Supabase profiles table. Then pass the derived isOwner boolean and fetched
customLinks array to the ProfileView component so that the ownership check on
line 271 (the `isOwner &&` condition for the edit pencil) and the custom links
rendering on line 245 become functional instead of remaining at their default
inert values.
- Around line 143-146: The update operation in ProfileView.tsx attempting to
update the custom_links column in the profiles table will fail because this
column does not exist in the database schema. Create a new database migration
that adds the custom_links column to the profiles table as a jsonb data type
using an ALTER TABLE statement with the if not exists clause to handle any edge
cases. Once the migration is applied to your database, the update operation
referencing custom_links will function correctly.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 294e96ae-93e7-4772-b9c8-d44e9f57dc4d

📥 Commits

Reviewing files that changed from the base of the PR and between c802435 and 6c5e0ff.

📒 Files selected for processing (1)
  • src/components/profile/ProfileView.tsx

Comment thread src/components/profile/ProfileView.tsx Outdated
import Image from "next/image";
import Link from "next/link";
import type { ContributorStats, Org, TechEntry, HeatmapWeek } from "@/types";
import { createClient } from "@/utils/supabase/client";

@coderabbitai coderabbitai Bot Jun 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the imported module and its createClient export
fd -t f . src/utils/supabase 2>/dev/null
rg -nP 'export\s+(async\s+)?function\s+createClient|export\s+const\s+createClient' -g '**/utils/supabase/**'

Repository: PRODHOSH/ossfolio

Length of output: 155


🏁 Script executed:

# Check what actually exists in src/
ls -la src/ 2>/dev/null | head -20

Repository: PRODHOSH/ossfolio

Length of output: 481


🏁 Script executed:

# Search for supabase-related files anywhere in src/
find src -name "*supabase*" -type f 2>/dev/null

Repository: PRODHOSH/ossfolio

Length of output: 175


🏁 Script executed:

# Check the actual ProfileView.tsx file to see the import and line 140
cat -n src/components/profile/ProfileView.tsx | head -150

Repository: PRODHOSH/ossfolio

Length of output: 5340


🏁 Script executed:

# Check for path alias configuration (tsconfig.json or similar)
cat tsconfig.json 2>/dev/null || cat tsconfig.base.json 2>/dev/null || cat vite.config.ts 2>/dev/null

Repository: PRODHOSH/ossfolio

Length of output: 857


🏁 Script executed:

# Read the existing supabase.ts to see what's exported
cat -n src/lib/supabase.ts

Repository: PRODHOSH/ossfolio

Length of output: 811


🏁 Script executed:

# Double-check that src/utils doesn't exist at all
ls -la src/utils 2>/dev/null || echo "src/utils does not exist"

Repository: PRODHOSH/ossfolio

Length of output: 180


🏁 Script executed:

# Verify the entire src/ directory structure
find src -type d | sort

Repository: PRODHOSH/ossfolio

Length of output: 487


Remove broken import and use the established singleton from src/lib/supabase.ts.

The module @/utils/supabase/client does not exist. The import at line 7 will resolve to a non-existent path, and the createClient() call at line 140 will fail at runtime. Use the project's singleton instead: import { supabase } from "@/lib/supabase"; and replace const supabase = createClient(); with direct use of the imported singleton.

🤖 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 `@src/components/profile/ProfileView.tsx` at line 7, Replace the broken import
statement from the non-existent module `@/utils/supabase/client` with the correct
import from the project's singleton: change `import { createClient } from
"`@/utils/supabase/client`";` to `import { supabase } from "`@/lib/supabase`";`.
Then locate the line in ProfileView.tsx where `const supabase = createClient();`
is called (around line 140) and remove it, using the imported supabase singleton
directly instead of trying to create a new instance.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

fix this issue @mspandey

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment thread src/components/profile/ProfileView.tsx Outdated
Comment thread src/components/profile/ProfileView.tsx
Comment thread src/components/profile/ProfileView.tsx
Comment on lines +139 to +146
const newLinks = [{ label: labelInput.value, url: urlInput.value }];
const supabase = createClient();

// Update the Supabase database exactly as the issue requested
const { error } = await supabase
.from('profiles')
.update({ custom_links: newLinks })
.eq('username', user.login);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Save overwrites all custom links with a single entry — data loss.

newLinks is hard-coded to a one-element array and replaces the entire custom_links column. A user with multiple existing links loses all but the one edited in the modal. The feature spec calls for add/change of multiple links. Persist a merged/edited array rather than a single-element replacement.

🤖 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 `@src/components/profile/ProfileView.tsx` around lines 139 - 146, The newLinks
variable in the ProfileView component is hard-coded as a single-element array
containing only the newly entered link, which replaces all existing custom_links
in the database causing data loss. Instead of creating a one-element array for
newLinks, first fetch the existing custom_links array from the profiles table
for the current user, then merge or upsert the new link entry into that existing
array before updating the database. This way, previously saved links are
preserved and the new/edited link is added or modified within the complete list.

Comment on lines +143 to +146
const { error } = await supabase
.from('profiles')
.update({ custom_links: newLinks })
.eq('username', user.login);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Look for RLS policy / migration governing UPDATE on profiles
rg -nP -C2 'profiles' -g '**/*.sql'
rg -nP -i 'policy|row level security|rls' -g '**/*.sql'

Repository: PRODHOSH/ossfolio

Length of output: 155


🏁 Script executed:

#!/bin/bash
# Explore repo structure to understand database setup
git ls-files | head -30
echo "---"
# Look for common database/supabase config files
find . -maxdepth 3 -type f \( -name "*supabase*" -o -name "*migrations*" -o -name "*schema*" \) 2>/dev/null | head -20
echo "---"
# Check for TypeScript/JS migration or database definition files
fd -e ts -e js -e json | grep -i -E "(migration|schema|supabase|database|rls|policy)" | head -20

Repository: PRODHOSH/ossfolio

Length of output: 1001


🏁 Script executed:

#!/bin/bash
# Read the schema and migration files to check for RLS policies
echo "=== supabase/schema.sql ==="
cat ./supabase/schema.sql
echo ""
echo "=== supabase/migrations/20260520000001_initial_schema.sql ==="
cat ./supabase/migrations/20260520000001_initial_schema.sql

Repository: PRODHOSH/ossfolio

Length of output: 3811


🏁 Script executed:

#!/bin/bash
# Read the ProfileView.tsx file to see the full context around lines 143-146
echo "=== ProfileView.tsx context ==="
sed -n '130,160p' ./src/components/profile/ProfileView.tsx
echo ""
# Check if custom_links column exists in schema
rg -i "custom_links" ./supabase/

Repository: PRODHOSH/ossfolio

Length of output: 1318


🏁 Script executed:

#!/bin/bash
# Check for isOwner check and understand the user object/session context
echo "=== Looking for isOwner check in ProfileView.tsx ==="
rg -n "isOwner" ./src/components/profile/ProfileView.tsx -B2 -A2
echo ""
echo "=== Looking for user object definition and session context ==="
rg -n "user\." ./src/components/profile/ProfileView.tsx | head -20
echo ""
echo "=== Check how createClient is implemented ==="
cat ./src/lib/supabase.ts

Repository: PRODHOSH/ossfolio

Length of output: 2593


🏁 Script executed:

#!/bin/bash
# Check if custom_links column is defined anywhere in the schema files
echo "=== Searching for custom_links in schema files ==="
rg -i "custom_links" ./supabase/
echo ""
# Check the full schema.sql for all columns
echo "=== Full profiles table definition from schema.sql ==="
sed -n '/^create table public.profiles/,/^);/p' ./supabase/schema.sql
echo ""
# Check if there are any other migration files
echo "=== All migration files ==="
find ./supabase/migrations -type f -name "*.sql" | sort

Repository: PRODHOSH/ossfolio

Length of output: 911


🏁 Script executed:

#!/bin/bash
# Check the second migration file
echo "=== Migration 20260605000001_add_profile_score.sql ==="
cat ./supabase/migrations/20260605000001_add_profile_score.sql
echo ""
# Check if there are any more migration files
ls -la ./supabase/migrations/
echo ""
# Search the entire codebase for custom_links references
echo "=== All references to custom_links in the codebase ==="
rg "custom_links" . --type ts --type tsx --type sql

Repository: PRODHOSH/ossfolio

Length of output: 1240


🏁 Script executed:

#!/bin/bash
# Simple rg search for custom_links without type restrictions
echo "=== All references to custom_links ==="
rg "custom_links" .
echo ""
# Check the RLS policy more carefully
echo "=== RLS policies in schema ==="
rg -A2 "create policy.*profiles.*update" ./supabase/

Repository: PRODHOSH/ossfolio

Length of output: 617


Add custom_links column to the profiles schema before this update will work.

The custom_links column referenced in the update at line 146 does not exist in the profiles table. The update will fail with a column-not-found error. Additionally, while the isOwner check is purely cosmetic (only gates the button), the existing RLS policy properly enforces authorization:

create policy "profiles_update" on public.profiles for update using (auth.uid() = id);

This policy correctly restricts UPDATE to authenticated users whose Supabase ID matches the profile being updated, so unauthorized clients cannot bypass it. Add the custom_links column to your schema migration, e.g.:

alter table public.profiles
  add column if not exists custom_links jsonb;
🤖 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 `@src/components/profile/ProfileView.tsx` around lines 143 - 146, The update
operation in ProfileView.tsx attempting to update the custom_links column in the
profiles table will fail because this column does not exist in the database
schema. Create a new database migration that adds the custom_links column to the
profiles table as a jsonb data type using an ALTER TABLE statement with the if
not exists clause to handle any edge cases. Once the migration is applied to
your database, the update operation referencing custom_links will function
correctly.

Comment thread src/components/profile/ProfileView.tsx Outdated
Comment on lines +295 to +337
{isEditLinksOpen && (
<div style={{
position: "fixed", top: 0, left: 0, width: "100vw", height: "100vh",
backgroundColor: "rgba(0,0,0,0.5)", zIndex: 9999,
display: "flex", justifyContent: "center", alignItems: "center"
}}>
<div style={{
background: "var(--color-canvas-default, #fff)",
padding: "24px", borderRadius: "12px", width: "400px", maxWidth: "90%",
border: "1px solid var(--color-hairline)",
boxShadow: "0 8px 24px rgba(0,0,0,0.12)"
}}>
<h3 style={{ margin: "0 0 8px 0", color: "var(--color-ink)" }}>Edit Custom Links</h3>
<p style={{ margin: "0 0 20px 0", fontSize: "13px", color: "var(--color-ink-mute)" }}>Add a custom link to display on your profile.</p>

<div style={{ display: "flex", flexDirection: "column", gap: "12px", marginBottom: "24px" }}>
<div>
<label style={{ display: "block", fontSize: "12px", marginBottom: "6px", color: "var(--color-ink-mute)" }}>Link Label</label>
<input id="edit-link-label" type="text" placeholder="e.g. My Portfolio" defaultValue={customLinks?.[0]?.label || ""} style={{ width: "95%", padding: "8px", borderRadius: "6px", border: "1px solid var(--color-hairline)", background: "var(--color-canvas-soft)", color: "var(--color-ink)" }} />
</div>
<div>
<label style={{ display: "block", fontSize: "12px", marginBottom: "6px", color: "var(--color-ink-mute)" }}>Link URL</label>
<input id="edit-link-url" type="url" placeholder="https://..." defaultValue={customLinks?.[0]?.url || ""} style={{ width: "95%", padding: "8px", borderRadius: "6px", border: "1px solid var(--color-hairline)", background: "var(--color-canvas-soft)", color: "var(--color-ink)" }} />
</div>
</div>

<div style={{ display: "flex", justifyContent: "flex-end", gap: "10px" }}>
<button
onClick={() => setIsEditLinksOpen(false)}
style={{ padding: "8px 16px", borderRadius: "6px", border: "1px solid var(--color-hairline-strong)", background: "transparent", color: "var(--color-ink)", cursor: "pointer", fontSize: "13px" }}
>
Cancel
</button>
<button
onClick={handleSaveLinks}
style={{ padding: "8px 16px", borderRadius: "6px", border: "none", background: "#3ecf8e", color: "#fff", cursor: "pointer", fontSize: "13px", fontWeight: 500 }}
>
Save Changes
</button>
</div>
</div>
</div>
)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Edit modal lacks accessibility semantics.

The overlay/dialog has no role="dialog", aria-modal="true", accessible name, focus trap/initial focus, or ESC-to-close handling. Keyboard and screen-reader users can’t reliably operate or escape it, and focus leaks to the background content. Add the dialog roles, move focus into the modal on open, restore it on close, and handle the Escape key.

🤖 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 `@src/components/profile/ProfileView.tsx` around lines 295 - 337, The modal
dialog in the isEditLinksOpen block lacks essential accessibility attributes and
keyboard handling. Add role="dialog" and aria-modal="true" to the outer
fixed-position div, provide an accessible name using aria-labelledby pointing to
the h3 "Edit Custom Links" heading, and add an id to that heading. Implement
focus management by using useEffect to move focus into the modal (preferably to
the first input with id "edit-link-label") when isEditLinksOpen becomes true,
and restore focus to the previously focused element when the modal closes. Add
an onKeyDown handler to the modal to detect the Escape key and call
setIsEditLinksOpen(false) to close the modal, enabling keyboard users to escape
without using the Cancel button.

@PRODHOSH PRODHOSH self-requested a review June 23, 2026 13:05
@PRODHOSH PRODHOSH added ELUSOC ELUSOC project submission in-progress ADVENTURER Intermediate — 25 pts labels Jun 23, 2026
@PRODHOSH

Copy link
Copy Markdown
Owner

@mspandey pls check ur pr and fix all the issues stated by coderabbit
and also currently u have merge conflicts

Also pls read CONTRIBUTING.md properly branch names are supposed to be proper & descriptive

any doubts join discord #34 and ask
happy to help!

@PRODHOSH

Copy link
Copy Markdown
Owner

@mspandey
image
build error as well
pls check locally npm run build and then raise a pr
Thanks!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 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 `@src/app/`[username]/page.tsx:
- Around line 127-137: The custom-links query and ownership check in the page
logic are still reading from params.username even though params has already been
resolved into username. Update the profile lookup and the isOwner comparison in
the page component to use the awaited username variable consistently so both
checks operate on the resolved value rather than the Promise-backed params
object.
- Around line 136-137: The ownership check in the page component is using the
shared Supabase instance, which does not have the current request’s
cookie/session context, so it may miss the signed-in user. Update the `page.tsx`
logic around `supabase.auth.getUser()` and `isOwner` to use a request-scoped
Supabase client created from the incoming request/session context, then compare
`authData.user.user_metadata.user_name` against `params.username` as before.

In `@src/components/profile/ProfileView.tsx`:
- Line 64: The ProfileExtras interface is missing props that ProfileView already
destructures and uses, so update the contract to include isOwner and
customLinks. Add these fields to ProfileExtras in the ProfileView component
module, making sure their types match how ProfileView consumes them and how the
page passes them in.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 668346c5-c184-4f83-a7c0-3058c1127285

📥 Commits

Reviewing files that changed from the base of the PR and between 6c5e0ff and 7c6fdc1.

📒 Files selected for processing (2)
  • src/app/[username]/page.tsx
  • src/components/profile/ProfileView.tsx

Comment on lines +127 to +137
// Fetch custom links and check ownership
const { data: profile } = await supabase
.from('profiles')
.select('custom_links')
.eq('username', params.username)
.single();

const customLinks = profile?.custom_links || [];

const { data: authData } = await supabase.auth.getUser();
const isOwner = authData?.user?.user_metadata?.user_name === params.username;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify params is a Promise and the changed block no longer reads params.username.
sed -n '19,21p;80,82p;127,137p' src/app/\[username\]/page.tsx

Repository: PRODHOSH/ossfolio

Length of output: 723


Use username here instead of params.username

params is a Promise<{ username: string }> and is already awaited above. Both the custom-links lookup and the ownership check should use the resolved username.

Proposed fix
   const { data: profile } = await supabase
     .from('profiles')
     .select('custom_links')
-    .eq('username', params.username)
+    .eq('username', username)
     .single();
@@
   const { data: authData } = await supabase.auth.getUser();
-  const isOwner = authData?.user?.user_metadata?.user_name === params.username;
+  const isOwner = authData?.user?.user_metadata?.user_name === username;
📝 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
// Fetch custom links and check ownership
const { data: profile } = await supabase
.from('profiles')
.select('custom_links')
.eq('username', params.username)
.single();
const customLinks = profile?.custom_links || [];
const { data: authData } = await supabase.auth.getUser();
const isOwner = authData?.user?.user_metadata?.user_name === params.username;
// Fetch custom links and check ownership
const { data: profile } = await supabase
.from('profiles')
.select('custom_links')
.eq('username', username)
.single();
const customLinks = profile?.custom_links || [];
const { data: authData } = await supabase.auth.getUser();
const isOwner = authData?.user?.user_metadata?.user_name === username;
🤖 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 `@src/app/`[username]/page.tsx around lines 127 - 137, The custom-links query
and ownership check in the page logic are still reading from params.username
even though params has already been resolved into username. Update the profile
lookup and the isOwner comparison in the page component to use the awaited
username variable consistently so both checks operate on the resolved value
rather than the Promise-backed params object.

Comment on lines +136 to +137
const { data: authData } = await supabase.auth.getUser();
const isOwner = authData?.user?.user_metadata?.user_name === params.username;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether the repo has a request-scoped Supabase server client/helper.
cat -n src/lib/supabase.ts
rg -n "createServerClient|`@supabase/ssr`|cookies\\(|supabase.auth.getUser" src package.json

Repository: PRODHOSH/ossfolio

Length of output: 903


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the page component and any Supabase server-client helpers.
wc -l src/app/[username]/page.tsx src/lib/supabase.ts package.json
printf '\n--- src/app/[username]/page.tsx (120-160) ---\n'
sed -n '120,160p' src/app/[username]/page.tsx
printf '\n--- package.json relevant deps ---\n'
cat package.json | python3 - <<'PY'
import json,sys
data=json.load(sys.stdin)
for k in ["dependencies","devDependencies"]:
    print(f"[{k}]")
    for name in sorted(data.get(k, {})):
        if "supabase" in name.lower() or name in ("next","react","react-dom"):
            print(f"{name}: {data[k][name]}")
PY
printf '\n--- Supabase auth/server helper search ---\n'
rg -n "createServerClient|cookies\\(|`@supabase/ssr`|createBrowserClient|createServerComponentClient|createMiddlewareClient|auth\\.getUser\\(" src package.json

Repository: PRODHOSH/ossfolio

Length of output: 2260


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the page component and any Supabase server-client helpers.
wc -l src/app/[username]/page.tsx src/lib/supabase.ts package.json
printf '\n--- src/app/[username]/page.tsx (120-160) ---\n'
sed -n '120,160p' src/app/[username]/page.tsx
printf '\n--- package.json relevant deps ---\n'
python3 - <<'PY'
import json
with open("package.json") as f:
    data=json.load(f)
for k in ["dependencies","devDependencies"]:
    print(f"[{k}]")
    for name in sorted(data.get(k, {})):
        if "supabase" in name.lower() or name in ("next","react","react-dom"):
            print(f"{name}: {data[k][name]}")
PY
printf '\n--- Supabase auth/server helper search ---\n'
rg -n "createServerClient|cookies\\(|`@supabase/ssr`|createBrowserClient|createServerComponentClient|createMiddlewareClient|auth\\.getUser\\(" src package.json

Repository: PRODHOSH/ossfolio

Length of output: 1787


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n--- src/app/[username]/page.tsx (1-60) ---\n'
sed -n '1,60p' src/app/[username]/page.tsx
printf '\n--- src/lib/supabase.ts ---\n'
cat -n src/lib/supabase.ts
printf '\n--- any auth/session helpers in src ---\n'
rg -n "getSession\\(|getUser\\(|createServerClient|cookies\\(" src

Repository: PRODHOSH/ossfolio

Length of output: 3157


Use a request-scoped Supabase client for ownership checks. src/lib/supabase.ts creates a singleton without cookie/session context, so supabase.auth.getUser() here can’t reliably see the current request’s signed-in user. isOwner can stay false for the profile owner and hide the edit UI.

🤖 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 `@src/app/`[username]/page.tsx around lines 136 - 137, The ownership check in
the page component is using the shared Supabase instance, which does not have
the current request’s cookie/session context, so it may miss the signed-in user.
Update the `page.tsx` logic around `supabase.auth.getUser()` and `isOwner` to
use a request-scoped Supabase client created from the incoming request/session
context, then compare `authData.user.user_metadata.user_name` against
`params.username` as before.

score: number;
/** ISO 8601 timestamp from Supabase profiles.updated_at — null if the user has never synced. */
updatedAt: string | null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify the prop contract includes every prop destructured by ProfileView and passed by the page.
sed -n '54,94p' src/components/profile/ProfileView.tsx
sed -n '150,163p' src/app/\[username\]/page.tsx

Repository: PRODHOSH/ossfolio

Length of output: 1735


Add isOwner and customLinks to ProfileExtras. ProfileView destructures and uses these props, and the page passes them in, but the interface stops at updatedAt, so the component contract is incomplete.

🤖 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 `@src/components/profile/ProfileView.tsx` at line 64, The ProfileExtras
interface is missing props that ProfileView already destructures and uses, so
update the contract to include isOwner and customLinks. Add these fields to
ProfileExtras in the ProfileView component module, making sure their types match
how ProfileView consumes them and how the page passes them in.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@src/components/profile/ProfileView.tsx`:
- Around line 243-249: Guard the ProfileView custom link rendering so
`customLinks` is normalized before `customLinks.map(...)` is called. In
`ProfileView`, validate that `profile?.custom_links` is an array of link-like
objects before passing it through, and filter out malformed entries so the
anchor list only renders safe `{ url, label }` items. Use the existing
`customLinks` mapping block as the fix location and make the render path
resilient to JSON column values that are objects, strings, or partially invalid.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: cff42379-c43f-4f82-9052-0fa1a27fb6ec

📥 Commits

Reviewing files that changed from the base of the PR and between 7c6fdc1 and ba6f78d.

📒 Files selected for processing (1)
  • src/components/profile/ProfileView.tsx

Comment on lines +243 to +249
{customLinks && customLinks.map((link) => (
<a
key={link.url}
href={link.url}
target="_blank"
rel="noopener noreferrer"
aria-label={`${link.label} (opens in a new tab)`}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Guard JSON-backed customLinks before mapping.

custom_links is loaded from a JSON column and passed through as profile?.custom_links || []; if that value is an object/string or contains malformed entries, Line 243 can throw and break the profile page. Normalize before rendering.

Proposed guard
 }: { user: GitHubUser; repos: GitHubRepo[] } & ProfileExtras) {
+  const safeCustomLinks = Array.isArray(customLinks)
+    ? customLinks.filter(
+        (link): link is { label: string; url: string } =>
+          !!link &&
+          typeof link === "object" &&
+          typeof (link as { label?: unknown }).label === "string" &&
+          typeof (link as { url?: unknown }).url === "string"
+      )
+    : [];
+
...
-       {customLinks && customLinks.map((link) => (
+       {safeCustomLinks.map((link) => (
📝 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
{customLinks && customLinks.map((link) => (
<a
key={link.url}
href={link.url}
target="_blank"
rel="noopener noreferrer"
aria-label={`${link.label} (opens in a new tab)`}
const safeCustomLinks = Array.isArray(customLinks)
? customLinks.filter(
(link): link is { label: string; url: string } =>
!!link &&
typeof link === "object" &&
typeof (link as { label?: unknown }).label === "string" &&
typeof (link as { url?: unknown }).url === "string"
)
: [];
{safeCustomLinks.map((link) => (
<a
key={link.url}
href={link.url}
target="_blank"
rel="noopener noreferrer"
aria-label={`${link.label} (opens in a new tab)`}
🤖 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 `@src/components/profile/ProfileView.tsx` around lines 243 - 249, Guard the
ProfileView custom link rendering so `customLinks` is normalized before
`customLinks.map(...)` is called. In `ProfileView`, validate that
`profile?.custom_links` is an array of link-like objects before passing it
through, and filter out malformed entries so the anchor list only renders safe
`{ url, label }` items. Use the existing `customLinks` mapping block as the fix
location and make the render path resilient to JSON column values that are
objects, strings, or partially invalid.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ADVENTURER Intermediate — 25 pts ELUSOC ELUSOC project submission fix-merge-conflict frontend Related to UI / Next.js in-progress UI Visual / design changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants