Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
d6c0b5c
Enhance API key management UI and functionality
sarimrmalik Apr 15, 2026
50b6c6d
Refactor cache tag handling in teams API router
sarimrmalik Apr 15, 2026
99831d1
Refactor teams API router to remove unused cache tag handling
sarimrmalik Apr 15, 2026
edba037
Enhance API key management UI with new components and improved functi…
sarimrmalik Apr 15, 2026
255669e
Refactor CreateApiKeyDialog for improved UI and functionality
sarimrmalik Apr 15, 2026
1adab93
Show filtered API key counts while searching
sarimrmalik Apr 15, 2026
30fc9c9
Refactor member and API key components to enhance UI and functionality
sarimrmalik Apr 15, 2026
a45f618
Refactor ApiKeysTableRow and ApiKeysTable for improved UI and functio…
sarimrmalik Apr 15, 2026
22dcffe
Refactor ApiKeysTableRow and ApiKeysTable for improved layout and fun…
sarimrmalik Apr 15, 2026
366f260
Refactor API key date formatting for improved consistency and readabi…
sarimrmalik Apr 15, 2026
ee79239
Refactor DeleteApiKeyDialog for improved layout and user experience
sarimrmalik Apr 15, 2026
f74c5a5
Refactor API keys page layout and improve component structure
sarimrmalik Apr 17, 2026
67bcbc5
Run biome format
sarimrmalik Apr 17, 2026
ee6b063
Refactor ApiKeysTableRow and ApiKeysTable for improved layout and fun…
sarimrmalik Apr 17, 2026
a576ee4
Enhance API key ID display and copy functionality in ApiKeysTableRow
sarimrmalik Apr 17, 2026
4625fc8
Refactor ApiKeysTableRow and utils for improved API key ID handling
sarimrmalik Apr 17, 2026
7ad7122
Run biome format
sarimrmalik Apr 17, 2026
a28d30e
Update getApiKeyIdBadgeLabel function to improve ID badge formatting
sarimrmalik Apr 17, 2026
ad12602
Refactor ApiKeysTable header styling for improved alignment
sarimrmalik Apr 20, 2026
e888854
Merge remote-tracking branch 'origin/main' into refactor/api-keys-page
sarimrmalik Apr 20, 2026
210d931
Merge remote-tracking branch 'origin/main' into refactor/api-keys-page
sarimrmalik Apr 29, 2026
579ce57
refactor: enhance ApiKeysTableRow and ApiKeysTable components
sarimrmalik Apr 29, 2026
579f93d
style: update ApiKeyIdBadge to enhance text visibility
sarimrmalik Apr 29, 2026
8a1d805
fix: adjust formatRelativeAgo function for accurate month calculation
sarimrmalik Apr 29, 2026
a44fd0f
Merge remote-tracking branch 'origin/main' into refactor/api-keys-page
sarimrmalik May 1, 2026
b49cc8a
refactor: replace deprecated loader_d component with updated loader
sarimrmalik May 2, 2026
312cdc3
refactor: replace Loader with Skeleton and enhance loading state hand…
sarimrmalik May 2, 2026
940f04d
refactor: simplify ApiKeysPageContent and related components by remov…
sarimrmalik May 2, 2026
364e8f9
refactor: enhance API keys management by integrating Suspense and Cat…
sarimrmalik May 2, 2026
ffee428
feat: implement CreateApiKeySchema for API key validation
sarimrmalik May 2, 2026
2b2007a
refactor: streamline ApiKeysTableRow component by consolidating cell …
sarimrmalik May 2, 2026
011d505
refactor: enhance API key deletion flow in dashboard
sarimrmalik May 2, 2026
70c5bbc
Merge branch 'main' of https://github.com/e2b-dev/dashboard into refa…
sarimrmalik May 2, 2026
6eac5c0
refactor: optimize rendering in ApiKeysTableRow component
sarimrmalik May 2, 2026
bc593e3
refactor: render API keys search field eagerly with disabled state
ben-fornefeld May 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ next-env.d.ts
CLAUDE.md
.agent

# Cursor IDE
.cursor/

# tooling
/template
58 changes: 13 additions & 45 deletions src/app/dashboard/[teamSlug]/keys/page.tsx
Original file line number Diff line number Diff line change
@@ -1,55 +1,23 @@
import { Plus } from 'lucide-react'
import CreateApiKeyDialog from '@/features/dashboard/settings/keys/create-api-key-dialog'
import ApiKeysTable from '@/features/dashboard/settings/keys/table'
import Frame from '@/ui/frame'
import { Button } from '@/ui/primitives/button'
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@/ui/primitives/card'
import { Page } from '@/features/dashboard/layouts/page'
import { ApiKeysPageContent } from '@/features/dashboard/settings/keys'
import { HydrateClient, prefetch, trpc } from '@/trpc/server'

