Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
419 changes: 30 additions & 389 deletions frontend/src/components/AccountMenu.tsx

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions frontend/src/components/apikeys/ApiCreditsSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,8 @@ export function ApiCreditsSection({ showSuccessMessage = false }: ApiCreditsSect
} else {
// For web or desktop, use regular URLs with query params
const baseUrl = window.location.origin;
successUrl = `${baseUrl}/?credits_success=true`;
cancelUrl = method === "stripe" ? `${baseUrl}/` : undefined;
successUrl = `${baseUrl}/settings?tab=api&credits_success=true`;
cancelUrl = method === "stripe" ? `${baseUrl}/settings?tab=api` : undefined;
}

if (method === "stripe") {
Expand Down
99 changes: 55 additions & 44 deletions frontend/src/components/apikeys/ApiKeyDashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useState } from "react";
import { DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import {
Expand Down Expand Up @@ -33,6 +32,21 @@ interface ApiKeyDashboardProps {
showCreditSuccessMessage?: boolean;
}

function DashboardHeader({
title,
description
}: {
title: React.ReactNode;
description?: React.ReactNode;
}) {
return (
<div className="space-y-1">
<h2 className="text-base font-semibold leading-none tracking-tight">{title}</h2>
{description ? <div className="text-sm text-muted-foreground">{description}</div> : null}
</div>
);
}

export function ApiKeyDashboard({ showCreditSuccessMessage = false }: ApiKeyDashboardProps) {
const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false);
const isTauriDesktopPlatform = isTauriDesktop();
Expand Down Expand Up @@ -91,46 +105,41 @@ export function ApiKeyDashboard({ showCreditSuccessMessage = false }: ApiKeyDash
// Show loading state if billing status or API keys are loading
if (isBillingLoading || isLoading) {
return (
<>
<DialogHeader>
<DialogTitle className="text-base">API Management</DialogTitle>
<DialogDescription>Loading...</DialogDescription>
</DialogHeader>
<div className="space-y-4">
<DashboardHeader title="API Management" description="Loading..." />
<div className="flex items-center justify-center py-8">
<Loader2 className="h-6 w-6 animate-spin" />
</div>
</>
</div>
);
}

if (error) {
return (
<>
<DialogHeader>
<DialogTitle className="text-base">API Management</DialogTitle>
<DialogDescription className="text-destructive">
Failed to load API keys. Please try again.
</DialogDescription>
</DialogHeader>
</>
<DashboardHeader
title="API Management"
description={
<span className="text-destructive">Failed to load API keys. Please try again.</span>
}
/>
);
}

// Show upgrade prompt for users without API access (Free and Starter plans)
if (!hasApiAccess) {
return (
<>
<DialogHeader>
<DialogTitle className="text-base flex items-center gap-2">
<Sparkles className="h-5 w-5 text-amber-500" />
Unlock API Access
</DialogTitle>
<DialogDescription>
Upgrade to a paid plan to access powerful API features
</DialogDescription>
</DialogHeader>
<div className="space-y-6">
<DashboardHeader
title={
<span className="flex items-center gap-2">
<Sparkles className="h-5 w-5 text-amber-500" />
Unlock API Access
</span>
}
description="Upgrade to a paid plan to access powerful API features"
/>

<div className="mt-6 space-y-4">
<div className="space-y-4">
<Card className="p-6 bg-gradient-to-br from-amber-500/10 to-orange-500/10 border-amber-500/20">
<div className="space-y-4">
<div className="flex items-center gap-3">
Expand Down Expand Up @@ -187,28 +196,30 @@ export function ApiKeyDashboard({ showCreditSuccessMessage = false }: ApiKeyDash
</p>
</div>
</div>
</>
</div>
);
}

return (
<>
<DialogHeader>
<DialogTitle className="text-base">API Access</DialogTitle>
<DialogDescription>
Manage API keys and configure access to Maple services.{" "}
<a
href="https://blog.trymaple.ai/maple-proxy-documentation/"
target="_blank"
rel="noopener noreferrer"
className="text-primary underline hover:no-underline"
>
Read more
</a>
</DialogDescription>
</DialogHeader>
<div className="space-y-4">
<DashboardHeader
title="API Access"
description={
<span>
Manage API keys and configure access to Maple services.{" "}
<a
href="https://blog.trymaple.ai/maple-proxy-documentation/"
target="_blank"
rel="noopener noreferrer"
className="underline hover:no-underline"
>
Read more
</a>
</span>
}
/>

<Tabs defaultValue="credits" className="mt-4">
<Tabs defaultValue="credits">
<TabsList
className={`grid w-full ${isTauriDesktopPlatform ? "grid-cols-3" : "grid-cols-2"}`}
>
Expand Down Expand Up @@ -280,6 +291,6 @@ export function ApiKeyDashboard({ showCreditSuccessMessage = false }: ApiKeyDash
onOpenChange={setIsCreateDialogOpen}
onKeyCreated={handleKeyCreated}
/>
</>
</div>
);
}
33 changes: 18 additions & 15 deletions frontend/src/components/team/TeamDashboard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useState } from "react";
import { DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Input } from "@/components/ui/input";
Expand All @@ -14,6 +13,21 @@ interface TeamDashboardProps {
teamStatus?: TeamStatus;
}

function DashboardHeader({
title,
description
}: {
title: React.ReactNode;
description?: React.ReactNode;
}) {
return (
<div className="space-y-1">
<h2 className="text-base font-semibold leading-none tracking-tight">{title}</h2>
{description ? <div className="text-sm text-muted-foreground">{description}</div> : null}
</div>
);
}

export function TeamDashboard({ teamStatus }: TeamDashboardProps) {
const [isInviteDialogOpen, setIsInviteDialogOpen] = useState(false);
const [isEditingName, setIsEditingName] = useState(false);
Expand All @@ -23,14 +37,7 @@ export function TeamDashboard({ teamStatus }: TeamDashboardProps) {
const queryClient = useQueryClient();

if (!teamStatus) {
return (
<>
<DialogHeader>
<DialogTitle>Team Dashboard</DialogTitle>
<DialogDescription>Loading team information...</DialogDescription>
</DialogHeader>
</>
);
return <DashboardHeader title="Team Dashboard" description="Loading team information..." />;
}

const seatsUsed = teamStatus.seats_used || 0;
Expand Down Expand Up @@ -94,9 +101,7 @@ export function TeamDashboard({ teamStatus }: TeamDashboardProps) {
if (!isAdmin) {
return (
<>
<DialogHeader>
<DialogTitle className="text-base">Team Information</DialogTitle>
</DialogHeader>
<DashboardHeader title="Team Information" />

<div className="mt-3 space-y-3 overflow-hidden">
{/* Compact team info */}
Expand Down Expand Up @@ -132,9 +137,7 @@ export function TeamDashboard({ teamStatus }: TeamDashboardProps) {
// Full admin view
return (
<>
<DialogHeader>
<DialogTitle className="text-base">Team Dashboard</DialogTitle>
</DialogHeader>
<DashboardHeader title="Team Dashboard" />

<div className="mt-3 space-y-3 overflow-hidden">
{/* Seat limit exceeded warning */}
Expand Down
27 changes: 27 additions & 0 deletions frontend/src/routeTree.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { Route as AuthImport } from './routes/_auth'
import { Route as IndexImport } from './routes/index'
import { Route as VerifyCodeImport } from './routes/verify.$code'
import { Route as PasswordResetConfirmImport } from './routes/password-reset.confirm'
import { Route as AuthSettingsImport } from './routes/_auth.settings'
import { Route as TeamInviteInviteIdImport } from './routes/team.invite.$inviteId'
import { Route as AuthProviderCallbackImport } from './routes/auth.$provider.callback'
import { Route as AuthChatChatIdImport } from './routes/_auth.chat.$chatId'
Expand Down Expand Up @@ -128,6 +129,12 @@ const PasswordResetConfirmRoute = PasswordResetConfirmImport.update({
getParentRoute: () => PasswordResetRoute,
} as any)

const AuthSettingsRoute = AuthSettingsImport.update({
id: '/settings',
path: '/settings',
getParentRoute: () => AuthRoute,
} as any)

const TeamInviteInviteIdRoute = TeamInviteInviteIdImport.update({
id: '/team/invite/$inviteId',
path: '/team/invite/$inviteId',
Expand Down Expand Up @@ -248,6 +255,13 @@ declare module '@tanstack/react-router' {
preLoaderRoute: typeof TeamsImport
parentRoute: typeof rootRoute
}
'/_auth/settings': {
id: '/_auth/settings'
path: '/settings'
fullPath: '/settings'
preLoaderRoute: typeof AuthSettingsImport
parentRoute: typeof AuthImport
}
'/password-reset/confirm': {
id: '/password-reset/confirm'
path: '/confirm'
Expand Down Expand Up @@ -289,10 +303,12 @@ declare module '@tanstack/react-router' {
// Create and export the route tree

interface AuthRouteChildren {
AuthSettingsRoute: typeof AuthSettingsRoute
AuthChatChatIdRoute: typeof AuthChatChatIdRoute
}

const AuthRouteChildren: AuthRouteChildren = {
AuthSettingsRoute: AuthSettingsRoute,
AuthChatChatIdRoute: AuthChatChatIdRoute,
}

Expand Down Expand Up @@ -325,6 +341,7 @@ export interface FileRoutesByFullPath {
'/redeem': typeof RedeemRoute
'/signup': typeof SignupRoute
'/teams': typeof TeamsRoute
'/settings': typeof AuthSettingsRoute
'/password-reset/confirm': typeof PasswordResetConfirmRoute
'/verify/$code': typeof VerifyCodeRoute
'/chat/$chatId': typeof AuthChatChatIdRoute
Expand All @@ -347,6 +364,7 @@ export interface FileRoutesByTo {
'/redeem': typeof RedeemRoute
'/signup': typeof SignupRoute
'/teams': typeof TeamsRoute
'/settings': typeof AuthSettingsRoute
'/password-reset/confirm': typeof PasswordResetConfirmRoute
'/verify/$code': typeof VerifyCodeRoute
'/chat/$chatId': typeof AuthChatChatIdRoute
Expand All @@ -370,6 +388,7 @@ export interface FileRoutesById {
'/redeem': typeof RedeemRoute
'/signup': typeof SignupRoute
'/teams': typeof TeamsRoute
'/_auth/settings': typeof AuthSettingsRoute
'/password-reset/confirm': typeof PasswordResetConfirmRoute
'/verify/$code': typeof VerifyCodeRoute
'/_auth/chat/$chatId': typeof AuthChatChatIdRoute
Expand All @@ -394,6 +413,7 @@ export interface FileRouteTypes {
| '/redeem'
| '/signup'
| '/teams'
| '/settings'
| '/password-reset/confirm'
| '/verify/$code'
| '/chat/$chatId'
Expand All @@ -415,6 +435,7 @@ export interface FileRouteTypes {
| '/redeem'
| '/signup'
| '/teams'
| '/settings'
| '/password-reset/confirm'
| '/verify/$code'
| '/chat/$chatId'
Expand All @@ -436,6 +457,7 @@ export interface FileRouteTypes {
| '/redeem'
| '/signup'
| '/teams'
| '/_auth/settings'
| '/password-reset/confirm'
| '/verify/$code'
| '/_auth/chat/$chatId'
Expand Down Expand Up @@ -519,6 +541,7 @@ export const routeTree = rootRoute
"/_auth": {
"filePath": "_auth.tsx",
"children": [
"/_auth/settings",
"/_auth/chat/$chatId"
]
},
Expand Down Expand Up @@ -561,6 +584,10 @@ export const routeTree = rootRoute
"/teams": {
"filePath": "teams.tsx"
},
"/_auth/settings": {
"filePath": "_auth.settings.tsx",
"parent": "/_auth"
},
"/password-reset/confirm": {
"filePath": "password-reset.confirm.tsx",
"parent": "/password-reset"
Expand Down
Loading
Loading