Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@TWilson023
Copy link
Collaborator

@TWilson023 TWilson023 commented Sep 17, 2025

Summary by CodeRabbit

  • New Features

    • Added Partner About and Comments views with loading states and error handling.
    • Introduced tabbed About/Comments panel and two-column layout in the application sheet.
    • Enabled previous/next navigation within partner applications and rejected lists.
    • Added “Message” action from the application sheet to contact partners.
    • Added keyboard shortcuts for confirm dialogs and approve/reject actions.
  • Refactor

    • Consolidated partner pages to use shared components for About and Comments.
    • Replaced Partner Info with updated Info Cards across partner layouts.
  • Style

    • Improved sheet responsiveness with container-aware styling.

@vercel
Copy link
Contributor

vercel bot commented Sep 17, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Updated (UTC)
dub Ready Ready Preview Sep 18, 2025 11:36pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 17, 2025

Warning

Rate limit exceeded

@steven-tey has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 20 minutes and 45 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 0f7cd8c and e7bc1d4.

📒 Files selected for processing (1)
  • apps/web/ui/partners/partner-application-sheet.tsx (12 hunks)

Walkthrough

Refactors partner pages to centralize UI into new shared components (PartnerAbout, PartnerComments, PartnerInfoCards). Adds tabbed “About/Comments” panel and prev/next navigation to PartnerApplicationSheet, plus keyboard shortcuts for confirm modal. Updates styling for Sheet container. Page clients now delegate to shared components; some hooks/components are removed.

Changes

Cohort / File(s) Summary
Page clients refactor
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/about/page-client.tsx, .../[partnerId]/comments/page-client.tsx, .../[partnerId]/layout.tsx
About/comments pages simplified to render PartnerAbout/PartnerComments. Layout now uses PartnerInfoCards instead of PartnerInfo. Removes inline logic, skeletons, and comment management from pages.
Applications sheets: prev/next navigation
.../partners/applications/page-client.tsx, .../partners/applications/rejected/page-client.tsx
Adds adjacent navigation by computing previous/next partner IDs and passing onPrevious/onNext to PartnerApplicationSheet; updates URL query param on navigation.
PartnerApplicationSheet overhaul
apps/web/ui/partners/partner-application-sheet.tsx
Converts to two-column layout with PartnerInfoCards and tabbed content (About/Comments). Adds Message button, chevron navigation (onPrevious/onNext), keyboard shortcuts (A/R) for approve/reject, and removes GroupSelector/usePartnerApplicationSheet. Adjusts approval flow and sheet width.
New shared partner components
apps/web/ui/partners/partner-about.tsx, apps/web/ui/partners/partner-comments.tsx, apps/web/ui/partners/partner-application-tabs.tsx
Adds PartnerAbout (description + OnlinePresenceSummary), PartnerComments (CRUD with optimistic updates, MessageInput, CommentCard), and PartnerApplicationTabs (About/Comments with animated indicator and counts).
Partner info consolidation
apps/web/ui/partners/partner-info-cards.tsx
Replaces PartnerInfo with PartnerInfoCards; uses passed partner prop, gates events/bounties by status, adjusts fields/labels, and approved-only sections. Public name change.
Confirm modal shortcuts
apps/web/ui/modals/confirm-modal.tsx
Adds confirmShortcut and confirmShortcutOptions props; binds keyboard shortcut to confirm action via useKeyboardShortcut; centralizes confirm handler.
Sheet container styling
packages/ui/src/sheet.tsx
Adds @container/sheet variant to Drawer.Content className; no logic changes.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Page as Applications Page
  participant Sheet as PartnerApplicationSheet
  participant Tabs as PartnerApplicationTabs
  participant About as PartnerAbout
  participant Comments as PartnerComments

  User->>Page: Open Applications
  Page->>Sheet: Render with partner + {onPrevious,onNext}
  Sheet->>Tabs: set currentTabId ("about" default)
  Tabs->>About: Render (when "about")
  User->>Tabs: Click "Comments"
  Tabs->>Comments: Render PartnerComments(partnerId)
  User->>Sheet: Click ‹ / ›
  Sheet->>Page: onPrevious/onNext()
  Page->>Page: Update ?partnerId in URL
  Page->>Sheet: Re-render with new partner
