-
Notifications
You must be signed in to change notification settings - Fork 0
feat(dashboard): add LaunchPix API platform dashboard #39
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
Open
Abdulmuiz44
wants to merge
1
commit into
main
Choose a base branch
from
feat/launchpix-api-platform-dashboard
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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,26 @@ | ||
| import Link from "next/link"; | ||
| import { ApiPageHeader } from "@/components/api-dashboard/api-page-header"; | ||
| import { EmptyState } from "@/components/api-dashboard/empty-state"; | ||
|
|
||
| export default function ApiAppsPage() { | ||
| return ( | ||
| <div> | ||
| <ApiPageHeader | ||
| title="Apps" | ||
| subtitle="Connect LaunchPix to agents, apps, and launch workflows." | ||
| /> | ||
| <EmptyState | ||
| title="No connected apps yet" | ||
| description="Register apps to scope API keys and monitor usage by integration." | ||
| action={ | ||
| <Link | ||
| href="/docs/api" | ||
| className="inline-flex rounded-lg bg-[#f5f5f5] px-4 py-2 text-sm font-medium text-[#050505] hover:opacity-90" | ||
| > | ||
| View documentation | ||
| </Link> | ||
| } | ||
| /> | ||
| </div> | ||
| ); | ||
| } |
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,11 @@ | ||
| import { ApiPageHeader } from "@/components/api-dashboard/api-page-header"; | ||
| import { BillingTabs } from "@/components/api-dashboard/billing-tabs"; | ||
|
|
||
| export default function ApiBillingPage() { | ||
| return ( | ||
| <div> | ||
| <ApiPageHeader title="Billing" /> | ||
| <BillingTabs /> | ||
| </div> | ||
| ); | ||
| } |
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,17 @@ | ||
| import { ApiKeysTable } from "@/components/api-dashboard/api-keys-table"; | ||
| import { ApiPageHeader } from "@/components/api-dashboard/api-page-header"; | ||
| import { CreateKeyButton } from "@/components/api-dashboard/create-key-button"; | ||
| import { MOCK_API_KEYS } from "@/lib/api-dashboard/mock-data"; | ||
|
|
||
| export default function ApiKeysPage() { | ||
| return ( | ||
| <div> | ||
| <ApiPageHeader | ||
| title="API Keys" | ||
| subtitle="API keys are used to authenticate requests to the LaunchPix API." | ||
| action={<CreateKeyButton />} | ||
| /> | ||
| <ApiKeysTable keys={MOCK_API_KEYS} /> | ||
| </div> | ||
| ); | ||
| } |
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,21 @@ | ||
| import type { ReactNode } from "react"; | ||
| import { ApiDashboardShell } from "@/components/api-dashboard/api-dashboard-shell"; | ||
| import { MOCK_API_KEYS, MOCK_SETUP } from "@/lib/api-dashboard/mock-data"; | ||
| import { requireUser } from "@/lib/supabase/auth"; | ||
|
|
||
| export const dynamic = "force-dynamic"; | ||
|
|
||
| export default async function ApiPlatformLayout({ children }: { children: ReactNode }) { | ||
| const { user } = await requireUser(); | ||
|
|
||
| const setup = { | ||
| hasPaymentMethod: MOCK_SETUP.hasPaymentMethod, | ||
| hasApiKey: MOCK_SETUP.hasApiKey || MOCK_API_KEYS.length > 0 | ||
| }; | ||
|
|
||
| return ( | ||
| <ApiDashboardShell setup={setup} userEmail={user.email ?? "developer@launchpix.dev"}> | ||
| {children} | ||
| </ApiDashboardShell> | ||
| ); | ||
| } |
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,95 @@ | ||
| import Link from "next/link"; | ||
| import { BookOpen, Bot, KeyRound, LineChart } from "lucide-react"; | ||
| import { ApiDocsSnippet } from "@/components/api-dashboard/api-docs-snippet"; | ||
| import { GetStartedCard } from "@/components/api-dashboard/get-started-card"; | ||
| import { MetricCard } from "@/components/api-dashboard/metric-card"; | ||
| import { MOCK_METRICS } from "@/lib/api-dashboard/mock-data"; | ||
| import { getDisplayName, getTimeGreeting } from "@/lib/api-dashboard/greeting"; | ||
| import { requireUser } from "@/lib/supabase/auth"; | ||
| import { getAccessContext } from "@/lib/services/access/permissions"; | ||
|
|
||
| export default async function ApiPlatformHomePage() { | ||
| const { user } = await requireUser(); | ||
| const { subscription } = await getAccessContext(user.id); | ||
| const greeting = getTimeGreeting(); | ||
| const name = getDisplayName(user.name, user.email); | ||
| const creditsDisplay = | ||
| subscription.credits_remaining > 0 | ||
| ? `${subscription.credits_remaining} credits` | ||
| : MOCK_METRICS.availableCreditsUsd; | ||
|
|
||
| return ( | ||
| <div className="space-y-8"> | ||
| <header> | ||
| <h1 className="text-2xl font-semibold tracking-tight text-[#f5f5f5] sm:text-[1.65rem]"> | ||
| {greeting} | ||
| {name ? `, ${name}` : ""} | ||
| </h1> | ||
| <p className="mt-2 max-w-2xl text-sm leading-6 text-[#8a8a8a]"> | ||
| Generate launch images, banners, social creatives, and product launch packs from one API. | ||
| </p> | ||
| </header> | ||
|
|
||
| <section className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4"> | ||
| <MetricCard label="Available credits" value={creditsDisplay} hint="Track generation usage and credits." /> | ||
| <MetricCard label="Total spent" value={MOCK_METRICS.totalSpentUsd} /> | ||
| <MetricCard label="Total issued" value={MOCK_METRICS.totalIssuedUsd} /> | ||
| <MetricCard | ||
| label="Active API keys" | ||
| value={String(MOCK_METRICS.activeApiKeys)} | ||
| hint={MOCK_METRICS.activeApiKeys === 0 ? "0 active keys" : undefined} | ||
| /> | ||
| </section> | ||
|
|
||
| <section> | ||
| <h2 className="text-sm font-medium text-[#f5f5f5]">Get started</h2> | ||
| <div className="mt-4 grid gap-4 sm:grid-cols-3"> | ||
| <GetStartedCard | ||
| title="Create an API key" | ||
| description="API keys authenticate requests to LaunchPix." | ||
| href="/dashboard/api/keys" | ||
| icon={<KeyRound className="size-4" />} | ||
| /> | ||
| <GetStartedCard | ||
| title="View docs" | ||
| description="Launch-ready visuals through one API." | ||
| href="/docs/api" | ||
| icon={<BookOpen className="size-4" />} | ||
| /> | ||
| <GetStartedCard | ||
| title="Track usage" | ||
| description="Monitor generation spend and credit consumption." | ||
| href="/dashboard/api/usage" | ||
| icon={<LineChart className="size-4" />} | ||
| /> | ||
| </div> | ||
| </section> | ||
|
|
||
| <section> | ||
| <h2 className="text-sm font-medium text-[#f5f5f5]">Build with agents</h2> | ||
| <div className="mt-4 grid gap-4 lg:grid-cols-2"> | ||
| <div className="api-dashboard-card rounded-[14px] border border-[rgba(255,255,255,0.08)] bg-[#111111] p-5 sm:p-6"> | ||
| <div className="flex items-start gap-3"> | ||
| <span className="flex size-9 shrink-0 items-center justify-center rounded-lg border border-[rgba(255,255,255,0.08)] bg-[#0d0d0d]"> | ||
| <Bot className="size-4 text-[#a1a1a1]" /> | ||
| </span> | ||
| <div> | ||
| <p className="text-sm font-medium text-[#f5f5f5]">MCP server</p> | ||
| <p className="mt-1.5 text-sm leading-6 text-[#8a8a8a]"> | ||
| Connect LaunchPix to your preferred agent workflow for automated launch asset generation. | ||
| </p> | ||
| <Link | ||
| href="/docs/api" | ||
| className="mt-3 inline-block text-sm text-[#a1a1a1] underline-offset-2 hover:text-[#f5f5f5] hover:underline" | ||
| > | ||
| Read integration guide | ||
| </Link> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| <ApiDocsSnippet /> | ||
| </div> | ||
| </section> | ||
| </div> | ||
| ); | ||
| } | ||
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,14 @@ | ||
| import { ApiPageHeader } from "@/components/api-dashboard/api-page-header"; | ||
| import { UsageChartPlaceholder } from "@/components/api-dashboard/usage-chart-placeholder"; | ||
|
|
||
| export default function ApiUsagePage() { | ||
| return ( | ||
| <div> | ||
| <ApiPageHeader | ||
| title="Usage" | ||
| subtitle="View and track your API usage over time." | ||
| /> | ||
| <UsageChartPlaceholder /> | ||
| </div> | ||
| ); | ||
| } |
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
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
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,48 @@ | ||
| import Link from "next/link"; | ||
| import { AlertTriangle } from "lucide-react"; | ||
|
|
||
| export function ApiAlertBanner({ | ||
| hasPaymentMethod, | ||
| hasApiKey | ||
| }: { | ||
| hasPaymentMethod: boolean; | ||
| hasApiKey: boolean; | ||
| }) { | ||
| const messages: string[] = []; | ||
| if (!hasPaymentMethod) messages.push("Add a payment method to enable API billing and credit refills."); | ||
| if (!hasApiKey) messages.push("Create an API key to authenticate LaunchPix API requests."); | ||
|
|
||
| return ( | ||
| <div | ||
| role="status" | ||
| className="border-b border-[rgba(220,80,80,0.25)] bg-[#3b0d0d] px-4 py-3 text-sm text-[#e8a0a0] sm:px-6" | ||
| > | ||
| <div className="mx-auto flex max-w-[960px] flex-wrap items-start gap-2"> | ||
| <AlertTriangle className="mt-0.5 size-4 shrink-0 text-[#c96a6a]" aria-hidden /> | ||
| <div className="min-w-0 flex-1 space-y-1"> | ||
| {messages.map((message) => ( | ||
| <p key={message}>{message}</p> | ||
| ))} | ||
| </div> | ||
| <div className="flex flex-wrap gap-2"> | ||
| {!hasApiKey ? ( | ||
| <Link | ||
| href="/dashboard/api/keys" | ||
| className="rounded-lg border border-[rgba(255,255,255,0.12)] bg-[rgba(255,255,255,0.06)] px-3 py-1.5 text-xs font-medium text-[#f5f5f5] hover:bg-[rgba(255,255,255,0.1)]" | ||
| > | ||
| Create API key | ||
| </Link> | ||
| ) : null} | ||
| {!hasPaymentMethod ? ( | ||
| <Link | ||
| href="/dashboard/api/billing" | ||
| className="rounded-lg border border-[rgba(255,255,255,0.12)] bg-[rgba(255,255,255,0.06)] px-3 py-1.5 text-xs font-medium text-[#f5f5f5] hover:bg-[rgba(255,255,255,0.1)]" | ||
| > | ||
| Add payment method | ||
| </Link> | ||
| ) : null} | ||
| </div> | ||
| </div> | ||
| </div> | ||
| ); | ||
| } |
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,28 @@ | ||
| import type { ReactNode } from "react"; | ||
| import { ApiAlertBanner } from "@/components/api-dashboard/api-alert-banner"; | ||
| import { ApiSidebar } from "@/components/api-dashboard/api-sidebar"; | ||
| import type { ApiSetupState } from "@/components/api-dashboard/types"; | ||
|
|
||
| export function ApiDashboardShell({ | ||
| children, | ||
| setup, | ||
| userEmail | ||
| }: { | ||
| children: ReactNode; | ||
| setup: ApiSetupState; | ||
| userEmail: string; | ||
| }) { | ||
| const showAlert = !setup.hasPaymentMethod || !setup.hasApiKey; | ||
|
|
||
| return ( | ||
| <div className="api-dashboard min-h-screen bg-[#050505] text-[#f5f5f5]"> | ||
| <ApiSidebar userEmail={userEmail} /> | ||
| <div className="api-dashboard-main flex min-h-screen min-w-0 flex-1 flex-col lg:pl-[240px]"> | ||
| {showAlert ? <ApiAlertBanner hasPaymentMethod={setup.hasPaymentMethod} hasApiKey={setup.hasApiKey} /> : null} | ||
| <main className="flex-1 px-4 py-6 sm:px-6 sm:py-8"> | ||
| <div className="mx-auto w-full max-w-[960px]">{children}</div> | ||
| </main> | ||
| </div> | ||
| </div> | ||
| ); | ||
| } |
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,21 @@ | ||
| export function ApiDocsSnippet() { | ||
| return ( | ||
| <div className="api-dashboard-card rounded-[14px] border border-[rgba(255,255,255,0.08)] bg-[#111111] p-5 sm:p-6"> | ||
| <p className="text-xs font-medium uppercase tracking-[0.14em] text-[#8a8a8a]">Quick example</p> | ||
| <pre className="mt-4 overflow-x-auto rounded-lg border border-[rgba(255,255,255,0.08)] bg-[#0a0a0a] p-4 font-mono text-xs leading-6 text-[#d4d4d4]"> | ||
| <code>{`POST /v1/launch-pack | ||
|
|
||
| { | ||
| "productName": "TeraAI", | ||
| "description": "AI learning companion for students and builders", | ||
| "launchGoal": "announce product launch", | ||
| "style": "clean startup launch graphic" | ||
| }`}</code> | ||
| </pre> | ||
| <p className="mt-3 text-xs leading-5 text-[#8a8a8a]"> | ||
| Docs-only example. Production routes use{" "} | ||
| <span className="font-mono text-[#a1a1a1]">/api/v1/projects</span> and generation endpoints today. | ||
| </p> | ||
| </div> | ||
| ); | ||
| } |
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,48 @@ | ||
| import type { ApiKeyRow } from "@/lib/api-dashboard/mock-data"; | ||
| import { EmptyState } from "@/components/api-dashboard/empty-state"; | ||
|
|
||
| export function ApiKeysTable({ keys }: { keys: ApiKeyRow[] }) { | ||
| if (!keys.length) { | ||
| return ( | ||
| <EmptyState | ||
| title="No API key yet" | ||
| description="Create a key to authenticate requests to LaunchPix. Keys are shown partially for security." | ||
| /> | ||
| ); | ||
| } | ||
|
|
||
| return ( | ||
| <div className="api-dashboard-card overflow-hidden rounded-[14px] border border-[rgba(255,255,255,0.08)] bg-[#111111]"> | ||
| <div className="overflow-x-auto"> | ||
| <table className="w-full min-w-[480px] text-left text-sm"> | ||
| <thead> | ||
| <tr className="border-b border-[rgba(255,255,255,0.08)] text-xs text-[#8a8a8a]"> | ||
| <th className="px-5 py-3 font-medium">Partial key</th> | ||
| <th className="px-5 py-3 font-medium">Date created</th> | ||
| <th className="px-5 py-3 font-medium">Status</th> | ||
| </tr> | ||
| </thead> | ||
| <tbody> | ||
| {keys.map((key) => ( | ||
| <tr key={key.id} className="border-b border-[rgba(255,255,255,0.06)] last:border-0"> | ||
| <td className="px-5 py-4 font-mono text-[#f5f5f5]">{key.partialKey}</td> | ||
| <td className="px-5 py-4 text-[#a1a1a1]">{key.createdAt}</td> | ||
| <td className="px-5 py-4"> | ||
| <span | ||
| className={ | ||
| key.status === "active" | ||
| ? "text-[#9ed49e]" | ||
| : "text-[#8a8a8a]" | ||
| } | ||
| > | ||
| {key.status === "active" ? "Active" : "Revoked"} | ||
| </span> | ||
| </td> | ||
| </tr> | ||
| ))} | ||
| </tbody> | ||
| </table> | ||
| </div> | ||
| </div> | ||
| ); | ||
| } |
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.
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.
When a user has exhausted their balance (
subscription.credits_remaining === 0), this fallback makes the “Available credits” card display"$0.00"instead of"0 credits", mixing currency with the credit balance while the rest of the app treatscredits_remainingas the source of truth. Use the subscription value for zero as well so the dashboard accurately reports the user's credit state.Useful? React with 👍 / 👎.