Skip to content

Commit

Permalink
Added pro subscription modal when api limit exhausts
Browse files Browse the repository at this point in the history
  • Loading branch information
hemantwasthere committed Jul 31, 2023
1 parent b6747bc commit c718971
Show file tree
Hide file tree
Showing 15 changed files with 322 additions and 24 deletions.
6 changes: 3 additions & 3 deletions app/(dashboard)/(routes)/code/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ import { Input } from "@/components/ui/input";
import { UserAvatar } from "@/components/user-avatar";
import { cn } from "@/lib/utils";
import { zodResolver } from "@hookform/resolvers/zod";
// import { useProModal } from "@/hooks/use-pro-modal";
import { useProModal } from "@/hooks/use-pro-modal";

import { NextPage } from "next";
import { formSchema } from "./constants";

const CodePage: NextPage = () => {
const router = useRouter();
// const proModal = useProModal();
const proModal = useProModal();
const [messages, setMessages] = useState<ChatCompletionRequestMessage[]>([]);

const form = useForm<z.infer<typeof formSchema>>({
Expand All @@ -50,7 +50,7 @@ const CodePage: NextPage = () => {
form.reset();
} catch (error: any) {
if (error?.response?.status === 403) {
// proModal.onOpen();
proModal.onOpen();
} else {
toast.error("Something went wrong.");
}
Expand Down
6 changes: 3 additions & 3 deletions app/(dashboard)/(routes)/conversation/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@ import { Input } from "@/components/ui/input";
import { UserAvatar } from "@/components/user-avatar";
import { cn } from "@/lib/utils";
import { zodResolver } from "@hookform/resolvers/zod";
// import { useProModal } from "@/hooks/use-pro-modal";
import { useProModal } from "@/hooks/use-pro-modal";

import { formSchema } from "./constants";

const ConversationPage: NextPage = () => {
const router = useRouter();
// const proModal = useProModal();
const proModal = useProModal();
const [messages, setMessages] = useState<ChatCompletionRequestMessage[]>([]);

const form = useForm<z.infer<typeof formSchema>>({
Expand All @@ -49,7 +49,7 @@ const ConversationPage: NextPage = () => {
form.reset();
} catch (error: any) {
if (error?.response?.status === 403) {
// proModal.onOpen();
proModal.onOpen();
} else {
toast.error("Something went wrong.");
}
Expand Down
6 changes: 3 additions & 3 deletions app/(dashboard)/(routes)/image/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ import { Empty } from "@/components/ui/empty";
import { Form, FormControl, FormField, FormItem } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
// import { useProModal } from "@/hooks/use-pro-modal";
import { useProModal } from "@/hooks/use-pro-modal";

import { amountOptions, formSchema, resolutionOptions } from "./constants";

const PhotoPage = () => {
// const proModal = useProModal();
const proModal = useProModal();
const router = useRouter();
const [photos, setPhotos] = useState<string[]>([]);

Expand All @@ -49,7 +49,7 @@ const PhotoPage = () => {
setPhotos(urls);
} catch (error: any) {
if (error?.response?.status === 403) {
// proModal.onOpen();
proModal.onOpen();
} else {
toast.error("Something went wrong.");
}
Expand Down
8 changes: 4 additions & 4 deletions app/(dashboard)/(routes)/music/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ import { Input } from "@/components/ui/input";
import { Form, FormControl, FormField, FormItem } from "@/components/ui/form";
import { Loader } from "@/components/loader";
import { Empty } from "@/components/ui/empty";
// import { useProModal } from "@/hooks/use-pro-modal";
import { useProModal } from "@/hooks/use-pro-modal";

import { formSchema } from "./constants";

const MusicPage = () => {
// const proModal = useProModal();
const proModal = useProModal();
const router = useRouter();
const [music, setMusic] = useState<string>();

Expand All @@ -42,7 +42,7 @@ const MusicPage = () => {
form.reset();
} catch (error: any) {
if (error?.response?.status === 403) {
// proModal.onOpen();
proModal.onOpen();
} else {
toast.error("Something went wrong.");
}
Expand Down Expand Up @@ -73,7 +73,7 @@ const MusicPage = () => {
<FormItem className="col-span-12 lg:col-span-10">
<FormControl className="m-0 p-0">
<Input
className="border-0 outline-none focus-visible:ring-0 focus-visible:ring-transparent"
className="px-2 border-0 outline-none focus-visible:ring-0 focus-visible:ring-transparent"
disabled={isLoading}
placeholder="Piano solo"
{...field}
Expand Down
8 changes: 4 additions & 4 deletions app/(dashboard)/(routes)/video/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ import { Button } from "@/components/ui/button";
import { Empty } from "@/components/ui/empty";
import { Form, FormControl, FormField, FormItem } from "@/components/ui/form";
import { Input } from "@/components/ui/input";
// import { useProModal } from "@/hooks/use-pro-modal";
import { useProModal } from "@/hooks/use-pro-modal";

import { formSchema } from "./constants";

const VideoPage = () => {
const router = useRouter();
// const proModal = useProModal();
const proModal = useProModal();
const [video, setVideo] = useState<string>();

const form = useForm<z.infer<typeof formSchema>>({
Expand All @@ -43,7 +43,7 @@ const VideoPage = () => {
form.reset();
} catch (error: any) {
if (error?.response?.status === 403) {
// proModal.onOpen();
proModal.onOpen();
} else {
toast.error("Something went wrong.");
}
Expand Down Expand Up @@ -74,7 +74,7 @@ const VideoPage = () => {
<FormItem className="col-span-12 lg:col-span-10">
<FormControl className="m-0 p-0">
<Input
className="border-0 outline-none focus-visible:ring-0 focus-visible:ring-transparent"
className="px-2 border-0 outline-none focus-visible:ring-0 focus-visible:ring-transparent"
disabled={isLoading}
placeholder="Clown fish swimming in a coral reef"
{...field}
Expand Down
2 changes: 1 addition & 1 deletion app/(dashboard)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const DashboardLayout = async ({ children }: { children: React.ReactNode }) => {

return (
<div className="h-full relative">
<div className="hidden h-full md:flex md:w-72 md:flex-col md:fixed md:inset-y-0 z-[80] bg-gray-900">
<div className="hidden h-full md:flex md:w-72 md:flex-col md:fixed md:inset-y-0 bg-gray-900">
<Sidebar apiLimitCount={apiLimitCount} />
</div>
<main className="md:pl-72 pb-10">
Expand Down
10 changes: 8 additions & 2 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import './globals.css'
import { ClerkProvider } from '@clerk/nextjs'

import './globals.css'

import { ModalProvider } from '@/components/modal-provider'

const inter = Inter({ subsets: ['latin'] })

export const metadata: Metadata = {
Expand All @@ -14,7 +17,10 @@ export default function RootLayout({ children }: { children: React.ReactNode })
return (
<ClerkProvider>
<html lang="en">
<body className={inter.className}>{children}</body>
<body className={inter.className}>
<ModalProvider />
{children}
</body>
</html>
</ClerkProvider>
)
Expand Down
6 changes: 3 additions & 3 deletions components/free-counter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { Progress } from "@/components/ui/progress";
import { MAX_FREE_COUNTS } from "@/constants";
// import { useProModal } from "@/hooks/use-pro-modal";
import { useProModal } from "@/hooks/use-pro-modal";

interface FreeCounterProps {
isPro?: boolean;
Expand All @@ -16,7 +16,7 @@ interface FreeCounterProps {

export const FreeCounter: React.FC<FreeCounterProps> = ({ isPro = false, apiLimitCount = 0 }) => {
const [mounted, setMounted] = useState(false);
// const proModal = useProModal();
const proModal = useProModal();

useEffect(() => setMounted(true), []);

Expand All @@ -34,7 +34,7 @@ export const FreeCounter: React.FC<FreeCounterProps> = ({ isPro = false, apiLimi
</p>
<Progress className="h-3" value={(apiLimitCount / MAX_FREE_COUNTS) * 100} />
</div>
<Button variant="premium" className="w-full">
<Button onClick={proModal.onOpen} variant="premium" className="w-full">
Upgrade
<Zap className="w-4 h-4 ml-2 fill-white" />
</Button>
Expand Down
19 changes: 19 additions & 0 deletions components/modal-provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"use client";

import { useEffect, useState } from "react";

import { ProModal } from "@/components/pro-modal";

export const ModalProvider = () => {
const [isMounted, setIsMounted] = useState(false);

useEffect(() => setIsMounted(true), []);

if (!isMounted) return null;

return (
<>
<ProModal />
</>
);
};
77 changes: 77 additions & 0 deletions components/pro-modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"use client";

import axios from "axios";
import { useState } from "react";
import { Check, Zap } from "lucide-react";
import { toast } from "react-hot-toast";

import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogDescription,
DialogFooter
} from "@/components/ui/dialog";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { useProModal } from "@/hooks/use-pro-modal";
import { tools } from "@/constants";
import { Card } from "@/components/ui/card";
import { cn } from "@/lib/utils";

export const ProModal = () => {
const proModal = useProModal();
const [loading, setLoading] = useState(false);

const onSubscribe = async () => {
try {
setLoading(true);
const response = await axios.get("/api/stripe");

window.location.href = response.data.url;
} catch (error) {
toast.error("Something went wrong");
} finally {
setLoading(false);
}
}

return (
<Dialog open={proModal.isOpen} onOpenChange={proModal.onClose}>
<DialogContent>
<DialogHeader>
<DialogTitle className="flex justify-center items-center flex-col gap-y-4 pb-2">
<div className="flex items-center gap-x-2 font-bold text-xl">
Upgrade to Genius
<Badge variant="premium" className="uppercase text-sm py-1">
pro
</Badge>
</div>
</DialogTitle>
<DialogDescription className="text-center pt-2 space-y-2 text-zinc-900 font-medium">
{tools.map((tool) => (
<Card key={tool.href} className="p-3 border-black/5 flex items-center justify-between">
<div className="flex items-center gap-x-4">
<div className={cn("p-2 w-fit rounded-md", tool.bgColor)}>
<tool.icon className={cn("w-6 h-6", tool.color)} />
</div>
<div className="font-semibold text-sm">
{tool.label}
</div>
</div>
<Check className="text-primary w-5 h-5" />
</Card>
))}
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button disabled={loading} onClick={onSubscribe} size="lg" variant="premium" className="w-full">
Upgrade
<Zap className="w-4 h-4 ml-2 fill-white" />
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
};
37 changes: 37 additions & 0 deletions components/ui/badge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"

import { cn } from "@/lib/utils"

const badgeVariants = cva(
"inline-flex items-center rounded-full border px-2.5 py-0.25 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
{
variants: {
variant: {
default:
"border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
secondary:
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
destructive:
"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
outline: "text-foreground",
premium: "bg-gradient-to-r from-indigo-500 via-purple-500 to-pink-500 text-primary-foreground border-0"
},
},
defaultVariants: {
variant: "default",
},
}
)

export interface BadgeProps
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof badgeVariants> { }

function Badge({ className, variant, ...props }: BadgeProps) {
return (
<div className={cn(badgeVariants({ variant }), className)} {...props} />
)
}

export { Badge, badgeVariants }
Loading

0 comments on commit c718971

Please sign in to comment.