@@ -209,6 +212,21 @@ export default async function OrganizationDiscountsPage({
{discount.reason ?? "—"}
+
+ {discount.expiresAt ? (
+
+ {formatDate(discount.expiresAt)}
+
+ ) : (
+ "Never"
+ )}
+
{formatDate(discount.createdAt)}
diff --git a/apps/admin/src/components/discount-form.tsx b/apps/admin/src/components/discount-form.tsx
index 3124133261..3acd295b29 100644
--- a/apps/admin/src/components/discount-form.tsx
+++ b/apps/admin/src/components/discount-form.tsx
@@ -36,6 +36,7 @@ interface DiscountFormProps {
model: string | null;
discountPercent: number;
reason: string | null;
+ expiresAt: string | null;
}) => Promise<{ success: boolean; error?: string }>;
}
@@ -53,6 +54,7 @@ export function DiscountForm({
const [model, setModel] = useState
("__all__");
const [discountPercent, setDiscountPercent] = useState("");
const [reason, setReason] = useState("");
+ const [expiresAt, setExpiresAt] = useState("");
// Filter mappings by selected provider
const filteredMappings = useMemo(() => {
@@ -131,6 +133,7 @@ export function DiscountForm({
model: model === "__all__" ? null : model,
discountPercent: percent,
reason: reason || null,
+ expiresAt: expiresAt ? new Date(expiresAt).toISOString() : null,
});
setLoading(false);
@@ -141,6 +144,7 @@ export function DiscountForm({
setModel("__all__");
setDiscountPercent("");
setReason("");
+ setExpiresAt("");
router.refresh();
} else {
setError(result.error ?? "Failed to create discount");
@@ -264,6 +268,19 @@ export function DiscountForm({
/>
+
{error}
diff --git a/apps/admin/src/lib/api/v1.d.ts b/apps/admin/src/lib/api/v1.d.ts
index 963fbf67d4..11e940fd5b 100644
--- a/apps/admin/src/lib/api/v1.d.ts
+++ b/apps/admin/src/lib/api/v1.d.ts
@@ -247,6 +247,53 @@ export interface paths {
patch?: never;
trace?: never;
};
+ "/public/discounts/model/{modelId}": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ modelId: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Active global discounts for the specified model */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ discounts: {
+ id: string;
+ provider: string | null;
+ model: string | null;
+ discountPercent: string;
+ reason: string | null;
+ expiresAt: string | null;
+ createdAt: string;
+ }[];
+ };
+ };
+ };
+ };
+ };
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
"/user/me": {
parameters: {
query?: never;
@@ -3884,6 +3931,66 @@ export interface paths {
patch?: never;
trace?: never;
};
+ "/orgs/{id}/discounts": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Active discounts for the organization (org-specific and global) */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ orgDiscounts: {
+ id: string;
+ organizationId: string | null;
+ provider: string | null;
+ model: string | null;
+ discountPercent: string;
+ reason: string | null;
+ expiresAt: string | null;
+ createdAt: string;
+ updatedAt: string;
+ }[];
+ globalDiscounts: {
+ id: string;
+ organizationId: string | null;
+ provider: string | null;
+ model: string | null;
+ discountPercent: string;
+ reason: string | null;
+ expiresAt: string | null;
+ createdAt: string;
+ updatedAt: string;
+ }[];
+ };
+ };
+ };
+ };
+ };
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
"/team/{organizationId}/members": {
parameters: {
query?: never;
diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts
index ba9609b357..afd0b4d444 100644
--- a/apps/api/src/index.ts
+++ b/apps/api/src/index.ts
@@ -21,6 +21,7 @@ import { tracingMiddleware } from "./middleware/tracing.js";
import { beacon } from "./routes/beacon.js";
import { routes } from "./routes/index.js";
import { internalModels } from "./routes/internal-models.js";
+import { publicDiscounts } from "./routes/public-discounts.js";
import { referral } from "./routes/referral.js";
import { stripeRoutes } from "./stripe.js";
@@ -220,6 +221,8 @@ app.route("/", referral);
app.route("/internal", internalModels);
+app.route("/public/discounts", publicDiscounts);
+
app.doc("/json", config);
app.get("/docs", swaggerUI({ url: "./json" }));
diff --git a/apps/api/src/routes/organization.ts b/apps/api/src/routes/organization.ts
index ceccd25c37..ae90853eed 100644
--- a/apps/api/src/routes/organization.ts
+++ b/apps/api/src/routes/organization.ts
@@ -5,7 +5,7 @@ import { z } from "zod";
import { userHasOrganizationAccess } from "@/utils/authorization.js";
import { logAuditEvent } from "@llmgateway/audit";
-import { db, eq, tables } from "@llmgateway/db";
+import { and, db, desc, eq, gte, isNull, or, tables } from "@llmgateway/db";
import type { ServerTypes } from "@/vars.js";
@@ -739,4 +739,82 @@ organization.openapi(getReferralStats, async (c) => {
});
});
+const discountSchema = z.object({
+ id: z.string(),
+ organizationId: z.string().nullable(),
+ provider: z.string().nullable(),
+ model: z.string().nullable(),
+ discountPercent: z.string(),
+ reason: z.string().nullable(),
+ expiresAt: z.date().nullable(),
+ createdAt: z.date(),
+ updatedAt: z.date(),
+});
+
+const getOrgDiscounts = createRoute({
+ method: "get",
+ path: "/{id}/discounts",
+ request: {
+ params: z.object({
+ id: z.string(),
+ }),
+ },
+ responses: {
+ 200: {
+ content: {
+ "application/json": {
+ schema: z.object({
+ orgDiscounts: z.array(discountSchema).openapi({}),
+ globalDiscounts: z.array(discountSchema).openapi({}),
+ }),
+ },
+ },
+ description:
+ "Active discounts for the organization (org-specific and global)",
+ },
+ },
+});
+
+organization.openapi(getOrgDiscounts, async (c) => {
+ const user = c.get("user");
+ if (!user) {
+ throw new HTTPException(401, {
+ message: "Unauthorized",
+ });
+ }
+
+ const { id } = c.req.param();
+
+ const hasAccess = await userHasOrganizationAccess(user.id, id);
+ if (!hasAccess) {
+ throw new HTTPException(403, {
+ message: "You do not have access to this organization",
+ });
+ }
+
+ const now = new Date();
+ const notExpired = or(
+ isNull(tables.discount.expiresAt),
+ gte(tables.discount.expiresAt, now),
+ );
+
+ const [orgDiscounts, globalDiscounts] = await Promise.all([
+ db
+ .select()
+ .from(tables.discount)
+ .where(and(eq(tables.discount.organizationId, id), notExpired))
+ .orderBy(desc(tables.discount.createdAt)),
+ db
+ .select()
+ .from(tables.discount)
+ .where(and(isNull(tables.discount.organizationId), notExpired))
+ .orderBy(desc(tables.discount.createdAt)),
+ ]);
+
+ return c.json({
+ orgDiscounts,
+ globalDiscounts,
+ });
+});
+
export default organization;
diff --git a/apps/api/src/routes/public-discounts.ts b/apps/api/src/routes/public-discounts.ts
new file mode 100644
index 0000000000..650ff95260
--- /dev/null
+++ b/apps/api/src/routes/public-discounts.ts
@@ -0,0 +1,66 @@
+import { createRoute, OpenAPIHono } from "@hono/zod-openapi";
+import { z } from "zod";
+
+import { and, db, desc, eq, gte, isNull, or, tables } from "@llmgateway/db";
+
+import type { ServerTypes } from "@/vars.js";
+
+export const publicDiscounts = new OpenAPIHono
();
+
+const discountSchema = z.object({
+ id: z.string(),
+ provider: z.string().nullable(),
+ model: z.string().nullable(),
+ discountPercent: z.string(),
+ reason: z.string().nullable(),
+ expiresAt: z.date().nullable(),
+ createdAt: z.date(),
+});
+
+const getModelDiscounts = createRoute({
+ method: "get",
+ path: "/model/{modelId}",
+ request: {
+ params: z.object({
+ modelId: z.string(),
+ }),
+ },
+ responses: {
+ 200: {
+ content: {
+ "application/json": {
+ schema: z.object({
+ discounts: z.array(discountSchema).openapi({}),
+ }),
+ },
+ },
+ description: "Active global discounts for the specified model",
+ },
+ },
+});
+
+publicDiscounts.openapi(getModelDiscounts, async (c) => {
+ const { modelId } = c.req.param();
+
+ const now = new Date();
+ const notExpired = or(
+ isNull(tables.discount.expiresAt),
+ gte(tables.discount.expiresAt, now),
+ );
+
+ const discounts = await db
+ .select()
+ .from(tables.discount)
+ .where(
+ and(
+ isNull(tables.discount.organizationId),
+ or(isNull(tables.discount.model), eq(tables.discount.model, modelId)),
+ notExpired,
+ ),
+ )
+ .orderBy(desc(tables.discount.createdAt));
+
+ return c.json({
+ discounts,
+ });
+});
diff --git a/apps/code/src/lib/api/v1.d.ts b/apps/code/src/lib/api/v1.d.ts
index 963fbf67d4..11e940fd5b 100644
--- a/apps/code/src/lib/api/v1.d.ts
+++ b/apps/code/src/lib/api/v1.d.ts
@@ -247,6 +247,53 @@ export interface paths {
patch?: never;
trace?: never;
};
+ "/public/discounts/model/{modelId}": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ modelId: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Active global discounts for the specified model */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ discounts: {
+ id: string;
+ provider: string | null;
+ model: string | null;
+ discountPercent: string;
+ reason: string | null;
+ expiresAt: string | null;
+ createdAt: string;
+ }[];
+ };
+ };
+ };
+ };
+ };
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
"/user/me": {
parameters: {
query?: never;
@@ -3884,6 +3931,66 @@ export interface paths {
patch?: never;
trace?: never;
};
+ "/orgs/{id}/discounts": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Active discounts for the organization (org-specific and global) */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ orgDiscounts: {
+ id: string;
+ organizationId: string | null;
+ provider: string | null;
+ model: string | null;
+ discountPercent: string;
+ reason: string | null;
+ expiresAt: string | null;
+ createdAt: string;
+ updatedAt: string;
+ }[];
+ globalDiscounts: {
+ id: string;
+ organizationId: string | null;
+ provider: string | null;
+ model: string | null;
+ discountPercent: string;
+ reason: string | null;
+ expiresAt: string | null;
+ createdAt: string;
+ updatedAt: string;
+ }[];
+ };
+ };
+ };
+ };
+ };
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
"/team/{organizationId}/members": {
parameters: {
query?: never;
diff --git a/apps/playground/src/lib/api/v1.d.ts b/apps/playground/src/lib/api/v1.d.ts
index 963fbf67d4..11e940fd5b 100644
--- a/apps/playground/src/lib/api/v1.d.ts
+++ b/apps/playground/src/lib/api/v1.d.ts
@@ -247,6 +247,53 @@ export interface paths {
patch?: never;
trace?: never;
};
+ "/public/discounts/model/{modelId}": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ modelId: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Active global discounts for the specified model */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ discounts: {
+ id: string;
+ provider: string | null;
+ model: string | null;
+ discountPercent: string;
+ reason: string | null;
+ expiresAt: string | null;
+ createdAt: string;
+ }[];
+ };
+ };
+ };
+ };
+ };
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
"/user/me": {
parameters: {
query?: never;
@@ -3884,6 +3931,66 @@ export interface paths {
patch?: never;
trace?: never;
};
+ "/orgs/{id}/discounts": {
+ parameters: {
+ query?: never;
+ header?: never;
+ path?: never;
+ cookie?: never;
+ };
+ get: {
+ parameters: {
+ query?: never;
+ header?: never;
+ path: {
+ id: string;
+ };
+ cookie?: never;
+ };
+ requestBody?: never;
+ responses: {
+ /** @description Active discounts for the organization (org-specific and global) */
+ 200: {
+ headers: {
+ [name: string]: unknown;
+ };
+ content: {
+ "application/json": {
+ orgDiscounts: {
+ id: string;
+ organizationId: string | null;
+ provider: string | null;
+ model: string | null;
+ discountPercent: string;
+ reason: string | null;
+ expiresAt: string | null;
+ createdAt: string;
+ updatedAt: string;
+ }[];
+ globalDiscounts: {
+ id: string;
+ organizationId: string | null;
+ provider: string | null;
+ model: string | null;
+ discountPercent: string;
+ reason: string | null;
+ expiresAt: string | null;
+ createdAt: string;
+ updatedAt: string;
+ }[];
+ };
+ };
+ };
+ };
+ };
+ put?: never;
+ post?: never;
+ delete?: never;
+ options?: never;
+ head?: never;
+ patch?: never;
+ trace?: never;
+ };
"/team/{organizationId}/members": {
parameters: {
query?: never;
diff --git a/apps/ui/src/app/dashboard/[orgId]/org/discounts/discounts-client.tsx b/apps/ui/src/app/dashboard/[orgId]/org/discounts/discounts-client.tsx
new file mode 100644
index 0000000000..8fe68ae409
--- /dev/null
+++ b/apps/ui/src/app/dashboard/[orgId]/org/discounts/discounts-client.tsx
@@ -0,0 +1,217 @@
+"use client";
+
+import { Percent } from "lucide-react";
+
+import { Countdown } from "@/components/countdown";
+import { useIsMobile } from "@/hooks/use-mobile";
+import { Badge } from "@/lib/components/badge";
+import {
+ Card,
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/lib/components/card";
+import {
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeader,
+ TableRow,
+} from "@/lib/components/table";
+
+interface Discount {
+ id: string;
+ organizationId: string | null;
+ provider: string | null;
+ model: string | null;
+ discountPercent: string;
+ reason: string | null;
+ expiresAt: string | null;
+ createdAt: string;
+ updatedAt: string;
+}
+
+interface DiscountsClientProps {
+ data: {
+ orgDiscounts: Discount[];
+ globalDiscounts: Discount[];
+ };
+}
+
+function formatPercent(decimal: string): string {
+ return `${(parseFloat(decimal) * 100).toFixed(1)}%`;
+}
+
+function DiscountRow({
+ discount,
+ scope,
+}: {
+ discount: Discount;
+ scope: "org" | "global";
+}) {
+ return (
+
+
+
+ {scope === "org" ? "Org" : "Global"}
+
+
+
+ {discount.provider ? (
+ {discount.provider}
+ ) : (
+ All
+ )}
+
+
+ {discount.model ? (
+ {discount.model}
+ ) : (
+ All
+ )}
+
+
+
+ {formatPercent(discount.discountPercent)} off
+
+
+
+ {discount.expiresAt ? (
+
+ ) : (
+ Never
+ )}
+
+
+ {discount.reason ?? "—"}
+
+
+ );
+}
+
+function DiscountCard({
+ discount,
+ scope,
+}: {
+ discount: Discount;
+ scope: "org" | "global";
+}) {
+ return (
+
+
+
+
+
+ {scope === "org" ? "Org" : "Global"}
+
+
+ {formatPercent(discount.discountPercent)} off
+
+
+
+ Provider:
+ {discount.provider ? (
+ {discount.provider}
+ ) : (
+ All
+ )}
+
+
+ Model:
+ {discount.model ? (
+ {discount.model}
+ ) : (
+ All
+ )}
+
+
+ Expires:
+ {discount.expiresAt ? (
+
+ ) : (
+ Never
+ )}
+
+ {discount.reason && (
+
+ {discount.reason}
+
+ )}
+
+
+
+ );
+}
+
+export function DiscountsClient({ data }: DiscountsClientProps) {
+ const isMobile = useIsMobile();
+ const allDiscounts = [
+ ...data.orgDiscounts.map((d) => ({ ...d, scope: "org" as const })),
+ ...data.globalDiscounts.map((d) => ({ ...d, scope: "global" as const })),
+ ];
+
+ return (
+
+
+
+ Your Discounts
+
+ Active discounts applied to your organization's API usage
+
+
+
+ {allDiscounts.length === 0 ? (
+
+
+
+
+ No active discounts
+
+
+ When discounts are applied to your organization, they'll
+ appear here.
+
+
+
+ ) : isMobile ? (
+
+ {allDiscounts.map((discount) => (
+
+ ))}
+
+ ) : (
+
+
+
+
+ Scope
+ Provider
+ Model
+ Discount
+ Expires
+ Reason
+
+
+
+ {allDiscounts.map((discount) => (
+
+ ))}
+
+
+
+ )}
+
+
+
+ );
+}
diff --git a/apps/ui/src/app/dashboard/[orgId]/org/discounts/page.tsx b/apps/ui/src/app/dashboard/[orgId]/org/discounts/page.tsx
new file mode 100644
index 0000000000..ce5ce54d99
--- /dev/null
+++ b/apps/ui/src/app/dashboard/[orgId]/org/discounts/page.tsx
@@ -0,0 +1,63 @@
+import { fetchServerData } from "@/lib/server-api";
+
+import { DiscountsClient } from "./discounts-client";
+
+interface Discount {
+ id: string;
+ organizationId: string | null;
+ provider: string | null;
+ model: string | null;
+ discountPercent: string;
+ reason: string | null;
+ expiresAt: string | null;
+ createdAt: string;
+ updatedAt: string;
+}
+
+interface DiscountsData {
+ orgDiscounts: Discount[];
+ globalDiscounts: Discount[];
+}
+
+async function fetchDiscounts(orgId: string): Promise {
+ const data = await fetchServerData(
+ "GET",
+ "/orgs/{id}/discounts" as any,
+ {
+ params: {
+ path: { id: orgId },
+ },
+ },
+ );
+
+ return data ?? { orgDiscounts: [], globalDiscounts: [] };
+}
+
+export default async function OrgDiscountsPage({
+ params,
+}: {
+ params: Promise<{ orgId: string }>;
+}) {
+ const { orgId } = await params;
+
+ if (!orgId) {
+ return (
+
+
+
+
+ Your Discounts
+
+
+
+ No organization selected
+
+
+
+ );
+ }
+
+ const data = await fetchDiscounts(orgId);
+
+ return ;
+}
diff --git a/apps/ui/src/app/globals.css b/apps/ui/src/app/globals.css
index 5b60b3143f..36e5aa89a4 100644
--- a/apps/ui/src/app/globals.css
+++ b/apps/ui/src/app/globals.css
@@ -32,6 +32,7 @@
--chart-3: oklch(0.398 0.07 227.392);
--chart-4: oklch(0.828 0.189 84.429);
--chart-5: oklch(0.769 0.188 70.08);
+ --surface-elevated: oklch(0.98 0.001 286);
--radius: 0.625rem;
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.141 0.005 285.823);
@@ -68,6 +69,7 @@
--chart-3: oklch(0.769 0.188 70.08);
--chart-4: oklch(0.627 0.265 303.9);
--chart-5: oklch(0.645 0.246 16.439);
+ --surface-elevated: oklch(0.18 0.005 286);
--sidebar: oklch(0.21 0.006 285.885);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
@@ -115,6 +117,8 @@
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
+ --color-surface-elevated: var(--surface-elevated);
+ --font-family-display: var(--font-display);
--animate-marquee: marquee var(--duration) infinite linear;
--animate-marquee-vertical: marquee-vertical var(--duration) linear infinite;
@keyframes marquee {
@@ -205,3 +209,24 @@
@apply bg-background text-foreground;
}
}
+
+@utility font-display {
+ font-family: var(--font-display), var(--font-sans), system-ui, sans-serif;
+}
+
+@utility bg-noise {
+ &::before {
+ content: "";
+ position: absolute;
+ inset: 0;
+ opacity: 0.03;
+ background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
+ background-repeat: repeat;
+ background-size: 256px 256px;
+ pointer-events: none;
+ }
+}
+
+@utility bg-surface-elevated {
+ background-color: var(--surface-elevated);
+}
diff --git a/apps/ui/src/app/layout.tsx b/apps/ui/src/app/layout.tsx
index 78c3ae9a75..b1b65ac012 100644
--- a/apps/ui/src/app/layout.tsx
+++ b/apps/ui/src/app/layout.tsx
@@ -1,4 +1,4 @@
-import { Inter, Geist_Mono } from "next/font/google";
+import { Inter, Geist_Mono, Plus_Jakarta_Sans } from "next/font/google";
import { Providers } from "@/components/providers";
import { getConfig } from "@/lib/config-server";
@@ -20,6 +20,13 @@ const geistMono = Geist_Mono({
display: "swap",
});
+const plusJakarta = Plus_Jakarta_Sans({
+ variable: "--font-display",
+ subsets: ["latin"],
+ weight: ["500", "600", "700", "800"],
+ display: "swap",
+});
+
export const dynamic = "force-dynamic";
export const metadata: Metadata = {
@@ -134,7 +141,7 @@ export default function RootLayout({ children }: { children: ReactNode }) {
/>
{children}
diff --git a/apps/ui/src/app/models/[name]/page.tsx b/apps/ui/src/app/models/[name]/page.tsx
index a961b938a0..ab7de37214 100644
--- a/apps/ui/src/app/models/[name]/page.tsx
+++ b/apps/ui/src/app/models/[name]/page.tsx
@@ -15,6 +15,7 @@ import { notFound } from "next/navigation";
import Footer from "@/components/landing/footer";
import { Navbar } from "@/components/landing/navbar";
import { CopyModelName } from "@/components/models/copy-model-name";
+import { GlobalDiscountBanner } from "@/components/models/global-discount-banner";
import { ModelProviderCard } from "@/components/models/model-provider-card";
import { ModelStatusBadgeAuto } from "@/components/models/model-status-badge-auto";
import { ProviderTabs } from "@/components/models/provider-tabs";
@@ -397,6 +398,13 @@ export default async function ModelPage({ params }: PageProps) {