diff --git a/app/(auth)/layout.tsx b/app/(auth)/layout.tsx
index 6a4915609..fa81dce81 100644
--- a/app/(auth)/layout.tsx
+++ b/app/(auth)/layout.tsx
@@ -1,4 +1,4 @@
-import PageIllustration from "@/components/page-illustration";
+import PageIllustration from "@/components/ui/page-illustration";
export default function AuthLayout({
children,
diff --git a/app/(default)/layout.tsx b/app/(default)/layout.tsx
index 67902dc12..c2155be49 100644
--- a/app/(default)/layout.tsx
+++ b/app/(default)/layout.tsx
@@ -5,7 +5,8 @@ import { useEffect } from "react";
import AOS from "aos";
import "aos/dist/aos.css";
-import PageIllustration from "@/components/page-illustration";
+import PageIllustration from "@/components/ui/page-illustration";
+import Footer from "@/components/footer";
export default function DefaultLayout({
children,
@@ -27,6 +28,7 @@ export default function DefaultLayout({
{children}
+
>
);
}
diff --git a/app/api/create-topup-session/route.ts b/app/api/create-topup-session/route.ts
new file mode 100644
index 000000000..f3508e581
--- /dev/null
+++ b/app/api/create-topup-session/route.ts
@@ -0,0 +1,60 @@
+import { NextRequest, NextResponse } from "next/server";
+import { withAuth } from "@/utils/withAuth";
+import { createClient } from "@/utils/supabase/server";
+import { User } from "@supabase/supabase-js";
+import { STRIPE_PRICE_IDS } from "@/utils/constants";
+import { TEST_MODE_ENABLED } from "@/utils/constants";
+
+const PEARAI_SERVER_URL = process.env.PEARAI_SERVER_URL;
+
+async function createTopUpSession(request: NextRequest & { user: User }) {
+ const supabase = createClient();
+
+ try {
+ const { amount } = await request.json();
+ const priceId = STRIPE_PRICE_IDS.TOP_UP_CREDITS[amount];
+ if (!priceId) {
+ return NextResponse.json({ error: "Invalid amount" }, { status: 400 });
+ }
+ const {
+ data: { session },
+ } = await supabase.auth.getSession();
+
+ if (!session) {
+ return NextResponse.json(
+ { error: "Failed to get session" },
+ { status: 401 },
+ );
+ }
+
+ const token = session.access_token;
+ const url = `${PEARAI_SERVER_URL}/payment${TEST_MODE_ENABLED ? "/test" : ""}/create-topup-session`;
+
+ const response = await fetch(url, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: `Bearer ${token}`,
+ },
+ body: JSON.stringify({ priceId, amount }),
+ });
+
+ if (!response.ok) {
+ const errorData = await response.json();
+ throw new Error(
+ errorData.error || `HTTP error! status: ${response.status}`,
+ );
+ }
+
+ const data = await response.json();
+ return NextResponse.json({ url: data.url });
+ } catch (error) {
+ console.error("Error creating top-up session:", error);
+ return NextResponse.json(
+ { error: "Failed to create top-up session" },
+ { status: 500 },
+ );
+ }
+}
+
+export const POST = withAuth(createTopUpSession);
diff --git a/app/api/dashboard-usage/route.ts b/app/api/dashboard-usage/route.ts
index 6ca0844ce..90d435841 100644
--- a/app/api/dashboard-usage/route.ts
+++ b/app/api/dashboard-usage/route.ts
@@ -40,7 +40,10 @@ const getDashboardUsage = async (request: NextRequest) => {
}
const data = await res.json();
- return NextResponse.json(data);
+ return NextResponse.json({
+ percent_credit_used: data.percent_credit_used,
+ remaining_topup_credits: data.remaining_topup_credits,
+ });
} catch (error) {
return NextResponse.json(
{ error: "Error getting requests usage" },
diff --git a/app/blog/[slug]/page.tsx b/app/blog/[slug]/page.tsx
index d208b0dd0..9918429d4 100644
--- a/app/blog/[slug]/page.tsx
+++ b/app/blog/[slug]/page.tsx
@@ -3,6 +3,7 @@ import { format, parseISO } from "date-fns";
import Image from "next/image";
import { notFound } from "next/navigation";
import { posts } from "@/lib/blog/postData";
+import Footer from "@/components/footer";
export const generateStaticParams = async () =>
posts.map((post) => ({ slug: post.url.split("/").pop() }));
@@ -83,6 +84,7 @@ const PostLayout = ({ params }: { params: { slug: string } }) => {
dangerouslySetInnerHTML={{ __html: post.content }}
/>
+
))}
+
);
}
diff --git a/app/faq/page.tsx b/app/faq/page.tsx
index b51fdffd2..c820bd904 100644
--- a/app/faq/page.tsx
+++ b/app/faq/page.tsx
@@ -9,6 +9,7 @@ import { constructMetadata } from "@/lib/utils";
import { Metadata } from "next/types";
import { FAQItem } from "@/types/faqItems";
import React from "react";
+import Footer from "@/components/footer";
export const metadata: Metadata = constructMetadata({
title: "FAQ",
@@ -77,6 +78,7 @@ const FAQ: React.FC = () => {
+
>
);
};
diff --git a/app/layout.tsx b/app/layout.tsx
index e23f18f07..ec21c7699 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -9,7 +9,6 @@ import dynamic from "next/dynamic";
import { Analytics } from "@vercel/analytics/react";
import { SpeedInsights } from "@vercel/speed-insights/next";
import { ThemeProvider } from "next-themes";
-import Footer from "@/components/footer";
const PostHogPageView = dynamic(() => import("./PostHogPageView"), {
ssr: false,
@@ -36,7 +35,6 @@ export default function RootLayout({
{children}
-
diff --git a/components/about.tsx b/components/about.tsx
index f102bd1fb..452d01a89 100644
--- a/components/about.tsx
+++ b/components/about.tsx
@@ -3,6 +3,7 @@ import { Card, CardContent, CardDescription } from "@/components/ui/card";
import { Tweet } from "@/components/tweet";
import { components } from "./ui/my-tweet";
import { Badge } from "./ui/badge";
+import Footer from "./footer";
type VideoData = {
src: string;
@@ -122,6 +123,7 @@ const AboutComponent: React.FC = () => {
+
);
};
diff --git a/components/dashboard.tsx b/components/dashboard.tsx
index 3825c95f2..1ecfd3fb4 100644
--- a/components/dashboard.tsx
+++ b/components/dashboard.tsx
@@ -19,6 +19,7 @@ type DashboardPageProps = {
export type UsageType = {
percent_credit_used: number | null;
+ remaining_topup_credits: number | null;
};
export default function DashboardPage({
@@ -31,6 +32,7 @@ export default function DashboardPage({
const [loading, setLoading] = useState(true);
const [usage, setUsage] = useState({
percent_credit_used: null,
+ remaining_topup_credits: null,
});
const handleCallbackForApp = useCallback(async () => {
diff --git a/components/dashboard/freetrial-card.tsx b/components/dashboard/freetrial-card.tsx
index bd91ed784..3ded4ea8b 100644
--- a/components/dashboard/freetrial-card.tsx
+++ b/components/dashboard/freetrial-card.tsx
@@ -3,8 +3,14 @@ import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Progress } from "@/components/ui/progress";
import { Badge } from "@/components/ui/badge";
-import { Info } from "lucide-react";
+import { InfoIcon } from "lucide-react";
import { UsageType } from "../dashboard";
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipProvider,
+ TooltipTrigger,
+} from "@/components/ui/tooltip";
type FreeTrialCardProps = {
usage: UsageType;
@@ -39,17 +45,15 @@ export default function FreeTrialCard({
PearAI Credits
-
- {loading ? (
- "-"
- ) : (
-
- {usage?.percent_credit_used != null
- ? `${Math.min(usage.percent_credit_used, 100)}%`
- : "Cannot find used percentage. Please contact PearAI support."}
-
- )}
-
+ {loading ? (
+ "-"
+ ) : (
+
+ {usage?.percent_credit_used != null
+ ? `${Math.min(usage.percent_credit_used, 100)}%`
+ : "Usage info not found. Contact PearAI support"}
+
+ )}