Conversation
Introduce a new IntroductionRequestFeed component (mocked data) that shows network intro requests, lets users 'Check my network' (clears chat, navigates home, and sends a query via useAIChat), mark requests as sent, and dismiss them. Integrate the feed into ChatContent in two places and add a dev preview page at /dev/intro-requests. Register the new dev route in routes.tsx. Feed currently uses in-file mock data and should be replaced with an API when available.
… polish - IntroductionRequestFeed: user chip inline component (avatar+name pill), remove card wrappers (flat list), remove narrator chip, bump section titles from text-xs to text-sm, rename "Check my network" to "Who fits" - OpportunityCardInChat: dual-avatar header for introducer view showing both parties as [pic Name] → [pic Name], with fallback parsing from headline for existing card data without parties field - Backend: add parties field to OpportunityCardPayload, FormattedDiscoveryCandidate, HomeCardItem; populate in opportunity.tools.ts, opportunity.discover.ts, and home.graph.ts for introducer cards Made-with: Cursor
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughAdds an introduction-requests discovery feed and dev page, injects the feed into chat empty states, updates UI skeleton/typography, and propagates a new optional Changes
Sequence DiagramsequenceDiagram
participant User
participant IntroRequestFeed
participant ChatContent
participant Router
participant Backend
participant Database
User->>IntroRequestFeed: View feed / click actions
IntroRequestFeed->>IntroRequestFeed: Filter dismissed, compute visibleRequests
IntroRequestFeed->>User: Render requests (avatars, intents)
User->>IntroRequestFeed: Click "Who fits"
IntroRequestFeed->>ChatContent: Clear AI chat state
IntroRequestFeed->>Router: Navigate to "/"
IntroRequestFeed->>ChatContent: Send generated chat query
IntroRequestFeed->>IntroRequestFeed: Mark request as sent
ChatContent->>Backend: Send chat query
Backend->>Database: Query opportunities (may include parties)
Database-->>Backend: Return opportunities
Backend->>ChatContent: Return cards with optional parties
ChatContent->>User: Render opportunity cards (introducer dual-avatar)
User->>IntroRequestFeed: Click "Skip"
IntroRequestFeed->>IntroRequestFeed: Add to dismissedIds
IntroRequestFeed->>User: Remove request from feed
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 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: 3
🧹 Nitpick comments (2)
frontend/src/components/IntroductionRequestFeed.tsx (2)
83-88: Verify state persistence after navigation.The
handleSearchNetworkfunction callsnavigate('/')after updating state. SinceIntroductionRequestFeedis rendered on the home page, the component should remain mounted and retainsentIdsstate. However, if the user navigates away and returns, this ephemeral state will be lost.This is acceptable for the mock-data phase, but ensure the backend integration (IND-208) persists sent/dismissed status server-side.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/IntroductionRequestFeed.tsx` around lines 83 - 88, handleSearchNetwork currently updates local state via setSentIds then calls navigate('/'), which means sentIds is ephemeral and will be lost if the user leaves and returns; for IND-208 you should persist sent/dismissed status to the backend instead of relying only on local state—call the appropriate API (e.g., a new persistIntroStatus or updateIntroRequest endpoint) from handleSearchNetwork (alongside clearChat, navigate, sendMessage, buildQuery) to save request.id and its "sent" status, and update setSentIds only after a successful response or reconcile on component mount by fetching persisted statuses so the UI remains consistent across navigations and sessions.
127-128: Lowercasing the first character may break proper nouns or acronyms.The intent text's first character is lowercased with
request.intent.charAt(0).toLowerCase(). If the intent starts with a proper noun (e.g., "AI-focused investors"), it would become "aI-focused investors".Consider checking if the first character is already lowercase or using a more nuanced approach:
♻️ Suggested fix
- {' '}is looking for {request.intent.charAt(0).toLowerCase() + request.intent.slice(1)} + {' '}is looking for {/^[A-Z]/.test(request.intent) && !/^[A-Z]{2}/.test(request.intent) ? request.intent.charAt(0).toLowerCase() + request.intent.slice(1) : request.intent}Alternatively, adjust the mock data to start with lowercase and rely on content authors to follow the convention.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/IntroductionRequestFeed.tsx` around lines 127 - 128, The current inline transformation in IntroductionRequestFeed uses request.intent.charAt(0).toLowerCase() which can corrupt acronyms/proper nouns; replace that expression with a small helper (e.g., decapitalize or decapitalizeIntent) and use it where the intent is rendered: implement decapitalizeIntent(intent: string) to return the original string if the first two characters are uppercase (acronym) or if the first character is already lowercase, otherwise lower-case only the first character and concatenate the rest, then call decapitalizeIntent(request.intent) in place of the existing charAt(0)...slice(...) expression.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@protocol/src/lib/protocol/graphs/home.graph.ts`:
- Around line 378-384: Replace the direct use of introducerCounterparts.slice(0,
2) when building introducerParties with the existing
getUniqueCounterpartUserIds(...) helper so duplicate actor rows are collapsed
first; call getUniqueCounterpartUserIds(introducerCounterparts) or similar, take
the first two unique IDs, then map those IDs to { userId, name:
userMap.get(id)?.name ?? 'Unknown', avatar: userMap.get(id)?.avatar ?? null } to
construct introducerParties; apply the same change to the other similar blocks
that build presenter/fallback cards (the other introducer occurrences) so both
cards never render the same repeated user.
In `@protocol/src/lib/protocol/support/opportunity.discover.ts`:
- Around line 448-462: The introducer payload can contain duplicate users
because nonIntroducerActors is sliced raw; update the block where viewerRole ===
"introducer" builds parties to first deduplicate by a.userId (from
opportunity.actors) and then take the first two distinct userIds, mapping them
to the party objects using nameByUserId and avatarByUserId; specifically,
replace the current nonIntroducerActors.slice(0, 2).map(...) logic with logic
that iterates nonIntroducerActors, collects unique userIds in order, stops after
two distinct ids, and returns parties built from those unique ids.
In `@protocol/src/lib/protocol/tools/opportunity.tools.ts`:
- Around line 467-480: The new parties field is only added in the synchronous
create_introduction branch; update the other serializers/builders that build
card payloads (the continuation serializer, the regular discovery serializer
used around the card builder at the code near Line 267 and Line 649, and the
list_opportunities serializer/builder) to include parties when present by adding
the same conditional spread (e.g. ...(opp.parties ? { parties: opp.parties } :
{})) to the card payload construction so introducer cards receive the
dual-avatar/header data.
---
Nitpick comments:
In `@frontend/src/components/IntroductionRequestFeed.tsx`:
- Around line 83-88: handleSearchNetwork currently updates local state via
setSentIds then calls navigate('/'), which means sentIds is ephemeral and will
be lost if the user leaves and returns; for IND-208 you should persist
sent/dismissed status to the backend instead of relying only on local state—call
the appropriate API (e.g., a new persistIntroStatus or updateIntroRequest
endpoint) from handleSearchNetwork (alongside clearChat, navigate, sendMessage,
buildQuery) to save request.id and its "sent" status, and update setSentIds only
after a successful response or reconcile on component mount by fetching
persisted statuses so the UI remains consistent across navigations and sessions.
- Around line 127-128: The current inline transformation in
IntroductionRequestFeed uses request.intent.charAt(0).toLowerCase() which can
corrupt acronyms/proper nouns; replace that expression with a small helper
(e.g., decapitalize or decapitalizeIntent) and use it where the intent is
rendered: implement decapitalizeIntent(intent: string) to return the original
string if the first two characters are uppercase (acronym) or if the first
character is already lowercase, otherwise lower-case only the first character
and concatenate the rest, then call decapitalizeIntent(request.intent) in place
of the existing charAt(0)...slice(...) expression.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 346fd0e8-d807-4f2f-957c-a7db0bb50c7d
📒 Files selected for processing (11)
frontend/src/app/dev/intro-requests/page.tsxfrontend/src/components/ChatContent.tsxfrontend/src/components/IntroductionRequestFeed.tsxfrontend/src/components/chat/OpportunityCardInChat.tsxfrontend/src/routes.tsxfrontend/src/services/opportunities.tsprotocol/src/lib/protocol/graphs/home.graph.tsprotocol/src/lib/protocol/states/home.state.tsprotocol/src/lib/protocol/support/opportunity.discover.tsprotocol/src/lib/protocol/tools/opportunity.tools.tsprotocol/src/types/chat-streaming.types.ts
…tent text - home.graph.ts: Use getUniqueCounterpartUserIds() instead of raw slice - opportunity.discover.ts: Deduplicate via Set before slicing parties - IntroductionRequestFeed.tsx: Skip lowercasing when intent starts with acronym
|
Re: nitpick comments on
|
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
🧹 Nitpick comments (1)
frontend/src/components/chat/OpportunityCardInChat.tsx (1)
271-312: Dual-avatar introducer header looks good overall.The fallback parsing from headline (lines 275-280) gracefully handles cases where
card.partiesisn't populated. One minor note: when parsing from headline, the second party'suserIdis set to an empty string (line 277). WhileUserAvatarhandles this gracefully via itsname-based fallback, consider usingundefinedfor clearer semantics:♻️ Optional: use undefined instead of empty string
: card.headline?.includes("→") ? card.headline.split("→").map((n, i) => ({ - userId: i === 0 ? card.userId : "", + userId: i === 0 ? card.userId : undefined, name: n.trim(), avatar: i === 0 ? (card.avatar ?? null) : null, }))This would also require updating the
partiestype to allowuserId?: string(already compatible since the interface allows optional fields).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/src/components/chat/OpportunityCardInChat.tsx` around lines 271 - 312, In OpportunityCardInChat adjust the headline-parsing fallback so the second party’s userId is undefined instead of an empty string (change the map branch that sets userId: i === 0 ? card.userId : "" to use undefined for the non-first party), and update the parties type/interface to allow userId?: string so UserAvatar (and any other consumers) receive an absent userId rather than an empty string.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@frontend/src/components/chat/OpportunityCardInChat.tsx`:
- Around line 271-312: In OpportunityCardInChat adjust the headline-parsing
fallback so the second party’s userId is undefined instead of an empty string
(change the map branch that sets userId: i === 0 ? card.userId : "" to use
undefined for the non-first party), and update the parties type/interface to
allow userId?: string so UserAvatar (and any other consumers) receive an absent
userId rather than an empty string.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 5437f591-1d6c-4ef3-93bb-81f2bc5292c9
📒 Files selected for processing (9)
frontend/src/components/ChatContent.tsxfrontend/src/components/IntroductionRequestFeed.tsxfrontend/src/components/chat/OpportunityCardInChat.tsxfrontend/src/routes.tsxprotocol/src/lib/protocol/graphs/home.graph.tsprotocol/src/lib/protocol/states/home.state.tsprotocol/src/lib/protocol/support/opportunity.discover.tsprotocol/src/lib/protocol/tools/opportunity.tools.tsprotocol/src/types/chat-streaming.types.ts
✅ Files skipped from review due to trivial changes (5)
- protocol/src/types/chat-streaming.types.ts
- frontend/src/routes.tsx
- protocol/src/lib/protocol/states/home.state.ts
- protocol/src/lib/protocol/graphs/home.graph.ts
- frontend/src/components/IntroductionRequestFeed.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
- frontend/src/components/ChatContent.tsx
- protocol/src/lib/protocol/support/opportunity.discover.ts
Fallback card now always includes an "Index" narrator chip for non-introducer opportunities (filtered out by frontend). Update test expectation to match.
Replace ambiguous 'Who fits' button text with 'Find matches' in frontend/src/components/IntroductionRequestFeed.tsx to improve clarity for users. No functional changes made.
Summary
[pic Name] → [pic Name], with backward-compatible fallback parsing from headlinepartiesfield across the full data pipeline (types, tools, discover, home graph) so introducer cards carry both party identities for the frontendFollow-up
Summary by CodeRabbit
New Features
Improvements