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
12 changes: 12 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,15 @@ examples/target
.env
.env.local
.env.production

# Yarn
.pnp.*
.yarn/*

# Better Auth
better-auth_migrations

# SQLite databases
*.db
*.db-journal
sqlite.db
12 changes: 12 additions & 0 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
enableGlobalCache: true

enableScripts: true

enableTelemetry: false

packageExtensions:
better-sqlite3@*:
dependencies:
bindings: ^1.5.0

pnpEnableInlining: false
4 changes: 4 additions & 0 deletions app/api/auth/[...all]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { auth } from "@/lib/auth";
import { toNextJsHandler } from "better-auth/next-js";

export const { GET, POST } = toNextJsHandler(auth);
2 changes: 1 addition & 1 deletion app/blog/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export default async function Page({
return (
<>
<Navigation />
<div className="blog-container relative w-full max-w-screen-2xl mx-auto px-4 sm:px-8 lg:px-16 flex items-start pt-32 sm:pt-40 min-h-screen">
<div className="blog-container relative w-full max-w-(--breakpoint-2xl) mx-auto px-4 sm:px-8 lg:px-16 flex items-start pt-32 sm:pt-40 min-h-screen">
<aside className="hidden md:flex shrink-0 sticky top-32 sm:top-40 mb-16 flex-col w-64">
<div className="text-black dark:text-white uppercase text-sm font-semibold h-8 flex items-end mb-9">
{post.authors.length > 1 ? "Authors" : "Author"}
Expand Down
4 changes: 2 additions & 2 deletions app/blog/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export default function Blog() {
return (
<>
<Navigation />
<main className="docs-container relative w-full max-w-screen-2xl mx-auto px-4 sm:px-8 lg:px-16 pb-24 pt-32 sm:pt-40 min-h-screen">
<main className="docs-container relative w-full max-w-(--breakpoint-2xl) mx-auto px-4 sm:px-8 lg:px-16 pb-24 pt-32 sm:pt-40 min-h-screen">
<h2 className="font-display text-2xl sm:text-3xl lg:text-4xl text-black dark:text-white">
{content.heading}
</h2>
Expand All @@ -33,7 +33,7 @@ export default function Blog() {
<Link
href={urlPath}
key={index}
className="bg-gradient-to-br from-zinc-50 to-zinc-100 dark:from-zinc-800 dark:to-zinc-900 rounded-sm p-6 generic-hover"
className="bg-linear-to-br from-zinc-50 to-zinc-100 dark:from-zinc-800 dark:to-zinc-900 rounded-sm p-6 generic-hover"
>
<div className="flex items-start justify-between">
<h3 className="font-display text-2xl text-black dark:text-white">
Expand Down
15 changes: 15 additions & 0 deletions app/dashboard/apps/[appId]/api-keys/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { getAppById } from "@/lib/dashboard/mock-data";
import { ApiKeyPage } from "@/components/dashboard/app-api-key-content";

export default async function Page({
params,
}: {
params: Promise<{ appId: string }>;
}) {
const { appId } = await params;
const app = getAppById(appId);

if (!app) return null;

return <ApiKeyPage apiKey={app.apiKey} />;
}
23 changes: 23 additions & 0 deletions app/dashboard/apps/[appId]/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { notFound } from "next/navigation";

import { AppHeader } from "@/components/dashboard/app-header";
import { getAppById } from "@/lib/dashboard/mock-data";

export default async function AppDetailLayout({
children,
params,
}: {
children: React.ReactNode;
params: Promise<{ appId: string }>;
}) {
const { appId } = await params;
const app = getAppById(appId);
if (!app) notFound();

return (
<div className="space-y-6">
<AppHeader app={app} />
{children}
</div>
);
}
130 changes: 130 additions & 0 deletions app/dashboard/apps/[appId]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import { format } from "date-fns";
import {
ActivityIcon,
CalendarIcon,
CreditCardIcon,
KeyIcon,
} from "lucide-react";

import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { getAppById, getSubscriptionByAppId } from "@/lib/dashboard/mock-data";
import { cn } from "@/lib/utils";

export default async function Page({
params,
}: {
params: Promise<{ appId: string }>;
}) {
const { appId } = await params;

const app = getAppById(appId);
const subscription = getSubscriptionByAppId(appId);

if (!app) return null;

const maskedKey = `${app.apiKey.slice(0, 12)}${"•".repeat(10)}`;

const statusColor = {
active: "bg-brand",
inactive: "bg-muted-foreground",
pending: "bg-amber-500",
}[app.status];

return (
<div className="space-y-6">
<Card size="sm">
<CardHeader>
<CardTitle>General</CardTitle>
</CardHeader>

<CardContent className="divide-y">
<div className="flex items-center justify-between py-3">
<div className="flex items-center gap-2 text-muted-foreground">
<ActivityIcon className="size-4" />
<span>Status</span>
</div>

<div className="flex items-center gap-2">
<span className={cn("size-2 rounded-full", statusColor)} />
<span className="capitalize font-medium">{app.status}</span>
</div>
</div>

<div className="flex items-center justify-between py-3">
<div className="flex items-center gap-2 text-muted-foreground">
<CreditCardIcon className="size-4" />
<span>Plan</span>
</div>

<Badge>{app.plan}</Badge>
</div>

<div className="flex items-center justify-between py-3">
<div className="flex items-center gap-2 text-muted-foreground">
<CalendarIcon className="size-4" />
<span>Created</span>
</div>

<span className="font-medium">
{format(new Date(app.createdAt), "MMM d, yyyy")}
</span>
</div>

<div className="flex items-center justify-between py-3">
<div className="flex items-center gap-2 text-muted-foreground">
<KeyIcon className="size-4" />
<span>API Key</span>
</div>

<code className="rounded-md bg-muted px-2 py-1 font-mono text-xs">
{maskedKey}
</code>
</div>
</CardContent>
</Card>

{subscription && (
<Card size="sm">
<CardHeader>
<CardTitle>Billing</CardTitle>
</CardHeader>

<CardContent className="divide-y">
<div className="flex items-center justify-between py-3">
<span className="text-muted-foreground">Current Plan</span>

<span className="font-medium">
{subscription.currency === "USD" ? "$" : ""}
{(subscription.amount / 100).toFixed(2)}
<span className="text-muted-foreground">
/{subscription.interval}
</span>
</span>
</div>

<div className="flex items-center justify-between py-3">
<span className="text-muted-foreground">Next Billing</span>

<span className="font-medium">
{subscription.nextBillingDate}
</span>
</div>

<div className="flex items-center justify-between py-3">
<span className="text-muted-foreground">Subscription Status</span>

<Badge
variant={
subscription.status === "active" ? "success" : "secondary"
}
>
{subscription.status}
</Badge>
</div>
</CardContent>
</Card>
)}
</div>
);
}
15 changes: 15 additions & 0 deletions app/dashboard/apps/[appId]/settings/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { getAppById } from "@/lib/dashboard/mock-data";
import { AppSettingsContent } from "@/components/dashboard/app-settings-content";

export default async function Page({
params,
}: {
params: Promise<{ appId: string }>;
}) {
const { appId } = await params;
const app = getAppById(appId);

if (!app) return null;

return <AppSettingsContent app={app} />;
}
Loading
Loading