interface KeysPageClientProps {
interface KeysPageProps {
params: Promise<{
teamSlug: string
}>
}

export default async function KeysPage({ params }: KeysPageClientProps) {
return (
<Frame
classNames={{
wrapper: 'w-full max-md:p-0',
frame: 'max-md:border-none',
}}
>
<Card className="w-full">
<CardHeader>
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between sm:gap-6">
<div className="flex flex-col gap-2">
<CardTitle>Manage API Keys</CardTitle>
<CardDescription className="max-w-[400px]">
API Keys are used to authenticate API requests from your teams
applications.
</CardDescription>
</div>
export default async function KeysPage({ params }: KeysPageProps) {
const { teamSlug } = await params

<CreateApiKeyDialog>
<Button className="w-full sm:w-auto sm:self-start">
<Plus className="size-4" /> CREATE KEY
</Button>
</CreateApiKeyDialog>
</div>
</CardHeader>
prefetch(trpc.teams.listApiKeys.queryOptions({ teamSlug }))

<CardContent>
<div className="w-full overflow-x-auto">
<ApiKeysTable params={params} className="min-w-[800px]" />
</div>
</CardContent>
</Card>
</Frame>
return (
<HydrateClient>
<Page>
<ApiKeysPageContent teamSlug={teamSlug} />
</Page>
</HydrateClient>
)
}
1 change: 0 additions & 1 deletion src/configs/cache.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
export const CACHE_TAGS = {
TEAM_ID_FROM_SLUG: (segment: string) => `team-id-from-slug-${segment}`,
TEAM_USAGE: (teamId: string) => `team-usage-${teamId}`,
TEAM_API_KEYS: (teamId: string) => `team-api-keys-${teamId}`,

DEFAULT_TEMPLATES: 'default-templates',
} as const
100 changes: 0 additions & 100 deletions src/core/server/actions/key-actions.ts

This file was deleted.

85 changes: 83 additions & 2 deletions src/core/server/api/routers/teams.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,30 @@
import { z } from 'zod'
import { createKeysRepository } from '@/core/modules/keys/repository.server'
import { createUserTeamsRepository } from '@/core/modules/teams/user-teams-repository.server'
import { throwTRPCErrorFromRepoError } from '@/core/server/adapters/errors'
import { withAuthedRequestRepository } from '@/core/server/api/middlewares/repository'
import {
withAuthedRequestRepository,
withTeamAuthedRequestRepository,
} from '@/core/server/api/middlewares/repository'
import { createTRPCRouter } from '@/core/server/trpc/init'
import { protectedProcedure } from '@/core/server/trpc/procedures'
import {
protectedProcedure,
protectedTeamProcedure,
} from '@/core/server/trpc/procedures'
import { l } from '@/core/shared/clients/logger/logger'

const teamsRepositoryProcedure = protectedProcedure.use(
withAuthedRequestRepository(createUserTeamsRepository, (teamsRepository) => ({
teamsRepository,
}))
)

const keysRepositoryProcedure = protectedTeamProcedure.use(
withTeamAuthedRequestRepository(createKeysRepository, (keysRepository) => ({
keysRepository,
}))
)

export const teamsRouter = createTRPCRouter({
list: teamsRepositoryProcedure.query(async ({ ctx }) => {
const teamsResult = await ctx.teamsRepository.listUserTeams()
Expand All @@ -20,4 +35,70 @@ export const teamsRouter = createTRPCRouter({

return teamsResult.data
}),

listApiKeys: keysRepositoryProcedure.query(async ({ ctx }) => {
const result = await ctx.keysRepository.listTeamApiKeys()

if (!result.ok) {
throwTRPCErrorFromRepoError(result.error)
}

return { apiKeys: result.data }
}),

createApiKey: keysRepositoryProcedure
.input(
z.object({
name: z
.string({ error: 'Name is required' })
.min(1, 'Name cannot be empty')
.max(50, 'Name cannot be longer than 50 characters')
.trim(),
})
)
.mutation(async ({ ctx, input }) => {
const { name } = input

const result = await ctx.keysRepository.createApiKey(name)

if (!result.ok) {
l.error({
key: 'create_api_key_trpc:error',
message: result.error.message,
error: result.error,
team_id: ctx.teamId,
user_id: ctx.session.user.id,
context: { name },
})

throwTRPCErrorFromRepoError(result.error)
}

return { createdApiKey: result.data }
}),

deleteApiKey: keysRepositoryProcedure
.input(
z.object({
apiKeyId: z.uuid(),
})
)
.mutation(async ({ ctx, input }) => {
const { apiKeyId } = input

const result = await ctx.keysRepository.deleteApiKey(apiKeyId)

if (!result.ok) {
l.error({
key: 'delete_api_key_trpc:error',
message: result.error.message,
error: result.error,
team_id: ctx.teamId,
user_id: ctx.session.user.id,
context: { apiKeyId },
})

throwTRPCErrorFromRepoError(result.error)
}
}),
})
49 changes: 0 additions & 49 deletions src/core/server/functions/keys/get-api-keys.ts

This file was deleted.

7 changes: 0 additions & 7 deletions src/core/server/functions/keys/types.ts

This file was deleted.

10 changes: 4 additions & 6 deletions src/features/dashboard/billing/invoices.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,14 @@ function InvoicesEmpty({ error }: InvoicesEmptyProps) {
return (
<TableEmptyState colSpan={4}>
<InvoiceIcon
className={cn('size-4', error && 'text-accent-error-highlight')}
/>
<p
className={cn(
'prose-body-highlight',
'size-4 shrink-0',
error && 'text-accent-error-highlight'
)}
>
/>
<span className={cn(error && 'text-accent-error-highlight')}>
{error ? error : 'No invoices yet'}
</p>
</span>
</TableEmptyState>
)
}
Expand Down
2 changes: 1 addition & 1 deletion src/features/dashboard/layouts/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ interface PageProps {
}

export const Page = ({ children, className }: PageProps) => (
<div className={cn('mx-auto w-full max-w-[900px]', className)}>
<div className={cn('mx-auto w-full max-w-[900px] p-3 md:p-0', className)}>
{children}
</div>
)
2 changes: 1 addition & 1 deletion src/features/dashboard/layouts/wrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export function DefaultDashboardLayout({
}) {
return (
<div className="flex-1 overflow-y-auto">
<div className="container mx-auto p-0 md:p-8 2xl:p-24 h-min w-full">
<div className="container mx-auto h-min w-full p-0 md:p-10 2xl:p-24">
<CatchErrorBoundary
classNames={{
wrapper: 'h-full w-full',
Expand Down
Loading
Loading