-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Enrolled partner page #2821
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enrolled partner page #2821
Changes from all commits
e2e9fa6
7cc03cc
ebe5b8a
845c0d3
427d75f
d61b32a
f7f17ae
f4414a0
b6c589b
197e701
0b8a7dd
09b7913
1263e5b
7e73283
7a5ea44
173ed0f
5f538a0
790a1fc
af6e224
8fe1282
26508a2
1fba211
478a157
08e411c
440162d
ba242c8
86fc211
e9c6eb3
63ec7b9
dee186b
bd91664
05d9fbb
78fe9c5
2957a1d
ff5ce0d
1d85e83
4cc6697
64b76a1
b29150e
1adec52
865bc9b
05144f8
fcb7706
76752de
438a3bc
e5612b6
d5a4c5d
74d34fe
020166b
02cd6a3
4e5205e
041fa3e
eb98353
259d5b5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| import { getDefaultProgramIdOrThrow } from "@/lib/api/programs/get-default-program-id-or-throw"; | ||
| import { withWorkspace } from "@/lib/auth"; | ||
| import { prisma } from "@dub/prisma"; | ||
| import { NextResponse } from "next/server"; | ||
|
|
||
| // GET /api/partners/:id/comments/count – Get partner comments count | ||
| export const GET = withWorkspace( | ||
| async ({ workspace, params }) => { | ||
| const { partnerId } = params; | ||
| const programId = getDefaultProgramIdOrThrow(workspace); | ||
|
|
||
| const count = await prisma.partnerComment.count({ | ||
| where: { | ||
| programId, | ||
| partnerId, | ||
| }, | ||
| }); | ||
|
|
||
| return NextResponse.json(count); | ||
| }, | ||
| { | ||
| requiredPlan: [ | ||
| "business", | ||
| "business plus", | ||
| "business extra", | ||
| "business max", | ||
| "advanced", | ||
| "enterprise", | ||
| ], | ||
| }, | ||
| ); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| import { getDefaultProgramIdOrThrow } from "@/lib/api/programs/get-default-program-id-or-throw"; | ||
| import { withWorkspace } from "@/lib/auth"; | ||
| import { PartnerCommentSchema } from "@/lib/zod/schemas/programs"; | ||
| import { prisma } from "@dub/prisma"; | ||
| import { NextResponse } from "next/server"; | ||
| import { z } from "zod"; | ||
|
|
||
| // GET /api/partners/:id/comments – Get partner comments | ||
| export const GET = withWorkspace( | ||
| async ({ workspace, params }) => { | ||
| const { partnerId } = params; | ||
| const programId = getDefaultProgramIdOrThrow(workspace); | ||
|
|
||
| const comments = await prisma.partnerComment.findMany({ | ||
| where: { | ||
| programId, | ||
| partnerId, | ||
| }, | ||
| include: { | ||
| user: true, | ||
| }, | ||
| orderBy: { | ||
| createdAt: "desc", | ||
| }, | ||
| }); | ||
|
|
||
| return NextResponse.json(z.array(PartnerCommentSchema).parse(comments)); | ||
| }, | ||
| { | ||
| requiredPlan: [ | ||
| "business", | ||
| "business plus", | ||
| "business extra", | ||
| "business max", | ||
| "advanced", | ||
| "enterprise", | ||
| ], | ||
| }, | ||
| ); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,6 +19,7 @@ import { Button, useMediaQuery } from "@dub/ui"; | |
| import { ChevronLeft, LoadingSpinner } from "@dub/ui/icons"; | ||
| import { OG_AVATAR_URL, cn } from "@dub/utils"; | ||
| import { useAction } from "next-safe-action/hooks"; | ||
| import Link from "next/link"; | ||
| import { redirect, useParams } from "next/navigation"; | ||
| import { useState } from "react"; | ||
| import { toast } from "sonner"; | ||
|
|
@@ -215,17 +216,13 @@ export function ProgramMessagesPartnerPageClient() { | |
| Profile | ||
| </h2> | ||
| <div className="flex items-center gap-2"> | ||
| <Button | ||
| variant="secondary" | ||
| text="View profile" | ||
| className="h-8 rounded-lg px-3" | ||
| onClick={() => | ||
| window.open( | ||
| `/${workspaceSlug}/program/partners?partnerId=${partnerId}`, | ||
| "_blank", | ||
| ) | ||
| } | ||
| /> | ||
| <Link href={`/program/partners/${partnerId}`} target="_blank"> | ||
| <Button | ||
| variant="secondary" | ||
| text="View profile" | ||
| className="h-8 rounded-lg px-3" | ||
| /> | ||
| </Link> | ||
|
Comment on lines
+219
to
+225
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix route (missing slug) and avoid nested interactive elements.
Preferred (if @dub/ui Button supports asChild): - <Link href={`/program/partners/${partnerId}`} target="_blank">
- <Button
- variant="secondary"
- text="View profile"
- className="h-8 rounded-lg px-3"
- />
- </Link>
+ <Button asChild variant="secondary" className="h-8 rounded-lg px-3">
+ <Link
+ href={`/${workspaceSlug}/program/partners/${partnerId}`}
+ target="_blank"
+ rel="noopener noreferrer"
+ >
+ View profile
+ </Link>
+ </Button>Fallback (if asChild unsupported): render an with the same classes instead of Button.
🤖 Prompt for AI Agents |
||
| <button | ||
| type="button" | ||
| onClick={() => setIsRightPanelOpen(false)} | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| "use client"; | ||
|
|
||
| import usePartner from "@/lib/swr/use-partner"; | ||
| import { OnlinePresenceSummary } from "@/ui/partners/online-presence-summary"; | ||
| import { useParams } from "next/navigation"; | ||
|
|
||
| export function ProgramPartnerAboutPageClient() { | ||
| const { partnerId } = useParams() as { partnerId: string }; | ||
| const { partner, error } = usePartner({ partnerId }); | ||
|
|
||
| return partner ? ( | ||
| <> | ||
| <div className="flex flex-col gap-2"> | ||
| <h3 className="text-content-emphasis text-xs font-semibold"> | ||
| Description | ||
| </h3> | ||
| <p className="text-content-default text-xs"> | ||
| {partner.description || ( | ||
| <span className="italic text-neutral-400"> | ||
| No description provided | ||
| </span> | ||
| )} | ||
| </p> | ||
| </div> | ||
|
|
||
| <div className="flex flex-col gap-2"> | ||
| <h3 className="text-content-emphasis text-xs font-semibold"> | ||
| Website and socials | ||
| </h3> | ||
| <OnlinePresenceSummary | ||
| partner={partner} | ||
| showLabels={false} | ||
| className="gap-y-2" | ||
| /> | ||
| </div> | ||
| </> | ||
| ) : error ? ( | ||
| <div className="flex justify-center py-16"> | ||
| <span className="text-content-subtle text-sm"> | ||
| Failed to load partner links | ||
| </span> | ||
| </div> | ||
| ) : ( | ||
| [...Array(2)].map((_, index) => ( | ||
| <div key={index} className="flex flex-col gap-2"> | ||
| <div className="h-4 w-20 animate-pulse rounded-md bg-neutral-200" /> | ||
| <div className="h-4 w-40 animate-pulse rounded-md bg-neutral-200" /> | ||
| </div> | ||
| )) | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| import { ProgramPartnerAboutPageClient } from "./page-client"; | ||
|
|
||
| export default function ProgramPartnerAboutPage() { | ||
| return ( | ||
| <> | ||
| <h2 className="text-content-emphasis text-lg font-semibold">About</h2> | ||
| <div className="mt-5 flex flex-col gap-5"> | ||
| <ProgramPartnerAboutPageClient /> | ||
| </div> | ||
| </> | ||
| ); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The "View profile" link is missing the workspace slug, which will result in a broken navigation link.
View Details
Analysis
Missing workspace slug in partner profile navigation link
What fails: Link component in ProgramMessagesPartnerPageClient (line 219) navigates to
/program/partners/${partnerId}instead of/${workspaceSlug}/program/partners/${partnerId}, causing broken navigationHow to reproduce:
/program/partners/[partnerId]route which doesn't existResult: Navigation fails because Next.js App Router requires all dynamic segments including
[slug]to be present in navigation hrefsExpected: Should navigate to
/${workspaceSlug}/program/partners/${partnerId}as confirmed by the route structure atapps/web/app/app.dub.co/(dashboard)/[slug]/(ee)/program/partners/[partnerId]/and similar patterns in partner-nav.tsx