Loading
sequenceDiagram
  autonumber
  actor User
  participant PC as PartnerComments
  participant API as Actions (create/update/delete)
  participant Cache as SWR Cache

  User->>PC: Submit new comment
  PC->>PC: Optimistic add (temp ID)
  PC->>API: createPartnerCommentAction
  API-->>PC: Success with server ID
  PC->>Cache: mutatePrefix(...) to refresh

  User->>PC: Edit comment
  PC->>API: updatePartnerCommentAction
  API-->>PC: Success
  PC->>Cache: mutatePrefix(...)

  User->>PC: Delete comment
  PC->>API: deletePartnerCommentAction
  API-->>PC: Success
  PC->>Cache: mutatePrefix(...)
Loading
sequenceDiagram
  autonumber
  actor User
  participant Modal as ConfirmModal
  participant Hook as useKeyboardShortcut
  participant Handler as onConfirm

  User->>Modal: Open confirm modal
  Modal->>Hook: Bind confirmShortcut
  User->>Modal: Press shortcut / Click Confirm
  Modal->>Handler: handleConfirm()
  Handler-->>Modal: Resolve -> Close
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • TWilson023

Poem

Ears up, I hop through tabs so bright,
Left, right—chevrons guide my flight.
Comments bloom with nimble grace,
Shortcuts whisper, “Confirm, make haste!”
Cards of info, neat and stout—
A rabbit approves, then bounds on out. 🐇✨

Pre-merge checks and finishing touches

❌ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Partner application sheet updates" is concise and accurately captures the primary focus of the changeset, which centers on substantial updates to the PartnerApplicationSheet and related partner UI (tabs, about/comments, navigation). It is specific enough for a teammate scanning the PR list to understand the main change without being overly verbose.

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
Contributor

@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: 5

♻️ Duplicate comments (1)
apps/web/ui/partners/partner-application-sheet.tsx (1)

262-267: Pass groupId to approval action to avoid wrong group assignment

Approve is gated on partner.groupId, but executeAsync omits it. If the action defaults to program.defaultGroupId when groupId is absent, approvals will land in the wrong group. Add a guard and pass groupId explicitly.

-    onConfirm: async () => {
-      if (!program || !workspaceId) return;
-
-      await executeAsync({
-        workspaceId: workspaceId,
-        partnerId: partner.id,
-      });
-    },
+    onConfirm: async () => {
+      if (!program || !workspaceId) {
+        toast.error("Missing program or workspace. Please refresh and try again.");
+        return;
+      }
+      if (!partner.groupId) {
+        toast.error("Select a group before approving.");
+        return;
+      }
+      await executeAsync({
+        workspaceId,
+        partnerId: partner.id,
+        groupId: partner.groupId,
+      });
+    },

Run to confirm current server logic:

#!/bin/bash
rg -nP -C3 'approvePartnerAction|approve-partner-enrollment|groupId\s*:\s*groupId\s*\|\|\s*program\.defaultGroupId'
🧹 Nitpick comments (12)
apps/web/ui/partners/partner-about.tsx (1)

19-25: Preserve author formatting in description

Allow line breaks and long words to wrap.

-        <p className="text-content-default text-xs">
+        <p className="text-content-default text-xs whitespace-pre-wrap break-words">
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/partners-table.tsx (1)

379-382: Loading state simplification is fine

Using only isLoading matches the removed current-partner side fetch.

Since the details sheet was removed, consider deleting the unused useCurrentPartner helper at the bottom to reduce dead code.

apps/web/ui/partners/partner-application-tabs.tsx (2)

