feat: Implement Organization Profile Page ui#411
Conversation
|
@Sandijigs is attempting to deploy a commit to the Threadflow Team on Vercel. A member of the Team first needs to authorize it. |
|
Caution Review failedThe pull request is closed. ℹ️ Recent review infoConfiguration used: Organization UI Review profile: CHILL Plan: Pro Disabled knowledge base sources:
📒 Files selected for processing (3)
📝 WalkthroughWalkthroughAdds an organization profile page: server-side metadata generation, a client component that fetches and renders an organization profile with loading/error handling, a Skeleton loading component, and a new API helper and type for fetching organization profiles by slug. Changes
Sequence Diagram(s)sequenceDiagram
participant Browser as Browser
participant NextPage as Next.js Page (server)
participant OrgClient as OrgProfileClient (client)
participant APIlib as lib/api/getOrganizationProfile
participant Backend as Backend API (/organizations/profile/:slug)
Browser->>NextPage: Request /org/[slug]
NextPage->>APIlib: getOrganizationProfile(slug) for metadata (server)
APIlib->>Backend: GET /organizations/profile/:slug
Backend-->>APIlib: 200 + profile JSON
APIlib-->>NextPage: OrganizationProfile
NextPage-->>Browser: HTML + client bundle
Browser->>OrgClient: Hydrated component mounts with slug
OrgClient->>APIlib: getOrganizationProfile(slug) (client)
APIlib->>Backend: GET /organizations/profile/:slug
Backend-->>APIlib: 200 + profile JSON
APIlib-->>OrgClient: OrganizationProfile
OrgClient-->>Browser: Renders profile UI (or skeleton while loading)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 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. ✨ Finishing Touches🧪 Generate unit tests (beta)
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: 5
🧹 Nitpick comments (4)
app/(landing)/org/[id]/page.tsx (1)
10-12: Use typed const arrow exports for TS/TSX declarations.Both exported functions currently use function declarations; convert to typed const arrow functions to match the project convention.
As per coding guidelines,
**/*.{ts,tsx}: Prefer const arrow functions with explicit type annotations over function declarations.Also applies to: 42-44
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/`(landing)/org/[id]/page.tsx around lines 10 - 12, The exported functions are declared using function declarations; convert them to typed const arrow exports with explicit type annotations to match project conventions—replace the generateMetadata function (export async function generateMetadata({ params }: OrgProfilePageProps): Promise<Metadata>) and the other exported function at lines 42-44 with exported const arrow functions (e.g., export const generateMetadata: (args: OrgProfilePageProps) => Promise<Metadata> = async ({ params }) => { ... }) keeping the same parameter and return types and behavior so the signatures remain identical but use const arrow syntax.app/(landing)/org/[id]/loading.tsx (1)
3-3: Use const arrow component with explicit return type.Line 3 uses a function declaration; switch to a typed const arrow to match the TS/TSX guideline.
As per coding guidelines,
**/*.{ts,tsx}: Prefer const arrow functions with explicit type annotations over function declarations.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/`(landing)/org/[id]/loading.tsx at line 3, Replace the function declaration OrgProfileLoading with a const arrow component and add an explicit return type to satisfy TS/TSX guidelines: define a named const (OrgProfileLoading) typed to return JSX.Element or React.ReactElement and export it as the default export, preserving the existing component body/return value; update any references to the default export if needed.app/(landing)/org/[id]/org-profile-client.tsx (2)
267-306: Duplicate skeleton markup should be extracted to one shared component.This block duplicates the loading UI already defined in
app/(landing)/org/[id]/loading.tsx(Line 5-Line 40). Keeping both copies will drift over time.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/`(landing)/org/[id]/org-profile-client.tsx around lines 267 - 306, Duplicate skeleton markup in OrgProfileSkeleton should be extracted into a single reusable component and used by both org-profile-client.tsx and the existing loading.tsx; create a new component (e.g., OrgSkeleton or OrgProfileSkeletonShared) that returns the entire section JSX (banner skeleton, stats grid skeleton, and about skeleton), export it from a shared module, then replace the JSX return in the OrgProfileSkeleton function with a simple render of that new component and import+use the same component inside app/(landing)/org/[id]/loading.tsx so both files share the exact same markup.
64-64: Align component declarations with TS/TSX style guideline.Line 64 and Line 267 use function declarations; prefer typed const arrow components.
As per coding guidelines,
**/*.{ts,tsx}: Prefer const arrow functions with explicit type annotations over function declarations.Also applies to: 267-267
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/`(landing)/org/[id]/org-profile-client.tsx at line 64, Convert the function declaration "export default function OrgProfileClient({ id }: OrgProfileClientProps) { ... }" to a typed const arrow component, e.g. "const OrgProfileClient: React.FC<OrgProfileClientProps> = ({ id }) => { ... }; export default OrgProfileClient;", ensuring you keep the same props type (OrgProfileClientProps) and export; apply the same change to the other function component declared later in this file so all components follow the const arrow + explicit type annotation guideline.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/`(landing)/org/[id]/loading.tsx:
- Line 27: The stats skeleton grid currently uses "grid-cols-2" and renders two
columns on mobile; update the class on the container div in
app/(landing)/org/[id]/loading.tsx (the div with the existing 'mb-8 grid
grid-cols-2 gap-3 sm:gap-4 lg:grid-cols-4' classes) to use mobile 1-column and
responsive breakpoints by replacing the column classes with 'grid-cols-1
sm:grid-cols-2 lg:grid-cols-4' so it renders 1 column on mobile, 2 on small
screens, and 4 on large.
In `@app/`(landing)/org/[id]/org-profile-client.tsx:
- Around line 24-28: The OrgStats shape and rendering must be changed to match
the expected profile contract: replace the current interface OrgStats (which has
totalMembers/totalHackathons/totalGrants) with fields projectsCount,
totalHackathons, totalBounties, and totalGrants, and update the UI cards in
org-profile-client.tsx to read and render those new fields instead of “Members”
or deriving projects from org.hackathons.length; import and use the canonical
Types from the Trustless Work docs for the org/profile stats type so the
component (org-profile-client.tsx) consumes the correct properties
(projectsCount, totalHackathons, totalBounties, totalGrants) throughout the
rendering logic.
- Around line 232-233: The stats grid currently uses "grid grid-cols-2 ...
lg:grid-cols-4" which makes mobile show two columns; update both occurrences
that render the stats grid (the JSX mapping over statCards) to use responsive
classes for 1 column on mobile, 2 on tablet, and 4 on desktop by replacing the
class sequence to include "grid-cols-1 sm:grid-cols-2 lg:grid-cols-4" (keep
existing gap and other classes intact) so the component rendering statCards and
its duplicate instance follow the required breakpoints.
- Around line 212-221: The icon-only social link anchors in
org-profile-client.tsx (the mapped <a> using key={i}, href={link.url},
title={link.label} and rendering <Icon />) lack an explicit accessible name;
update that anchor to include aria-label={link.label} (or add
visually-hidden/sr-only text inside the anchor) so screen readers announce each
link reliably, keeping the existing href, target, rel and styling intact.
- Around line 78-82: The catch block in org-profile-client.tsx currently masks
real fetch failures by unconditionally calling setOrg(MOCK_ORG) and
setStats(MOCK_STATS); remove that fallback so real errors/invalid IDs surface.
Modify the catch to either (a) set an explicit error/notFound state (e.g.,
setNotFound(true) or setError(err)) or rethrow the error so the parent can
handle it, and only use MOCK_ORG/MOCK_STATS when a clear design-preview flag is
present (check process.env.NODE_ENV or an explicit isDesignPreview prop) before
calling setOrg/setStats; update the catch to reference setOrg, setStats,
MOCK_ORG, MOCK_STATS, and any notFound/error state you add (or rethrow) so the
page preserves proper error/not-found behavior.
---
Nitpick comments:
In `@app/`(landing)/org/[id]/loading.tsx:
- Line 3: Replace the function declaration OrgProfileLoading with a const arrow
component and add an explicit return type to satisfy TS/TSX guidelines: define a
named const (OrgProfileLoading) typed to return JSX.Element or
React.ReactElement and export it as the default export, preserving the existing
component body/return value; update any references to the default export if
needed.
In `@app/`(landing)/org/[id]/org-profile-client.tsx:
- Around line 267-306: Duplicate skeleton markup in OrgProfileSkeleton should be
extracted into a single reusable component and used by both
org-profile-client.tsx and the existing loading.tsx; create a new component
(e.g., OrgSkeleton or OrgProfileSkeletonShared) that returns the entire section
JSX (banner skeleton, stats grid skeleton, and about skeleton), export it from a
shared module, then replace the JSX return in the OrgProfileSkeleton function
with a simple render of that new component and import+use the same component
inside app/(landing)/org/[id]/loading.tsx so both files share the exact same
markup.
- Line 64: Convert the function declaration "export default function
OrgProfileClient({ id }: OrgProfileClientProps) { ... }" to a typed const arrow
component, e.g. "const OrgProfileClient: React.FC<OrgProfileClientProps> = ({ id
}) => { ... }; export default OrgProfileClient;", ensuring you keep the same
props type (OrgProfileClientProps) and export; apply the same change to the
other function component declared later in this file so all components follow
the const arrow + explicit type annotation guideline.
In `@app/`(landing)/org/[id]/page.tsx:
- Around line 10-12: The exported functions are declared using function
declarations; convert them to typed const arrow exports with explicit type
annotations to match project conventions—replace the generateMetadata function
(export async function generateMetadata({ params }: OrgProfilePageProps):
Promise<Metadata>) and the other exported function at lines 42-44 with exported
const arrow functions (e.g., export const generateMetadata: (args:
OrgProfilePageProps) => Promise<Metadata> = async ({ params }) => { ... })
keeping the same parameter and return types and behavior so the signatures
remain identical but use const arrow syntax.
ℹ️ Review info
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
app/(landing)/org/[id]/loading.tsxapp/(landing)/org/[id]/org-profile-client.tsxapp/(landing)/org/[id]/page.tsx
| </div> | ||
|
|
||
| {/* Stats grid skeleton */} | ||
| <div className='mb-8 grid grid-cols-2 gap-3 sm:gap-4 lg:grid-cols-4'> |
There was a problem hiding this comment.
Stats skeleton layout misses the mobile 1-column requirement.
Line 27 starts at grid-cols-2, so mobile renders 2 columns instead of the required 1. Use grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 to match the test plan.
Proposed fix
- <div className='mb-8 grid grid-cols-2 gap-3 sm:gap-4 lg:grid-cols-4'>
+ <div className='mb-8 grid grid-cols-1 gap-3 sm:grid-cols-2 sm:gap-4 lg:grid-cols-4'>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/`(landing)/org/[id]/loading.tsx at line 27, The stats skeleton grid
currently uses "grid-cols-2" and renders two columns on mobile; update the class
on the container div in app/(landing)/org/[id]/loading.tsx (the div with the
existing 'mb-8 grid grid-cols-2 gap-3 sm:gap-4 lg:grid-cols-4' classes) to use
mobile 1-column and responsive breakpoints by replacing the column classes with
'grid-cols-1 sm:grid-cols-2 lg:grid-cols-4' so it renders 1 column on mobile, 2
on small screens, and 4 on large.
| <a | ||
| key={i} | ||
| href={link.url} | ||
| target='_blank' | ||
| rel='noopener noreferrer' | ||
| title={link.label} | ||
| className='flex h-9 w-9 items-center justify-center rounded-lg border border-zinc-700/50 bg-zinc-800/50 text-zinc-400 transition-all hover:border-[#a7f950]/30 hover:bg-[#a7f950]/10 hover:text-[#a7f950]' | ||
| > | ||
| <Icon className='h-4 w-4' /> | ||
| </a> |
There was a problem hiding this comment.
Icon-only social links need explicit accessible names.
Line 212-Line 221 currently relies on title; add aria-label (or visible/sr-only text) so assistive tech can reliably announce each link.
Proposed fix
<a
key={i}
href={link.url}
target='_blank'
rel='noopener noreferrer'
title={link.label}
+ aria-label={link.label}
className='flex h-9 w-9 items-center justify-center rounded-lg border border-zinc-700/50 bg-zinc-800/50 text-zinc-400 transition-all hover:border-[`#a7f950`]/30 hover:bg-[`#a7f950`]/10 hover:text-[`#a7f950`]'
>📝 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.
| <a | |
| key={i} | |
| href={link.url} | |
| target='_blank' | |
| rel='noopener noreferrer' | |
| title={link.label} | |
| className='flex h-9 w-9 items-center justify-center rounded-lg border border-zinc-700/50 bg-zinc-800/50 text-zinc-400 transition-all hover:border-[#a7f950]/30 hover:bg-[#a7f950]/10 hover:text-[#a7f950]' | |
| > | |
| <Icon className='h-4 w-4' /> | |
| </a> | |
| <a | |
| key={i} | |
| href={link.url} | |
| target='_blank' | |
| rel='noopener noreferrer' | |
| title={link.label} | |
| aria-label={link.label} | |
| className='flex h-9 w-9 items-center justify-center rounded-lg border border-zinc-700/50 bg-zinc-800/50 text-zinc-400 transition-all hover:border-[`#a7f950`]/30 hover:bg-[`#a7f950`]/10 hover:text-[`#a7f950`]' | |
| > | |
| <Icon className='h-4 w-4' /> | |
| </a> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/`(landing)/org/[id]/org-profile-client.tsx around lines 212 - 221, The
icon-only social link anchors in org-profile-client.tsx (the mapped <a> using
key={i}, href={link.url}, title={link.label} and rendering <Icon />) lack an
explicit accessible name; update that anchor to include aria-label={link.label}
(or add visually-hidden/sr-only text inside the anchor) so screen readers
announce each link reliably, keeping the existing href, target, rel and styling
intact.
| <div className='mb-8 grid grid-cols-2 gap-3 sm:gap-4 lg:grid-cols-4'> | ||
| {statCards.map((stat, index) => { |
There was a problem hiding this comment.
Stats grid breakpoints are off for mobile.
Line 232 and Line 291 start from grid-cols-2; the requirement is mobile 1-column, tablet 2-column, desktop 4-column.
Proposed fix
- <div className='mb-8 grid grid-cols-2 gap-3 sm:gap-4 lg:grid-cols-4'>
+ <div className='mb-8 grid grid-cols-1 gap-3 sm:grid-cols-2 sm:gap-4 lg:grid-cols-4'>
...
- <div className='mb-8 grid grid-cols-2 gap-3 sm:gap-4 lg:grid-cols-4'>
+ <div className='mb-8 grid grid-cols-1 gap-3 sm:grid-cols-2 sm:gap-4 lg:grid-cols-4'>Also applies to: 291-292
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/`(landing)/org/[id]/org-profile-client.tsx around lines 232 - 233, The
stats grid currently uses "grid grid-cols-2 ... lg:grid-cols-4" which makes
mobile show two columns; update both occurrences that render the stats grid (the
JSX mapping over statCards) to use responsive classes for 1 column on mobile, 2
on tablet, and 4 on desktop by replacing the class sequence to include
"grid-cols-1 sm:grid-cols-2 lg:grid-cols-4" (keep existing gap and other classes
intact) so the component rendering statCards and its duplicate instance follow
the required breakpoints.
|
Thank you for your contribution. I appreciate the effort. However, I advise that you wait until the issue is unblocked. Did you see my comment here? Please wait until the organization public data are returned through a new endpoint. That is why the issue is currently blocked. Right now, your implementation is based on assumptions because the response format has not been defined yet. Thanks. |
…re and improve data fetching
Summary
/org/[id]displaying organization name, logo, description, and key statsCloses #392
Test plan
/org/{valid-org-id}and verify org name, logo, tagline, stats, and about section display correctly/org/invalid-idand verify error state is handled gracefullynpm run buildto verify no type errorsSummary by CodeRabbit