Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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
2 changes: 1 addition & 1 deletion app/(auth)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import PageIllustration from "@/components/page-illustration";
import PageIllustration from "@/components/ui/page-illustration";

export default function AuthLayout({
children,
Expand Down
4 changes: 3 additions & 1 deletion app/(default)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -27,6 +28,7 @@ export default function DefaultLayout({
<PageIllustration />
{children}
</main>
<Footer />
</>
);
}
60 changes: 60 additions & 0 deletions app/api/create-topup-session/route.ts
Original file line number Diff line number Diff line change
@@ -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);
5 changes: 4 additions & 1 deletion app/api/dashboard-usage/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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" },
Expand Down
2 changes: 2 additions & 0 deletions app/blog/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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() }));
Expand Down Expand Up @@ -83,6 +84,7 @@ const PostLayout = ({ params }: { params: { slug: string } }) => {
dangerouslySetInnerHTML={{ __html: post.content }}
/>
</article>
<Footer />
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
Expand Down
2 changes: 2 additions & 0 deletions app/blog/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { constructMetadata } from "@/lib/utils";
import { compareDesc } from "date-fns";
import { Metadata } from "next";
import { posts } from "@/lib/blog/postData";
import Footer from "@/components/footer";

export const metadata: Metadata = constructMetadata({
title: "Blog",
Expand All @@ -25,6 +26,7 @@ export default function Blog() {
<PostCard key={idx} {...post} />
))}
</div>
<Footer />
</div>
);
}
2 changes: 2 additions & 0 deletions app/faq/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -77,6 +78,7 @@ const FAQ: React.FC = () => {
</AccordionItem>
</Accordion>
</section>
<Footer />
</>
);
};
Expand Down
2 changes: 0 additions & 2 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -36,7 +35,6 @@ export default function RootLayout({
<div className="flex min-h-screen flex-col overflow-hidden">
<Header />
{children}
<Footer />
<Toaster position="bottom-right" richColors />
<Analytics />
<SpeedInsights />
Expand Down
2 changes: 2 additions & 0 deletions components/about.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -122,6 +123,7 @@ const AboutComponent: React.FC = () => {
</p>
</div>
</div>
<Footer />
</section>
);
};
Expand Down
2 changes: 2 additions & 0 deletions components/dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type DashboardPageProps = {

export type UsageType = {
percent_credit_used: number | null;
remaining_topup_credits: number | null;
};

export default function DashboardPage({
Expand All @@ -31,6 +32,7 @@ export default function DashboardPage({
const [loading, setLoading] = useState(true);
const [usage, setUsage] = useState<UsageType>({
percent_credit_used: null,
remaining_topup_credits: null,
});

const handleCallbackForApp = useCallback(async () => {
Expand Down
77 changes: 53 additions & 24 deletions components/dashboard/freetrial-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -39,17 +45,15 @@ export default function FreeTrialCard({
<div className="flex justify-between">
<p className="font-medium">PearAI Credits</p>
<p className="text-sm text-muted-foreground">
<strong>
{loading ? (
"-"
) : (
<strong>
{usage?.percent_credit_used != null
? `${Math.min(usage.percent_credit_used, 100)}%`
: "Cannot find used percentage. Please contact PearAI support."}
</strong>
)}
</strong>
{loading ? (
"-"
) : (
<strong>
{usage?.percent_credit_used != null
? `${Math.min(usage.percent_credit_used, 100)}%`
: "Usage info not found. Contact PearAI support"}
</strong>
)}
</p>
</div>
<Progress
Expand All @@ -64,6 +68,33 @@ export default function FreeTrialCard({
</p>
</div>
</div>
{usage.remaining_topup_credits && (
<div className="mb-4">
<div className="flex items-center justify-between">
<div className="flex items-center">
<p className="font-medium">Topup Credits</p>
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<InfoIcon className="ml-1 h-3 w-3 text-gray-700 dark:text-gray-600" />
</TooltipTrigger>
<TooltipContent className="-ml-9 max-w-[200px] border-gray-300 bg-white-50 text-center text-xs text-gray-700 dark:border-gray-200 dark:bg-secondary-main dark:text-gray-800">
<p>
Top-up credit does not expire and is utilized only
after the monthly quota is reached.
</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
<p className="text-sm text-muted-foreground">
{loading
? "-"
: `$${Math.floor(usage.remaining_topup_credits * 100) / 100} remaining`}
</p>
</div>
</div>
)}
<div className="mb-4">
<div className="flex justify-between">
<p className="font-medium">Current Plan</p>
Expand All @@ -85,19 +116,17 @@ export default function FreeTrialCard({
<Link href="/pricing">Subscribe Now</Link>
</Button>
</div>
<div className="mt-1 flex items-center">
<Info className="inline text-muted-foreground" size={14} />
<p className="ml-1.5 text-xs/3 text-muted-foreground">
Make sure PearAI is{" "}
<Button
variant="link"
asChild
className="p-0 text-xs text-primary-800"
>
<Link href="/pricing">installed.</Link>
</Button>{" "}
<div className="mt-4 flex items-start text-xs text-muted-foreground">
<InfoIcon className="mr-1 mt-0.5 h-3 w-3 flex-shrink-0" />
<div>
Make sure PearAI is
<Link href="/pricing" className="mx-1">
<span className="text-primary-800 hover:underline">
installed.
</span>
</Link>
Use this button to open app and login directly.
</p>
</div>
</div>
</CardContent>
</div>
Expand Down
Loading