-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Partner account settings updates #2686
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
Conversation
- Change the account drop-down to say "account settings". - Change some of the language on the display theme and your email. - Hiding the default workspace when you're logged in as a partner to avoid confusion for just partners.
|
@marcusljf is attempting to deploy a commit to the Dub Team on Vercel. A member of the Team first needs to authorize it. |
WalkthroughIntroduces a new hook useCurrentSubdomain and switches multiple components and SWR hooks to drive admin/partner/app behavior from the subdomain value; updates several UI copy strings; removes the old user-dropdown-old component and replaces usages with the new user-dropdown implementation; adds local hostnames for dev. Changes
Sequence Diagram(s)sequenceDiagram
participant Browser
participant useCurrentSubdomain
participant SettingsPageClient
participant UserDropdown
participant useLinksHook
note right of useCurrentSubdomain #D3E5FF: new hook (app/partners/admin/null)
Browser->>useCurrentSubdomain: read hostname
useCurrentSubdomain-->>Browser: { subdomain }
Browser->>SettingsPageClient: mount settings page
SettingsPageClient->>useCurrentSubdomain: get { subdomain }
alt subdomain === "app"
SettingsPageClient->>SettingsPageClient: render UpdateDefaultWorkspace
else
SettingsPageClient--xUpdateDefaultWorkspace: skip render
end
Browser->>UserDropdown: mount dropdown
UserDropdown->>useCurrentSubdomain: get { subdomain }
UserDropdown->>UserDropdown: build memoized menuOptions based on subdomain
UserDropdown-->>Browser: render menu items (Account settings / refer / switch / logout)
Browser->>useLinksHook: call (maybe)
useLinksHook->>useCurrentSubdomain: get { subdomain }
alt subdomain === "admin"
useLinksHook->>API: fetch /api/admin/links/...
else
useLinksHook->>API: fetch /api/links/...
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
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.
Actionable comments posted: 0
🧹 Nitpick comments (1)
apps/web/app/app.dub.co/(dashboard)/account/settings/page-client.tsx (1)
26-26: Fix double space in the template literal.There's an extra space before the
${APP_NAME}variable in the template literal, which will result in "display name on your Dub account" with double spacing.- description={`This is the display name on your ${APP_NAME} account.`} + description={`This is the display name on your ${APP_NAME} account.`}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
apps/web/app/app.dub.co/(dashboard)/account/settings/page-client.tsx(2 hunks)apps/web/ui/account/upload-avatar.tsx(1 hunks)apps/web/ui/layout/sidebar/user-dropdown.tsx(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
apps/web/app/app.dub.co/(dashboard)/account/settings/page-client.tsx (2)
Learnt from: TWilson023
PR: #2519
File: apps/web/ui/analytics/utils.ts:35-37
Timestamp: 2025-06-16T19:21:23.506Z
Learning: In the useAnalyticsFilterOption function in apps/web/ui/analytics/utils.ts, the pattern options?.context ?? useContext(AnalyticsContext) is intentionally designed as a complete replacement strategy, not a merge. When options.context is provided, it should contain all required fields (baseApiPath, queryString, selectedTab, requiresUpgrade) and completely replace the React context, not be merged with it. This is used for dependency injection or testing scenarios.
Learnt from: TWilson023
PR: #2471
File: apps/web/ui/auth/reset-password-form.tsx:52-65
Timestamp: 2025-06-04T15:09:51.562Z
Learning: In the Dub codebase, server-side validation errors for password fields are handled via toast notifications rather than using react-hook-form's setError method to display field-specific errors. This architectural pattern separates client-side validation feedback (inline) from server-side error handling (toast).
⏰ 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). (1)
- GitHub Check: build
🔇 Additional comments (5)
apps/web/ui/layout/sidebar/user-dropdown.tsx (1)
53-53: LGTM! Clearer menu label improves user experience.The change from "Account" to "Account settings" makes the menu option more descriptive and aligns well with the PR objective to improve clarity in the partner account settings interface.
apps/web/ui/account/upload-avatar.tsx (1)
46-46: LGTM! Improved text clarity and personalization.Adding "your" before the app name and "account" at the end makes the description more personalized and specific, improving the user's understanding of the context.
apps/web/app/app.dub.co/(dashboard)/account/settings/page-client.tsx (3)
11-11: LGTM! Correct imports for the new functionality.The addition of
useEffectanduseStateimports is necessary for the partner page detection logic being implemented.
84-84: LGTM! Correctly implements partner-specific UI hiding.The conditional rendering successfully hides the
UpdateDefaultWorkspacecomponent on partner pages, which aligns perfectly with the PR objective to reduce confusion for partner users.
16-20: Partner page detection is sound and safe in a client componentYour logic correctly detects the “partners.” subdomain at window.location.hostname. Because this file is marked
"use client", it’s rendered entirely on the client and cannot introduce SSR hydration mismatches. No changes required—approved!
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
@coderabbitai full review |
✅ Actions performedFull review triggered. |
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.
Actionable comments posted: 1
🧹 Nitpick comments (1)
apps/web/app/app.dub.co/(dashboard)/account/settings/page-client.tsx (1)
16-16: Avoid unnecessary hook call in app-only route (or handle initial null to prevent flicker)Given this file lives under app.dub.co, the subdomain is effectively always "app". Using a client hook that initializes to null can cause a brief flicker/layout shift before it resolves. If this component is not shared across other domains, simplify by inlining the value.
Apply this local change:
- const { subdomain } = useCurrentSubdomain(); + const subdomain = "app" as const;And ensure the import on Line 9 drops the hook:
import { Form } from "@dub/ui";If this component is intentionally shared across domains, consider improving the hook to compute a synchronous initial value (e.g., via a lazy initializer with typeof window check) to avoid UI jitter.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
apps/web/app/app.dub.co/(dashboard)/account/settings/page-client.tsx(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
apps/web/app/app.dub.co/(dashboard)/account/settings/page-client.tsx (2)
packages/ui/src/hooks/use-current-subdomain.ts (1)
useCurrentSubdomain(4-20)packages/utils/src/constants/main.ts (1)
APP_NAME(1-1)
⏰ 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). (1)
- GitHub Check: build
🔇 Additional comments (2)
apps/web/app/app.dub.co/(dashboard)/account/settings/page-client.tsx (2)
80-80: Confirm rendering scope for UpdateDefaultWorkspaceI wasn’t able to find any occurrences of
<UpdateDefaultWorkspace />under the partners subdomain, which may be due to the previous grep filter returning no results. Before merging, please manually verify that the partners route (e.g.
apps/web/app/app/partners.dub.co/(dashboard)/account/settings/page-client.tsx) does not import or render this component. Once confirmed, update the guard based on your requirements:• If admins should see the component but partners should not, change the condition to exclude partners:
- {subdomain === "app" && <UpdateDefaultWorkspace />} + {subdomain !== "partners" && <UpdateDefaultWorkspace />}• If this page truly only exists under the app domain, remove the conditional and render unconditionally:
- {subdomain === "app" && <UpdateDefaultWorkspace />} + <UpdateDefaultWorkspace />Location needing attention:
- apps/web/app/app/app.dub.co/(dashboard)/account/settings/page-client.tsx (around line 80)
9-9: useCurrentSubdomain is correctly re-exported from the package rootI confirmed that:
- The hook is defined in packages/ui/src/hooks/use-current-subdomain.ts and exported in packages/ui/src/hooks/index.ts via
export * from "./use-current-subdomain";.- The root entrypoint packages/ui/src/index.tsx uses
export * from "./hooks";, souseCurrentSubdomainis included in the top-level bundle.Importing it from
"@dub/ui"is safe and doesn’t require changing to a deep import.
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.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
apps/web/ui/account/upload-avatar.tsx (1)
21-41: Handle network errors and always clearuploadingviatry/catch/finallyIf
fetchrejects (offline, network error),setUploading(false)never runs and the button stays in a loading state. Wrap the submit flow withtry/catch/finally, and surface a helpful error message.- onSubmit={async (e) => { - setUploading(true); - e.preventDefault(); - fetch("/api/user", { - method: "PATCH", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ image }), - }).then(async (res) => { - setUploading(false); - if (res.status === 200) { - await update(); - toast.success("Successfully updated your profile picture!"); - } else { - const { error } = await res.json(); - toast.error(error.message); - } - }); - }} + onSubmit={async (e) => { + e.preventDefault(); + setUploading(true); + try { + const res = await fetch("/api/user", { + method: "PATCH", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ image }), + }); + if (!res.ok) { + // Be resilient if response isn't JSON + let msg = "Failed to update your profile picture."; + try { + const { error } = await res.json(); + if (error?.message) msg = error.message; + } catch {} + throw new Error(msg); + } + await update(); + toast.success("Successfully updated your profile picture!"); + } catch (err) { + const msg = + err instanceof Error + ? err.message + : "Something went wrong updating your profile picture."; + toast.error(msg); + } finally { + setUploading(false); + } + }}apps/web/lib/swr/use-links-count.ts (1)
25-43: Admin path precedence and enabled flag are mishandled in SWR key
- When subdomain === "admin", the current ternary still prefers the workspace path if workspaceId is truthy.
- The admin branch ignores enabled and will fetch even when enabled === false.
Reorder the conditions and gate all branches behind enabled to avoid unintended fetches and ensure admin precedence.
- const { data, error } = useSWR<any>( - workspaceId && !isMegaFolder && enabled - ? `/api/links/count${getQueryString( - { - workspaceId, - ...query, - }, - ignoreParams - ? { include: [] } - : { - exclude: ["import", "upgrade", "newLink"], - }, - )}` - : subdomain === "admin" - ? `/api/admin/links/count${getQueryString({ - ...query, - })}` - : null, + const { data, error } = useSWR<any>( + enabled + ? subdomain === "admin" + ? `/api/admin/links/count${getQueryString({ ...query })}` + : workspaceId && !isMegaFolder + ? `/api/links/count${getQueryString( + { workspaceId, ...query }, + ignoreParams + ? { include: [] } + : { exclude: ["import", "upgrade", "newLink"] }, + )}` + : null + : null, fetcher, { dedupingInterval: 60000, keepPreviousData: true, }, );
♻️ Duplicate comments (1)
apps/web/app/app.dub.co/(dashboard)/account/settings/page-client.tsx (1)
22-22: Fix double space in user-facing copyThere’s an extra space before ${APP_NAME}.
- description={`This is the display name on your ${APP_NAME} account.`} + description={`This is the display name on your ${APP_NAME} account.`}
🧹 Nitpick comments (11)
apps/web/ui/account/upload-avatar.tsx (2)
46-46: Microcopy tweak: tighten phrasing and reuse shared APP_NAME constantCurrent copy reads well, but two minor improvements:
- Slightly shorter: “This is your profile image for your {APP_NAME} account.”
- Consider importing APP_NAME from @dub/utils constants to keep brand-name usage centralized and consistent across the app.
- This is your avatar image on your {process.env.NEXT_PUBLIC_APP_NAME} account. + This is your profile image for your {process.env.NEXT_PUBLIC_APP_NAME} account.Or, if centralizing the constant:
+import { APP_NAME } from "@dub/utils"; ... - This is your avatar image on your {process.env.NEXT_PUBLIC_APP_NAME} account. + This is your profile image for your {APP_NAME} account.
11-11: Initializeimagetonullto match the declared state typeThe state is typed as
string | nullbut initialized asundefined. Initialize tonullfor type/intent consistency and to avoid subtle falsy checks relying onundefined.- const [image, setImage] = useState<string | null>(); + const [image, setImage] = useState<string | null>(null);packages/utils/src/constants/main.ts (1)
42-46: Symmetry suggestion: considerapp.localhostin APP_HOSTNAMES (optional)Since we’re adding
admin.localhosthere, consider whether local dev also benefits fromapp.localhost(no port) to mirroradmin/partners. Today APP_HOSTNAMES supportslocalhostandlocalhost:8888, which is fine—this is only for parity and predictability.Would you like me to open a small follow-up PR to add
app.localhostand verifyuseCurrentSubdomainmappings?apps/web/lib/swr/use-links.ts (1)
59-61: Key selection: confirm admin query params and consider constraining the keyFor the admin path, we pass
getQueryString(opts)without anincludefilter. That’s fine, but:
- Confirm the admin endpoint ignores unknown params (e.g., any workspace-only flags if callers pass them).
- Optionally restrict the admin SWR key to a known subset to reduce accidental cache fragmentation if callers pass transient params.
Example tightening (adjust allowed keys as appropriate):
- ? `/api/admin/links${getQueryString(opts)}` + ? `/api/admin/links${getQueryString(opts, { + include: ["page", "search", "sortBy", "sortOrder", "tagIds", "userId", "domain"] + })}`packages/ui/src/hooks/use-current-subdomain.ts (3)
4-20: Mark hook as client-only to prevent accidental RSC import and improve DXThis module uses React client hooks (useState/useEffect) but isn’t marked as a client module. Adding the directive avoids accidental imports from Server Components and yields clearer Next.js errors.
+ "use client"; + export function useCurrentSubdomain() { const [subdomain, setSubdomain] = useState< "app" | "partners" | "admin" | null >(null);
8-17: Consider SSR-friendly initialization to avoid initial null/flashSince subdomain is set in useEffect, consumers render once with null and then re-render. If that flash is undesirable (e.g., conditional UI like menus or settings), consider a server-derived value via a provider/context, or allow an optional initial value (e.g., from headers) to hydrate synchronously.
19-20: Return a primitive for simpler dependenciesReturning just the string (instead of an object) plays nicer with dependency arrays and memoization. This is optional; it would require updating call sites.
- return { subdomain }; + return subdomain;apps/web/app/app.dub.co/(dashboard)/account/settings/page-client.tsx (1)
80-81: Confirm intended visibility for adminUpdateDefaultWorkspace now renders only when subdomain === "app". If admins should also see this (or never see it), confirm the desired behavior. If admins should see it, gate on subdomain !== "partners" instead.
- {subdomain === "app" && <UpdateDefaultWorkspace />} + {subdomain !== "partners" && <UpdateDefaultWorkspace />}apps/web/ui/layout/sidebar/user-dropdown.tsx (3)
30-44: Type safety: avoid any for iconThe options array types icon as any; align it with the UserOption prop type to catch mismatches at compile time.
- const menuOptions = useMemo(() => { - const options: Array<{ - label: string; - icon: any; - href?: string; - type?: string; - onClick?: () => void; - }> = [ + const menuOptions = useMemo(() => { + type MenuOption = { + label: string; + icon: Icon; + href?: string; + type?: string; + onClick?: () => void; + }; + const options: MenuOption[] = [ { label: "Account settings", icon: User, href: "/account/settings", onClick: () => setOpenPopover(false), }, ];
83-85: Trim useMemo depssetOpenPopover is stable; it can be removed from the dependency array to avoid unnecessary recomputations flagged by some linters.
- }, [subdomain, partner, setOpenPopover]); + }, [subdomain, partner]);
105-111: Use a stable key instead of indexPrevent potential reordering issues by using a deterministic key (e.g., combination of label and href).
- {menuOptions.map((menuOption, idx) => ( + {menuOptions.map((menuOption) => ( <UserOption - key={idx} + key={`${menuOption.label}-${menuOption.href ?? "button"}`} as={menuOption.href ? Link : "button"} {...menuOption} /> ))}
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (10)
apps/web/app/app.dub.co/(dashboard)/account/settings/page-client.tsx(2 hunks)apps/web/lib/swr/use-links-count.ts(3 hunks)apps/web/lib/swr/use-links.ts(3 hunks)apps/web/ui/account/upload-avatar.tsx(1 hunks)apps/web/ui/layout/page-content/page-content-old.tsx(2 hunks)apps/web/ui/layout/sidebar/user-dropdown-old.tsx(0 hunks)apps/web/ui/layout/sidebar/user-dropdown.tsx(2 hunks)packages/ui/src/hooks/index.ts(1 hunks)packages/ui/src/hooks/use-current-subdomain.ts(1 hunks)packages/utils/src/constants/main.ts(2 hunks)
💤 Files with no reviewable changes (1)
- apps/web/ui/layout/sidebar/user-dropdown-old.tsx
🔇 Additional comments (10)
packages/utils/src/constants/main.ts (2)
27-33: Dev DX: Allowingapi.localhost(no port) looks goodAdding
api.localhostalongsideapi.localhost:8888improves flexibility when running the API without the dev port. No functional risk in production sincelocalhostis non-routable.
48-53: Good addition for partners local devIncluding
partners.localhost(no port) complementspartners.localhost:8888and aligns with the new subdomain-driven behavior.packages/ui/src/hooks/index.ts (1)
5-5: ExposeuseCurrentSubdomainvia hooks indexRe-export looks correct and keeps the public API tidy. No side effects.
apps/web/ui/layout/page-content/page-content-old.tsx (2)
7-7: Switch to newUserDropdowncomponentImport and usage updated to the new dropdown—fits the “partner account settings” direction and subdomain-driven menu. No behavioral concerns in this wrapper.
Also applies to: 77-77
7-7: Legacy import removal verifiedThe repository-wide search for
user-dropdown-oldreturned no matches, confirming that all legacy imports have been removed.apps/web/lib/swr/use-links.ts (2)
1-1: Client-only dependency: ensure consumers are client components
useCurrentSubdomain()depends onwindow.locationinternally. Make sureuseLinksis only called from Client Components. Consider a brief TSDoc note on this hook to prevent accidental Server Component usage.If helpful, I can scan typical consumers to ensure they’re under
use clientboundaries.Also applies to: 20-21
63-68: SWR options LGTMReasonable defaults: longer dedupe window, no focus revalidate, and keep previous data improve UX on paginated lists.
apps/web/app/app.dub.co/(dashboard)/account/settings/page-client.tsx (1)
9-16: Good move: drive settings off subdomainImporting and using useCurrentSubdomain to toggle account UI is aligned with the PR’s goal and keeps logic centralized.
apps/web/ui/layout/sidebar/user-dropdown.tsx (2)
48-53: Cross-domain Links: sanity check APP_DOMAIN/PARTNERS_DOMAIN valuesIf APP_DOMAIN/PARTNERS_DOMAIN are absolute URLs (e.g., https://app.dub.co), Link will render an anchor and navigate correctly. If they’re hostnames only (e.g., app.dub.co), this becomes a relative path. Confirm they include protocol. If not, switch to anchors with fully qualified URLs.
Also applies to: 63-69
24-44: Nice consolidation of menu logicThe subdomain-driven memoized menu reduces conditionals in JSX and makes the dropdown easier to extend.
Also applies to: 72-82
Summary by CodeRabbit
New Features
Style
Chores / Refactor