-
Notifications
You must be signed in to change notification settings - Fork 610
feat(dashboard): Slug routing #4009
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
Merged
Merged
Changes from all commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
158238e
feat: implement workspace-based URL routing
perkinsjr 32bdf85
invalidate api overview query after deletion
MichaelUnkey d72dbaa
Merge branch 'main' into slug-routing
perkinsjr 5c7bca5
added hook to handle navigation of workspace.slug items
MichaelUnkey d8e986d
Added suspense
MichaelUnkey fa011c8
fix permission updated and created values and fmt
MichaelUnkey 1a086c6
biome ignore and removed comma
MichaelUnkey 1b88e7f
why suspend many when you can suspend one.
perkinsjr 4030301
Merge branch 'main' into slug-routing
perkinsjr b92f897
merge main into slug-routing
MichaelUnkey 9d1805f
Comments in useWorkspaceNavigation and remove missed code no longer
MichaelUnkey 988b3ee
fix reverted workspace.slug
MichaelUnkey 4a191c5
[autofix.ci] apply automated fixes
autofix-ci[bot] c3a5dbb
fix: Update to [workspaceSlug]
perkinsjr 52d5273
Merge branch 'main' of https://github.com/unkeyed/unkey into slug-rou…
MichaelUnkey 1d40ae3
Missed import changes
MichaelUnkey c3545ff
fmt and workspace.id change to slug
MichaelUnkey File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
There are no files selected for viewing
File renamed without changes.
File renamed without changes.
6 changes: 3 additions & 3 deletions
6
...ey/components/external-id-field/index.tsx → ...ey/components/external-id-field/index.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
...ate-key/components/key-secret-section.tsx → ...ate-key/components/key-secret-section.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
234 changes: 234 additions & 0 deletions
234
apps/dashboard/app/(app)/[workspaceSlug]/apis/[apiId]/api-id-navbar.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,234 @@ | ||
| "use client"; | ||
|
|
||
| import { QuickNavPopover } from "@/components/navbar-popover"; | ||
| import { NavbarActionButton } from "@/components/navigation/action-button"; | ||
| import { Navbar } from "@/components/navigation/navbar"; | ||
| import { useIsMobile } from "@/hooks/use-mobile"; | ||
| import { useWorkspaceNavigation } from "@/hooks/use-workspace-navigation"; | ||
| import { trpc } from "@/lib/trpc/client"; | ||
| import type { Workspace } from "@unkey/db"; | ||
| import { ChevronExpandY, Nodes, Plus, TaskUnchecked } from "@unkey/icons"; | ||
| import { CreateKeyDialog } from "./_components/create-key"; | ||
|
|
||
| // Types for better type safety | ||
| interface ApiLayoutData { | ||
| currentApi: { | ||
| id: string; | ||
| name: string; | ||
| workspaceId: string; | ||
| keyAuthId: string | null; | ||
| keyspaceDefaults: { | ||
| prefix?: string; | ||
| bytes?: number; | ||
| } | null; | ||
| deleteProtection: boolean | null; | ||
| ipWhitelist: string | null; | ||
| }; | ||
| workspaceApis: Array<{ | ||
| id: string; | ||
| name: string; | ||
| }>; | ||
| keyAuth: { | ||
| id: string; | ||
| defaultPrefix: string | null; | ||
| defaultBytes: number | null; | ||
| sizeApprox: number; | ||
| } | null; | ||
| workspace: { | ||
| id: string; | ||
| ipWhitelist: boolean; | ||
| }; | ||
| } | ||
|
|
||
| interface ApisNavbarProps { | ||
| apiId: string; | ||
| keyspaceId?: string; | ||
| keyId?: string; | ||
| activePage?: { | ||
| href: string; | ||
| text: string; | ||
| }; | ||
| } | ||
|
|
||
| interface LoadingNavbarProps { | ||
| workspace: Workspace; | ||
| } | ||
|
|
||
| interface NavbarContentProps { | ||
| apiId: string; | ||
| keyspaceId?: string; | ||
| keyId?: string; | ||
| activePage?: { | ||
| href: string; | ||
| text: string; | ||
| }; | ||
| workspace: Workspace; | ||
| isMobile: boolean; | ||
| layoutData: ApiLayoutData; | ||
| } | ||
|
|
||
| // Loading state component | ||
| const LoadingNavbar = ({ workspace }: LoadingNavbarProps) => ( | ||
| <Navbar> | ||
| <Navbar.Breadcrumbs icon={<Nodes />}> | ||
| <Navbar.Breadcrumbs.Link href={`/${workspace.slug}/apis`}>APIs</Navbar.Breadcrumbs.Link> | ||
| <Navbar.Breadcrumbs.Link href="#" isIdentifier className="group" noop> | ||
| <div className="h-6 w-20 bg-grayA-3 rounded animate-pulse transition-all " /> | ||
| </Navbar.Breadcrumbs.Link> | ||
| <Navbar.Breadcrumbs.Link href="#" noop active> | ||
| <div className="hover:bg-gray-3 rounded-lg flex items-center gap-1 p-1"> | ||
| <div className="h-6 w-16 bg-grayA-3 rounded animate-pulse transition-all " /> | ||
| <ChevronExpandY className="size-4" /> | ||
| </div> | ||
| </Navbar.Breadcrumbs.Link> | ||
| </Navbar.Breadcrumbs> | ||
| <Navbar.Actions> | ||
| <NavbarActionButton disabled> | ||
| <Plus /> | ||
| Create new key | ||
| </NavbarActionButton> | ||
| <div className="h-7 bg-grayA-2 border border-gray-6 rounded-md animate-pulse px-3 flex gap-2 items-center justify-center w-[190px] transition-all "> | ||
| <div className="h-3 w-[190px] bg-grayA-3 rounded" /> | ||
| <div> | ||
| <TaskUnchecked size="md-regular" className="!size-4" /> | ||
| </div> | ||
| </div> | ||
| </Navbar.Actions> | ||
| </Navbar> | ||
| ); | ||
|
|
||
| // Main navbar content component | ||
| const NavbarContent = ({ | ||
| keyspaceId, | ||
| keyId, | ||
| activePage, | ||
| workspace, | ||
| isMobile, | ||
| layoutData, | ||
| }: NavbarContentProps) => { | ||
| const shouldFetchKey = Boolean(keyspaceId && keyId); | ||
|
|
||
| // If we expected to find a key but this component doesn't handle key details, | ||
| // we should handle this at a higher level or in a different component | ||
| if (shouldFetchKey) { | ||
| console.warn("Key fetching logic should be handled at a higher level"); | ||
| } | ||
|
|
||
| const { currentApi } = layoutData; | ||
|
|
||
| // Define base path for API navigation | ||
| const base = `/${workspace.slug}/apis/${currentApi.id}`; | ||
|
|
||
| // Create navigation items for QuickNavPopover | ||
| const navigationItems = [ | ||
| { | ||
| id: "requests", | ||
| label: "Requests", | ||
| href: `/${workspace.slug}/apis/${currentApi.id}`, | ||
| }, | ||
| ]; | ||
|
|
||
| // Add Keys navigation if keyAuthId exists | ||
| if (currentApi.keyAuthId) { | ||
| navigationItems.push({ | ||
| id: "keys", | ||
| label: "Keys", | ||
| href: `/${workspace.slug}/apis/${currentApi.id}/keys/${currentApi.keyAuthId}`, | ||
| }); | ||
| } | ||
|
|
||
| // Add Settings navigation | ||
| navigationItems.push({ | ||
| id: "settings", | ||
| label: "Settings", | ||
| href: `/${workspace.slug}/apis/${currentApi.id}/settings`, | ||
| }); | ||
|
|
||
| return ( | ||
| <div className="w-full"> | ||
| <Navbar className="w-full flex justify-between"> | ||
| <Navbar.Breadcrumbs className="flex-1 w-full" icon={<Nodes />}> | ||
| <Navbar.Breadcrumbs.Link | ||
| href={`/${workspace.slug}/apis`} | ||
| className={isMobile ? "hidden" : "max-md:hidden"} | ||
| > | ||
| APIs | ||
| </Navbar.Breadcrumbs.Link> | ||
| <Navbar.Breadcrumbs.Link | ||
| href={base} | ||
| isIdentifier | ||
| className={isMobile ? "hidden" : "group max-md:hidden"} | ||
| noop | ||
| > | ||
| <div className="text-accent-10 group-hover:text-accent-12">{currentApi.name}</div> | ||
| </Navbar.Breadcrumbs.Link> | ||
| <Navbar.Breadcrumbs.Link href={activePage?.href ?? ""} noop active={!shouldFetchKey}> | ||
| <QuickNavPopover items={navigationItems} shortcutKey="M"> | ||
| <div className="hover:bg-gray-3 rounded-lg flex items-center gap-1 p-1"> | ||
| {activePage?.text ?? ""} | ||
| <ChevronExpandY className="size-4" /> | ||
| </div> | ||
| </QuickNavPopover> | ||
perkinsjr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| </Navbar.Breadcrumbs.Link> | ||
| </Navbar.Breadcrumbs> | ||
| {layoutData.keyAuth ? ( | ||
| <CreateKeyDialog | ||
| keyspaceId={layoutData.keyAuth.id} | ||
| apiId={currentApi.id} | ||
| copyIdValue={currentApi.id} | ||
| keyspaceDefaults={currentApi.keyspaceDefaults} | ||
| /> | ||
| ) : ( | ||
| <NavbarActionButton disabled> | ||
| <Plus /> | ||
| Create new key | ||
| </NavbarActionButton> | ||
| )} | ||
| </Navbar> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| // Main component | ||
| export const ApisNavbar = ({ apiId, keyspaceId, keyId, activePage }: ApisNavbarProps) => { | ||
| const workspace = useWorkspaceNavigation(); | ||
|
|
||
| const isMobile = useIsMobile(); | ||
|
|
||
| // Only make the query if we have a valid apiId | ||
| const { | ||
| data: layoutData, | ||
| isLoading, | ||
| error, | ||
| } = trpc.api.queryApiKeyDetails.useQuery( | ||
| { apiId }, | ||
| { | ||
| enabled: Boolean(apiId), // Only run query if apiId exists | ||
| retry: 3, | ||
| retryDelay: 1000, | ||
| }, | ||
| ); | ||
|
|
||
| // Show loading state while fetching data | ||
| if (isLoading || !layoutData) { | ||
| return <LoadingNavbar workspace={workspace} />; | ||
| } | ||
|
|
||
| // Handle error state | ||
| if (error) { | ||
| console.error("Failed to fetch API layout data:", error); | ||
| return <LoadingNavbar workspace={workspace} />; | ||
| } | ||
|
|
||
| return ( | ||
| <NavbarContent | ||
| apiId={apiId} | ||
| keyspaceId={keyspaceId} | ||
| keyId={keyId} | ||
| activePage={activePage} | ||
| workspace={workspace} | ||
| isMobile={isMobile} | ||
| layoutData={layoutData} | ||
| /> | ||
| ); | ||
| }; | ||
File renamed without changes.
File renamed without changes.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.