49-86: Add ARIA roles for tabs for better accessibility.

Declare a tablist and mark buttons as tabs with aria-selected.

-    <div className="scrollbar-hide relative z-0 flex items-center justify-between gap-1 overflow-x-auto p-2">
+    <div
+      className="scrollbar-hide relative z-0 flex items-center justify-between gap-1 overflow-x-auto p-2"
+      role="tablist"
+      aria-label="Partner application"
+    >
@@
-              <button
+              <button
                 key={id}
                 type="button"
                 onClick={() => setCurrentTabId(id)}
-                data-selected={isSelected}
+                role="tab"
+                aria-selected={isSelected}
+                data-selected={isSelected}
                 className={cn(

70-72: Prefer design tokens over hard-coded color.

Replace bg-blue-600/text-white with your design system tokens (e.g., bg-accent text-accent-foreground) for theme consistency.

apps/web/ui/partners/partner-comments.tsx (3)

86-93: Also invalidate the comments count; avoid double revalidation.

Update mutations to refresh both list and count; avoid SWR’s extra revalidate since you’re explicitly invalidating.

           mutate(
             async (data) => {
@@
             },
             {
               optimisticData: (data) =>
                 data ? [optimisticComment, ...data] : [optimisticComment],
               rollbackOnError: true,
+              revalidate: false,
             },
           )
-            .then(() => mutatePrefix(`/api/partners/${partnerId}/comments`))
+            .then(() =>
+              mutatePrefix([
+                `/api/partners/${partnerId}/comments`,
+                `/api/partners/${partnerId}/comments/count`,
+              ]),
+            )
-      onSuccess: () => {
+      onSuccess: () => {
         toast.success("Comment edited successfully");
-        mutatePrefix(`/api/partners/${partnerId}/comments`);
+        mutatePrefix(`/api/partners/${partnerId}/comments`);
       },
-      onSuccess: () => {
+      onSuccess: () => {
         toast.success("Comment deleted successfully");
-        mutatePrefix(`/api/partners/${partnerId}/comments`);
+        mutatePrefix([
+          `/api/partners/${partnerId}/comments`,
+          `/api/partners/${partnerId}/comments/count`,
+        ]);
       },

Also applies to: 146-147, 156-157


239-247: Use the app’s confirm modal for consistency.

Replace window.confirm with your ConfirmModal for unified UX and keyboard handling.


193-206: Locale handling nit.

toLocaleTimeString("en-US") hardcodes locale. Consider using formatDate/timeAgo utilities for consistency with the rest of the UI.

apps/web/ui/partners/partner-application-sheet.tsx (5)

279-286: Prevent double-submits on Approve

Also disable the button while the action is pending.

-          disabled={!partner.groupId}
+          disabled={!partner.groupId || isPending}

53-64: Add rel to target=_blank link

Open-in-new-tab links should use rel="noopener noreferrer" to prevent window.opener leaks.

-          <Link
-            href={`/${workspaceSlug}/program/messages/${partner.id}`}
-            target="_blank"
-          >
+          <Link
+            href={`/${workspaceSlug}/program/messages/${partner.id}`}
+            target="_blank"
+            rel="noopener noreferrer"
+          >

57-63: Keep URL state consistent when opening Messages

Consider also clearing partnerId from the URL when closing via the Message button to avoid stale state.

-            <Button
+            <Button
               variant="secondary"
               text="Message"
               icon={<Msgs className="size-4 shrink-0" />}
-              onClick={() => setIsOpen(false)}
+              onClick={() => {
+                setIsOpen(false);
+                // keep URL in sync with closed sheet
+                queryParams({ del: "partnerId", scroll: false });
+              }}
               className="hidden h-9 rounded-lg px-4 sm:flex"
             />

Add the hook near the other hooks in this component:

-  const { slug: workspaceSlug } = useWorkspace();
+  const { slug: workspaceSlug } = useWorkspace();
+  const { queryParams } = useRouterStuff();

66-81: Add accessible labels to navigation buttons

Icon-only buttons need an accessible name.

             <Button
               type="button"
               disabled={!onPrevious}
               onClick={onPrevious}
               variant="secondary"
               className="size-9 rounded-l-lg rounded-r-none p-0"
               icon={<ChevronLeft className="size-3.5" />}
+              aria-label="Previous partner"
             />
             <Button
               type="button"
               disabled={!onNext}
               onClick={onNext}
               variant="secondary"
               className="-ml-px size-9 rounded-l-none rounded-r-lg p-0"
               icon={<ChevronRight className="size-3.5" />}
+              aria-label="Next partner"
             />

83-89: Add aria-label to Close button

Improve accessibility for the icon-only close control.

-            <Button
+            <Button
               variant="outline"
               icon={<X className="size-5" />}
               className="h-auto w-fit p-1"
+              aria-label="Close"
             />
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 24a633e and df69623.

📒 Files selected for processing (12)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/about/page-client.tsx (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/comments/page-client.tsx (1 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/layout.tsx (2 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/applications/page-client.tsx (2 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/applications/rejected/page-client.tsx (2 hunks)
  • apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/partners-table.tsx (2 hunks)
  • apps/web/ui/partners/partner-about.tsx (1 hunks)
  • apps/web/ui/partners/partner-application-sheet.tsx (7 hunks)
  • apps/web/ui/partners/partner-application-tabs.tsx (1 hunks)
  • apps/web/ui/partners/partner-comments.tsx (1 hunks)
  • apps/web/ui/partners/partner-info-cards.tsx (4 hunks)
  • packages/ui/src/sheet.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (8)
apps/web/ui/partners/partner-about.tsx (2)
apps/web/lib/types.ts (1)
  • EnrolledPartnerProps (434-434)
apps/web/ui/partners/online-presence-summary.tsx (1)
  • OnlinePresenceSummary (89-138)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/about/page-client.tsx (2)
apps/web/lib/swr/use-partner.ts (1)
  • usePartner (6-29)
apps/web/ui/partners/partner-about.tsx (1)
  • PartnerAbout (6-53)
apps/web/ui/partners/partner-comments.tsx (8)
apps/web/lib/swr/use-partner-comments.ts (1)
  • usePartnerComments (6-34)
apps/web/lib/actions/partners/create-partner-comment.ts (1)
  • createPartnerCommentAction (13-42)
apps/web/ui/shared/message-input.tsx (1)
  • MessageInput (8-109)
apps/web/lib/types.ts (1)
  • PartnerCommentProps (553-553)
apps/web/lib/actions/partners/update-partner-comment.ts (1)
  • updatePartnerCommentAction (12-37)
apps/web/lib/actions/partners/delete-partner-comment.ts (1)
  • deletePartnerCommentAction (9-23)
packages/utils/src/constants/misc.ts (1)
  • OG_AVATAR_URL (29-29)
packages/ui/src/popover.tsx (1)
  • Popover (25-102)
apps/web/ui/partners/partner-application-tabs.tsx (2)
apps/web/lib/swr/use-partner-comments-count.ts (1)
  • usePartnerCommentsCount (5-31)
packages/ui/src/icons/nucleo/msg.tsx (1)
  • Msg (3-24)
apps/web/ui/partners/partner-info-cards.tsx (4)
apps/web/lib/types.ts (1)
  • EnrolledPartnerProps (434-434)
apps/web/ui/analytics/events/events-table.tsx (1)
  • EventDatum (125-125)
packages/ui/src/icons/nucleo/trophy.tsx (1)
  • Trophy (3-48)
packages/ui/src/icons/nucleo/heart.tsx (1)
  • Heart (3-24)
apps/web/ui/partners/partner-application-sheet.tsx (6)
apps/web/lib/types.ts (1)
  • EnrolledPartnerProps (434-434)
packages/ui/src/sheet.tsx (1)
  • Sheet (74-78)
apps/web/ui/partners/partner-info-cards.tsx (1)
  • PartnerInfoCards (33-254)
apps/web/ui/partners/partner-application-tabs.tsx (1)
  • PartnerApplicationTabs (7-90)
apps/web/ui/partners/partner-about.tsx (1)
  • PartnerAbout (6-53)
apps/web/ui/partners/partner-comments.tsx (1)
  • PartnerComments (30-124)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/layout.tsx (1)
apps/web/ui/partners/partner-info-cards.tsx (1)
  • PartnerInfoCards (33-254)
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/comments/page-client.tsx (1)
apps/web/ui/partners/partner-comments.tsx (1)
  • PartnerComments (30-124)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: api-tests
  • GitHub Check: Vade Review
🔇 Additional comments (10)
apps/web/ui/partners/partner-about.tsx (1)

41-43: Tighten error copy

This page shows both description and links. Message should reflect that.

[ suggest_nitpick_refactor ]

-        Failed to load partner links
+        Failed to load partner details
apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/about/page-client.tsx (1)

11-11: LGTM – nice consolidation

Delegating to PartnerAbout simplifies the page and unifies loading/error states.

apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/partners-table.tsx (1)

58-58: LGTM

Import cleanup aligns with removed details‑sheet logic.

apps/web/ui/partners/partner-info-cards.tsx (2)

44-51: Confirm /api/events returns newest-first (or make ordering explicit)

Repo search returned no /api/events handler, so I couldn't verify the default ordering. With limit=1 the client will show the oldest event if the API is oldest-first — verify the endpoint returns newest-first, or include an explicit ordering parameter in the request (or update the server) so "Last event" always shows the newest.


212-251: No change required — rel="noopener noreferrer" not necessary for this internal link

href is an internal path (starts with '/{workspaceSlug}/...'); per team guidance internal target="_blank" links don't require rel="noopener noreferrer". Optional: guard workspaceSlug to avoid generating /undefined.

Likely an incorrect or invalid review comment.

apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/layout.tsx (1)

10-10: Swap to PartnerInfoCards looks correct; verify deprecation/cleanup.

Import/source change and usage align with PartnerInfoCards’ API ({ partner?: EnrolledPartnerProps }). Please confirm the old local PartnerInfo component and related styles are removed or unused to avoid dead code.

Also applies to: 74-75

apps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/comments/page-client.tsx (1)

3-3: LGTM: centralizing to PartnerComments.

This simplifies the page and reuses the shared component correctly.

Also applies to: 9-9

apps/web/ui/partners/partner-application-sheet.tsx (3)

32-33: LGTM: new navigation handlers

Optional onPrevious/onNext props are a clean extension for keyboard/UX navigation.


93-113: LGTM: layout restructure

The two-column grid with cards + tabbed content reads well and keeps the approval bar out of the scrollable area.


224-227: LGTM: sheet width constraint

The responsive clamp keeps the sheet legible across breakpoints.

Copy link
Contributor

@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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/ui/partners/partner-application-sheet.tsx (1)

323-329: Same double‑binding risk for “R” (reject).

Mirror the approval fix: scope the reject confirm shortcut to the modal only.

-    confirmShortcut: "r",
-    confirmShortcutOptions: { sheet: true, modal: true },
+    confirmShortcut: "r",
+    confirmShortcutOptions: { modal: true },

Also applies to: 337-338

♻️ Duplicate comments (1)
apps/web/ui/partners/partner-application-sheet.tsx (1)

267-270: Approval call omits groupId (verify backend expectation).

You disable Approve unless partner.groupId exists, but don’t pass it to the action. If the action defaults to program.defaultGroupId when groupId is absent, approved partners may land in the wrong group.

If the action expects/uses groupId, pass it:

   await executeAsync({
     workspaceId: workspaceId,
     partnerId: partner.id,
+    groupId: partner.groupId,
   });

Run to verify server semantics:

#!/bin/bash
# Inspect approve-partner action usage of groupId/defaultGroupId
fd -a 'approve-partner' | xargs -I{} rg -n -C3 -e '\bgroupId\b|\bdefaultGroupId\b' {}
# Also check for an "approve-partner-enrollment" path if present
rg -n -C3 -e 'approve[-_]?partner.*(groupId|defaultGroupId)' apps lib packages
🧹 Nitpick comments (5)
apps/web/ui/partners/partner-application-sheet.tsx (4)

54-66: Add rel for target=_blank and guard undefined slug.

Security/a11y nit: include rel="noopener noreferrer". Also consider hiding the button until workspaceSlug is ready to avoid "/undefined/..." links.

-  <Link
-    href={`/${workspaceSlug}/program/messages/${partner.id}`}
-    target="_blank"
-  >
+  {workspaceSlug && (
+    <Link
+      href={`/${workspaceSlug}/program/messages/${partner.id}`}
+      target="_blank"
+      rel="noopener noreferrer"
+    >
       <Button ... />
-  </Link>
+    </Link>
+  )}

67-83: Icon‑only nav buttons need accessible labels.

Add aria-label so screen readers announce their purpose.

   <Button
     type="button"
     disabled={!onPrevious}
     onClick={onPrevious}
     variant="secondary"
     className="size-9 rounded-l-lg rounded-r-none p-0"
+    aria-label="Previous application"
     icon={<ChevronLeft className="size-3.5" />}
   />
   <Button
     type="button"
     disabled={!onNext}
     onClick={onNext}
     variant="secondary"
     className="-ml-px size-9 rounded-l-none rounded-r-lg p-0"
+    aria-label="Next application"
     icon={<ChevronRight className="size-3.5" />}
   />

158-166: Avoid “undefined” program name during initial render.

When program hasn’t loaded, the title renders “promote undefined?”. Gate the fields on program or provide a neutral fallback.

-  const fields = [
+  const fields = program ? [
     {
-      title: `How do you plan to promote ${program?.name}?`,
+      title: `How do you plan to promote ${program.name}?`,
       value: application?.proposal,
     },
     {
       title: "Any additional questions or comments?",
       value: application?.comments,
     },
-  ];
+  ] : [];

And render a lightweight placeholder when !program:

-  return (
-    <div className="grid grid-cols-1 gap-6 text-xs">
-      {fields.map((field) => (
+  return (
+    <div className="grid grid-cols-1 gap-6 text-xs">
+      {program ? fields.map((field) => (
         ...
-      ))}
+      )) : (
+        <div className="h-4 w-28 min-w-0 animate-pulse rounded-md bg-neutral-200" />
+      )}

Also applies to: 170-197


330-333: Non‑null assertion on workspaceId.

workspaceId! can throw if context isn’t ready. Mirror the approve path’s guard or disable the button until available.

-  await rejectPartner({
-    workspaceId: workspaceId!,
-    partnerId: partner.id,
-  });
+  if (!workspaceId) return;
+  await rejectPartner({ workspaceId, partnerId: partner.id });
apps/web/ui/modals/confirm-modal.tsx (1)

89-101: Minor: type name typo (Model → Modal).

Rename for clarity; no behavior change.

-type PromptModelProps = {
+type PromptModalProps = {
   ...
-};
+};
 ...
-function ConfirmModal({
+function ConfirmModal({
   ...
-}: { ... } & PromptModelProps) {
+}: { ... } & PromptModalProps) {
 ...
-export function useConfirmModal(props: PromptModelProps) {
+export function useConfirmModal(props: PromptModalProps) {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 94e7cbd and 02eaa89.

📒 Files selected for processing (2)
  • apps/web/ui/modals/confirm-modal.tsx (4 hunks)
  • apps/web/ui/partners/partner-application-sheet.tsx (10 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
apps/web/ui/partners/partner-application-sheet.tsx (5)
apps/web/lib/types.ts (1)
  • EnrolledPartnerProps (434-434)
apps/web/ui/partners/partner-info-cards.tsx (1)
  • PartnerInfoCards (33-254)
apps/web/ui/partners/partner-application-tabs.tsx (1)
  • PartnerApplicationTabs (7-90)
apps/web/ui/partners/partner-about.tsx (1)
  • PartnerAbout (6-53)
apps/web/ui/partners/partner-comments.tsx (1)
  • PartnerComments (29-128)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Vade Review
  • GitHub Check: build

Copy link
Contributor

@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: 0

🧹 Nitpick comments (6)
apps/web/ui/partners/partner-application-sheet.tsx (6)

54-57: Add rel for security when using target="_blank".

Include rel="noopener noreferrer" to prevent tab‑nabbing.

-          <Link
+          <Link
             href={`/${workspaceSlug}/program/messages/${partner.id}`}
             target="_blank"
+            rel="noopener noreferrer"
           >

67-83: Provide accessible labels for icon‑only navigation buttons.

Screen readers won’t announce intent without an accessible name.

           <Button
             type="button"
             disabled={!onPrevious}
             onClick={onPrevious}
             variant="secondary"
             className="size-9 rounded-l-lg rounded-r-none p-0"
+            aria-label="Previous application"
             icon={<ChevronLeft className="size-3.5" />}
           />
           <Button
             type="button"
             disabled={!onNext}
             onClick={onNext}
             variant="secondary"
             className="-ml-px size-9 rounded-l-none rounded-r-lg p-0"
+            aria-label="Next application"
             icon={<ChevronRight className="size-3.5" />}
           />

170-193: Avoid wrapping fallback text in Linkify.

Simplifies markup and prevents link styles on “No response provided”.

-          <div className="mt-2">
-            {field.value || field.value === "" ? (
-              <Linkify
-                as="p"
-                options={{
-                  target: "_blank",
-                  rel: "noopener noreferrer nofollow",
-                  className:
-                    "underline underline-offset-4 text-neutral-400 hover:text-neutral-700",
-                }}
-              >
-                {field.value || (
-                  <span className="text-content-muted italic">
-                    No response provided
-                  </span>
-                )}
-              </Linkify>
-            ) : (
-              <div className="h-4 w-28 min-w-0 animate-pulse rounded-md bg-neutral-200" />
-            )}
-          </div>
+          <div className="mt-2">
+            {field.value === undefined ? (
+              <div className="h-4 w-28 min-w-0 animate-pulse rounded-md bg-neutral-200" />
+            ) : field.value ? (
+              <Linkify
+                as="p"
+                options={{
+                  target: "_blank",
+                  rel: "noopener noreferrer nofollow",
+                  className:
+                    "underline underline-offset-4 text-neutral-400 hover:text-neutral-700",
+                }}
+              >
+                {field.value}
+              </Linkify>
+            ) : (
+              <p className="text-content-muted italic">No response provided</p>
+            )}
+          </div>

283-291: Disable Approve until required data is loaded.

Prevents a no‑op confirm when program/workspaceId aren’t ready.

         <Button
           type="button"
           variant="primary"
           text="Approve"
           shortcut="A"
           loading={isPending}
           onClick={() => setShowConfirmModal(true)}
+          disabled={!program || !workspaceId}
           className="w-fit shrink-0"
         />

304-315: Also clear partnerId from URL on reject (parity with approve).

Prevents the sheet from reopening due to a lingering query param.

   const { id: workspaceId } = useWorkspace();
+  const { queryParams } = useRouterStuff();
       onSuccess: async () => {
         await mutatePrefix("/api/partners");
         toast.success(
           "Application rejected. No email sent, and they can reapply in 30 days.",
         );
+        queryParams({ del: "partnerId" });
         setIsOpen(false);
       },

33-35: Optional: Arrow‑key navigation between applications.

Small UX boost: allow ArrowLeft/Right to trigger previous/next when available.

   const [currentTabId, setCurrentTabId] = useState<string>("about");
+  useKeyboardShortcut("ArrowLeft", () => onPrevious?.(), { sheet: true });
+  useKeyboardShortcut("ArrowRight", () => onNext?.(), { sheet: true });

Also applies to: 44-46

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d64cd20 and 9a282da.

📒 Files selected for processing (1)
  • apps/web/ui/partners/partner-application-sheet.tsx (10 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-18T17:03:06.183Z
Learnt from: TWilson023
PR: dubinc/dub#2858
File: apps/web/ui/partners/partner-application-sheet.tsx:262-266
Timestamp: 2025-09-18T17:03:06.183Z
Learning: The useKeyboardShortcut hook with context options like {sheet: true, modal: true} requires ALL specified contexts to be present simultaneously. The shortcut will only trigger when both existingSheetBackdrop and existingModalBackdrop are present, using strict equality matching in the implementation.

Applied to files:

  • apps/web/ui/partners/partner-application-sheet.tsx
🧬 Code graph analysis (1)
apps/web/ui/partners/partner-application-sheet.tsx (6)
apps/web/lib/types.ts (1)
  • EnrolledPartnerProps (434-434)
packages/ui/src/sheet.tsx (1)
  • Sheet (74-78)
apps/web/ui/partners/partner-info-cards.tsx (1)
  • PartnerInfoCards (33-254)
apps/web/ui/partners/partner-application-tabs.tsx (1)
  • PartnerApplicationTabs (7-90)
apps/web/ui/partners/partner-about.tsx (1)
  • PartnerAbout (6-53)
apps/web/ui/partners/partner-comments.tsx (1)
  • PartnerComments (29-128)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Vade Review
  • GitHub Check: build
🔇 Additional comments (4)
apps/web/ui/partners/partner-application-sheet.tsx (4)

94-114: Nice compositional split and responsive grid.

The two‑pane layout with tabs is clean and aligns with the container‑query variants.


262-266: Confirm shortcuts while typing in inputs.

Please verify that useKeyboardShortcut suppresses triggers when focus is in inputs/textareas/contentEditable (Comments tab has a MessageInput). If not, guard to avoid surprising modal opens while typing “a”/“r”.

Option (guard locally if needed):

-  useKeyboardShortcut("a", () => setShowConfirmModal(true), { sheet: true });
+  useKeyboardShortcut(
+    "a",
+    () => {
+      const el = document.activeElement as HTMLElement | null;
+      if (el && (el.tagName === "INPUT" || el.tagName === "TEXTAREA" || el.isContentEditable)) return;
+      setShowConfirmModal(true);
+    },
+    { sheet: true },
+  );
-  useKeyboardShortcut("r", () => setShowConfirmModal(true), { sheet: true });
+  useKeyboardShortcut(
+    "r",
+    () => {
+      const el = document.activeElement as HTMLElement | null;
+      if (el && (el.tagName === "INPUT" || el.tagName === "TEXTAREA" || el.isContentEditable)) return;
+      setShowConfirmModal(true);
+    },
+    { sheet: true },
+  );

Also applies to: 274-275, 326-337


225-228: Width constraint looks good.

The responsive clamp keeps the sheet within sensible bounds.


201-208: Comments wrapper LGTM.

Clear heading + reuse of PartnerComments.

@steven-tey steven-tey closed this Sep 18, 2025
@steven-tey steven-tey reopened this Sep 18, 2025
@steven-tey steven-tey merged commit 7a5326d into main Sep 18, 2025
9 of 10 checks passed
@steven-tey steven-tey deleted the application-sheet-updates branch September 18, 2025 23:40
@coderabbitai coderabbitai bot mentioned this pull request Oct 5, 2025
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