feat: pricing page, navigation menu, footer
This commit is contained in:
parent
8a6d8dbfff
commit
985da98aee
|
@ -21,6 +21,7 @@
|
|||
"@radix-ui/react-dropdown-menu": "^2.0.5",
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@radix-ui/react-label": "^2.0.2",
|
||||
"@radix-ui/react-navigation-menu": "^1.1.4",
|
||||
"@radix-ui/react-popover": "^1.0.7",
|
||||
"@radix-ui/react-progress": "^1.0.3",
|
||||
"@radix-ui/react-scroll-area": "^1.0.5",
|
||||
|
@ -2423,6 +2424,42 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-navigation-menu": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.1.4.tgz",
|
||||
"integrity": "sha512-Cc+seCS3PmWmjI51ufGG7zp1cAAIRqHVw7C9LOA2TZ+R4hG6rDvHcTqIsEEFLmZO3zNVH72jOOE7kKNy8W+RtA==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.13.10",
|
||||
"@radix-ui/primitive": "1.0.1",
|
||||
"@radix-ui/react-collection": "1.0.3",
|
||||
"@radix-ui/react-compose-refs": "1.0.1",
|
||||
"@radix-ui/react-context": "1.0.1",
|
||||
"@radix-ui/react-direction": "1.0.1",
|
||||
"@radix-ui/react-dismissable-layer": "1.0.5",
|
||||
"@radix-ui/react-id": "1.0.1",
|
||||
"@radix-ui/react-presence": "1.0.1",
|
||||
"@radix-ui/react-primitive": "1.0.3",
|
||||
"@radix-ui/react-use-callback-ref": "1.0.1",
|
||||
"@radix-ui/react-use-controllable-state": "1.0.1",
|
||||
"@radix-ui/react-use-layout-effect": "1.0.1",
|
||||
"@radix-ui/react-use-previous": "1.0.1",
|
||||
"@radix-ui/react-visually-hidden": "1.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-popover": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.0.7.tgz",
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
"@radix-ui/react-dropdown-menu": "^2.0.5",
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@radix-ui/react-label": "^2.0.2",
|
||||
"@radix-ui/react-navigation-menu": "^1.1.4",
|
||||
"@radix-ui/react-popover": "^1.0.7",
|
||||
"@radix-ui/react-progress": "^1.0.3",
|
||||
"@radix-ui/react-scroll-area": "^1.0.5",
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
import Footer from "@/components/footer";
|
||||
import Navbar from "@/components/navbar";
|
||||
import { Metadata } from "next";
|
||||
import React from "react";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Skilled Ai",
|
||||
description: "Skilled Ai",
|
||||
};
|
||||
|
||||
const Layout = ({ children }: { children: React.ReactNode }) => {
|
||||
return (
|
||||
|
||||
<div className="flex flex-col min-h-screen">
|
||||
<Navbar />
|
||||
<main className="grow max-w-7xl mx-auto px-4">{children}</main>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Layout;
|
|
@ -0,0 +1,72 @@
|
|||
"use client";
|
||||
|
||||
import React, { useState } from "react";
|
||||
import PricingHeader from "@/components/pricing/pricing-header";
|
||||
import PricingSwitch from "@/components/pricing/pricing-switch";
|
||||
import PricingCard from "@/components/pricing/pricing-card";
|
||||
|
||||
export default function Page() {
|
||||
const [isYearly, setIsYearly] = useState(false);
|
||||
const togglePricingPeriod = (value: string) =>
|
||||
setIsYearly(parseInt(value) === 1);
|
||||
|
||||
const plans = [
|
||||
{
|
||||
title: "Basic",
|
||||
monthlyPrice: 10,
|
||||
yearlyPrice: 100,
|
||||
description: "Essential features you need to get started",
|
||||
features: [
|
||||
"Full access to our extensive library of courses and tutorials",
|
||||
"Regularly updated content to keep you ahead of the curve",
|
||||
"Skill assessments and progress tracking",
|
||||
"Community forums for networking and support",
|
||||
"Certificate of completion for each course",
|
||||
],
|
||||
actionLabel: "Get Started",
|
||||
},
|
||||
{
|
||||
title: "Pro",
|
||||
monthlyPrice: 25,
|
||||
yearlyPrice: 250,
|
||||
description: "Perfect for owners of small & medium businessess",
|
||||
features: [
|
||||
"All features included in the Basic tier",
|
||||
"Exclusive access to advanced courses and workshops",
|
||||
"Personalized career coaching sessions",
|
||||
"Priority customer support",
|
||||
"Premium resources such as e-books and industry reports",
|
||||
],
|
||||
actionLabel: "Get Started",
|
||||
popular: true,
|
||||
},
|
||||
{
|
||||
title: "Enterprise",
|
||||
price: "Custom",
|
||||
description: "Dedicated support and infrastructure to fit your needs",
|
||||
features: [
|
||||
"Tailored solutions for businesses and teams",
|
||||
"Dedicated account management",
|
||||
"Customizable learning paths and content curation",
|
||||
"Team analytics and progress tracking",
|
||||
"On-site workshops and training sessions (optional)",
|
||||
],
|
||||
actionLabel: "Contact Sales",
|
||||
exclusive: true,
|
||||
},
|
||||
];
|
||||
return (
|
||||
<div className="py-8 md:py-24">
|
||||
<PricingHeader
|
||||
title="Welcome to Skilled AI Pricing"
|
||||
subtitle="At Skilled AI, we empower software engineers with top-notch upskilling resources tailored to their career goals. Explore our pricing plans below and take the next step towards mastering your craft."
|
||||
/>
|
||||
<PricingSwitch onSwitch={togglePricingPeriod} />
|
||||
<section className="flex flex-col sm:flex-row sm:flex-wrap justify-center gap-8 mt-8">
|
||||
{plans.map((plan) => {
|
||||
return <PricingCard key={plan.title} {...plan} isYearly={isYearly} />;
|
||||
})}
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,223 @@
|
|||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import Link from "next/link";
|
||||
|
||||
export default function Footer() {
|
||||
return (
|
||||
<div>
|
||||
<section className="w-full py-12 md:20 border-t">
|
||||
<div className="container px-4 md:px-6 flex flex-col items-center text-center">
|
||||
<h2 className="text-2xl font-bold tracking-tighter sm:text-3xl md:text-4xl lg:text-5xl/none ">
|
||||
Stay Connected
|
||||
</h2>
|
||||
<p className="mx-auto max-w-[700px] md:text-lg text-muted-foreground mt-2">
|
||||
Subscribe to our newsletter and follow us on our social media.
|
||||
</p>
|
||||
<div className="w-full max-w-md space-y-2 my-4">
|
||||
<form className="flex space-x-2">
|
||||
<Input
|
||||
className="max-w-lg flex-1 "
|
||||
placeholder="Enter your email"
|
||||
type="email"
|
||||
/>
|
||||
<Button className="" type="submit" variant="outline">
|
||||
Subscribe
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
{/* <div className="flex justify-center space-x-4">
|
||||
<Link aria-label="Facebook page" className="" href="#">
|
||||
<FacebookIcon className="h-6 w-6" />
|
||||
</Link>
|
||||
<Link aria-label="Twitter profile" className="" href="#">
|
||||
<TwitterIcon className="h-6 w-6" />
|
||||
</Link>
|
||||
<Link aria-label="Instagram profile" className="" href="#">
|
||||
<InstagramIcon className="h-6 w-6" />
|
||||
</Link>
|
||||
<Link aria-label="LinkedIn profile" className="" href="#">
|
||||
<LinkedinIcon className="h-6 w-6" />
|
||||
</Link>
|
||||
</div> */}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer className="border-t py-12 md:20">
|
||||
<div className="max-w-7xl mx-auto px-4 grid grid-cols-4 gap-4">
|
||||
<ul className="space-y-1">
|
||||
<li>
|
||||
<Link className="text-muted-foreground hover:underline" href="#">
|
||||
About Us
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link className="text-muted-foreground hover:underline" href="#">
|
||||
Contact Us
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link className="text-muted-foreground hover:underline" href="#">
|
||||
FAQs
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link className="text-muted-foreground hover:underline" href="#">
|
||||
Terms of Service
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
<ul className="space-y-1">
|
||||
<li>
|
||||
<Link className="text-muted-foreground hover:underline" href="#">
|
||||
Privacy Policy
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link className="text-muted-foreground hover:underline" href="#">
|
||||
Blog
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link className="text-muted-foreground hover:underline" href="#">
|
||||
Careers
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link className="text-muted-foreground hover:underline" href="#">
|
||||
Resources
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
<ul className="space-y-1">
|
||||
<li>
|
||||
<Link className="text-muted-foreground hover:underline" href="#">
|
||||
Pricing
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link className="text-muted-foreground hover:underline" href="#">
|
||||
Feedback
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link className="text-muted-foreground hover:underline" href="#">
|
||||
Community Guidelines
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link className="text-muted-foreground hover:underline" href="#">
|
||||
Cookie Policy
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
<div className="space-y-2">
|
||||
<h4 className="font-semibold">Follow Us</h4>
|
||||
<div className="flex space-x-4 text-muted-foreground">
|
||||
<Link aria-label="Facebook page" className="" href="#">
|
||||
<FacebookIcon className="h-6 w-6" />
|
||||
</Link>
|
||||
<Link aria-label="Twitter profile" className="" href="#">
|
||||
<TwitterIcon className="h-6 w-6" />
|
||||
</Link>
|
||||
<Link aria-label="Instagram profile" className="" href="#">
|
||||
<InstagramIcon className="h-6 w-6" />
|
||||
</Link>
|
||||
<Link aria-label="LinkedIn profile" className="" href="#">
|
||||
<LinkedinIcon className="h-6 w-6" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="max-w-7xl mx-auto px-4 flex justify-between mt-8 md:mt-16">
|
||||
<p className="text-muted-foreground">
|
||||
Skilled AI - Empowering Software Engineers to Thrive
|
||||
</p>
|
||||
<p className="text-muted-foreground">
|
||||
Copyright © 2024 All Rights Reserved.
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function FacebookIcon(props: any) {
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="M18 2h-3a5 5 0 0 0-5 5v3H7v4h3v8h4v-8h3l1-4h-4V7a1 1 0 0 1 1-1h3z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function InstagramIcon(props: any) {
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<rect width="20" height="20" x="2" y="2" rx="5" ry="5" />
|
||||
<path d="M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z" />
|
||||
<line x1="17.5" x2="17.51" y1="6.5" y2="6.5" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function LinkedinIcon(props: any) {
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="M16 8a6 6 0 0 1 6 6v7h-4v-7a2 2 0 0 0-2-2 2 2 0 0 0-2 2v7h-4v-7a6 6 0 0 1 6-6z" />
|
||||
<rect width="4" height="12" x="2" y="9" />
|
||||
<circle cx="4" cy="4" r="2" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function TwitterIcon(props: any) {
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<path d="M22 4s-.7 2.1-2 3.4c1.6 10-9.4 17.3-18 11.6 2.2.1 4.4-.6 6-2C3 15.5.5 9.6 3 5c2.2 2.6 5.6 4.1 9 4-.9-4.2 4-6.6 7-3.8 1.1 0 3-1.2 3-1.2z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,177 @@
|
|||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import Link from "next/link";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Icons } from "@/components/icons";
|
||||
import {
|
||||
NavigationMenu,
|
||||
NavigationMenuContent,
|
||||
NavigationMenuItem,
|
||||
NavigationMenuLink,
|
||||
NavigationMenuList,
|
||||
NavigationMenuTrigger,
|
||||
navigationMenuTriggerStyle,
|
||||
} from "@/components/ui/navigation-menu";
|
||||
import ThemeToggle from "./layout/ThemeToggle/theme-toggle";
|
||||
import { buttonVariants } from "./ui/button";
|
||||
|
||||
const careerPaths: { title: string; href: string; description: string }[] = [
|
||||
{
|
||||
title: "Frontend Developer",
|
||||
href: "#",
|
||||
description:
|
||||
"Master frontend technologies like HTML, CSS, and JavaScript to build dynamic and user-friendly web interfaces.",
|
||||
},
|
||||
{
|
||||
title: "Backend Developer",
|
||||
href: "#",
|
||||
description:
|
||||
"Learn server-side programming languages and frameworks to create scalable and efficient web applications.",
|
||||
},
|
||||
{
|
||||
title: "Full Stack Developer",
|
||||
href: "#",
|
||||
description:
|
||||
"Combine frontend and backend development skills to build end-to-end web applications from scratch.",
|
||||
},
|
||||
{
|
||||
title: "Mobile App Developer",
|
||||
href: "#",
|
||||
description:
|
||||
"Specialize in mobile app development for iOS or Android platforms using Swift, Kotlin, or cross-platform frameworks.",
|
||||
},
|
||||
{
|
||||
title: "Game Developer",
|
||||
href: "#",
|
||||
description:
|
||||
"Dive into game development with Unity or Unreal Engine, creating immersive gaming experiences for various platforms.",
|
||||
},
|
||||
{
|
||||
title: "Machine Learning Engineer",
|
||||
href: "#",
|
||||
description:
|
||||
"Develop machine learning models and algorithms to solve complex problems and make predictions based on data.",
|
||||
},
|
||||
];
|
||||
|
||||
export default function Navbar() {
|
||||
return (
|
||||
<header className="border-b py-3">
|
||||
<div className="max-w-7xl mx-auto flex justify-between items-center px-4">
|
||||
<Link href="/" className="inline-block w-fit">
|
||||
<h2 className="text-xl font-bold">SkilledAI</h2>
|
||||
</Link>
|
||||
|
||||
<div className="flex gap-8 items-center">
|
||||
<NavigationMenu>
|
||||
<NavigationMenuList>
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuTrigger>Courses</NavigationMenuTrigger>
|
||||
<NavigationMenuContent>
|
||||
<ul className="grid gap-3 p-4 md:w-[400px] lg:w-[500px] lg:grid-cols-[.75fr_1fr]">
|
||||
<li className="row-span-3">
|
||||
<NavigationMenuLink asChild>
|
||||
<a
|
||||
className="flex h-full w-full select-none flex-col justify-end rounded-md bg-gradient-to-b from-muted/50 to-muted p-6 no-underline outline-none focus:shadow-md"
|
||||
href="/"
|
||||
>
|
||||
<Icons.logo className="h-6 w-6" />
|
||||
<div className="mb-2 mt-4 text-lg font-medium">
|
||||
Skilled AI
|
||||
</div>
|
||||
<p className="text-sm leading-tight text-muted-foreground">
|
||||
Browse our extensive catalog of courses covering a
|
||||
wide range of topics in software engineering, from
|
||||
programming languages to advanced technologies.
|
||||
</p>
|
||||
</a>
|
||||
</NavigationMenuLink>
|
||||
</li>
|
||||
<ListItem href="#" title="Python Programming">
|
||||
Learn the fundamentals of Python programming language,
|
||||
from basic syntax to advanced concepts like
|
||||
object-oriented programming.
|
||||
</ListItem>
|
||||
<ListItem href="#" title="Web Development with React.js:">
|
||||
Master frontend web development using React.js library,
|
||||
including state management, component-based architecture,
|
||||
and modern UI design techniques.
|
||||
</ListItem>
|
||||
<ListItem href="#" title="Machine Learning Fundamentals">
|
||||
Dive into the world of machine learning, covering topics
|
||||
such as supervised and unsupervised learning, neural
|
||||
networks, and model evaluation.
|
||||
</ListItem>
|
||||
</ul>
|
||||
</NavigationMenuContent>
|
||||
</NavigationMenuItem>
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuTrigger>Career Paths</NavigationMenuTrigger>
|
||||
<NavigationMenuContent>
|
||||
<ul className="grid w-[400px] gap-3 p-4 md:w-[500px] md:grid-cols-2 lg:w-[600px] ">
|
||||
{careerPaths.map((path) => (
|
||||
<ListItem
|
||||
key={path.title}
|
||||
title={path.title}
|
||||
href={path.href}
|
||||
>
|
||||
{path.description}
|
||||
</ListItem>
|
||||
))}
|
||||
</ul>
|
||||
</NavigationMenuContent>
|
||||
</NavigationMenuItem>
|
||||
<NavigationMenuItem>
|
||||
<Link href="#" legacyBehavior passHref>
|
||||
<NavigationMenuLink className={navigationMenuTriggerStyle()}>
|
||||
Community
|
||||
</NavigationMenuLink>
|
||||
</Link>
|
||||
</NavigationMenuItem>
|
||||
<NavigationMenuItem>
|
||||
<Link href="/pricing" legacyBehavior passHref>
|
||||
<NavigationMenuLink className={navigationMenuTriggerStyle()}>
|
||||
Pricing
|
||||
</NavigationMenuLink>
|
||||
</Link>
|
||||
</NavigationMenuItem>
|
||||
</NavigationMenuList>
|
||||
</NavigationMenu>
|
||||
|
||||
<Link className={buttonVariants({ variant: "secondary" })} href="/">
|
||||
Sign In
|
||||
</Link>
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
const ListItem = React.forwardRef<
|
||||
React.ElementRef<"a">,
|
||||
React.ComponentPropsWithoutRef<"a">
|
||||
>(({ className, title, children, ...props }, ref) => {
|
||||
return (
|
||||
<li>
|
||||
<NavigationMenuLink asChild>
|
||||
<a
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"block select-none space-y-1 rounded-md p-3 leading-none no-underline outline-none transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<div className="text-sm font-medium leading-none">{title}</div>
|
||||
<p className="line-clamp-2 text-sm leading-snug text-muted-foreground">
|
||||
{children}
|
||||
</p>
|
||||
</a>
|
||||
</NavigationMenuLink>
|
||||
</li>
|
||||
);
|
||||
});
|
||||
ListItem.displayName = "ListItem";
|
|
@ -0,0 +1,10 @@
|
|||
import { CheckCircle2 } from "lucide-react"
|
||||
|
||||
const CheckItem = ({ text }: { text: string }) => (
|
||||
<div className="flex gap-2">
|
||||
<CheckCircle2 size={18} className="my-auto text-green-400" />
|
||||
<p className="pt-0.5 text-zinc-700 dark:text-zinc-300 text-sm">{text}</p>
|
||||
</div>
|
||||
)
|
||||
|
||||
export default CheckItem
|
|
@ -0,0 +1,100 @@
|
|||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { cn } from "@/lib/utils";
|
||||
import CheckItem from "./check-item";
|
||||
|
||||
type PricingCardProps = {
|
||||
isYearly?: boolean;
|
||||
title: string;
|
||||
monthlyPrice?: number;
|
||||
yearlyPrice?: number;
|
||||
description: string;
|
||||
features: string[];
|
||||
actionLabel: string;
|
||||
popular?: boolean;
|
||||
exclusive?: boolean;
|
||||
};
|
||||
|
||||
const PricingCard = ({
|
||||
isYearly,
|
||||
title,
|
||||
monthlyPrice,
|
||||
yearlyPrice,
|
||||
description,
|
||||
features,
|
||||
actionLabel,
|
||||
popular,
|
||||
exclusive,
|
||||
}: PricingCardProps) => (
|
||||
<Card
|
||||
className={cn(
|
||||
`w-72 flex flex-col justify-between py-1 ${
|
||||
popular ? "border border-primary" : "border"
|
||||
} mx-auto sm:mx-0`,
|
||||
{
|
||||
"animate-background-shine bg-white dark:bg-[linear-gradient(110deg,#000103,45%,#1e2631,55%,#000103)] bg-[length:200%_100%] transition-colors":
|
||||
exclusive,
|
||||
},
|
||||
)}
|
||||
>
|
||||
<div>
|
||||
<CardHeader className="pb-8 pt-4">
|
||||
{isYearly && yearlyPrice && monthlyPrice ? (
|
||||
<div className="flex justify-between">
|
||||
<CardTitle className="text-zinc-700 dark:text-zinc-300 text-lg">
|
||||
{title}
|
||||
</CardTitle>
|
||||
<div
|
||||
className={cn(
|
||||
"px-2.5 rounded-xl h-fit text-sm py-1 bg-zinc-200 text-black dark:bg-zinc-800 dark:text-white",
|
||||
{
|
||||
"bg-gradient-to-r from-orange-400 to-rose-400 dark:text-black ":
|
||||
popular,
|
||||
},
|
||||
)}
|
||||
>
|
||||
Save ${monthlyPrice * 12 - yearlyPrice}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<CardTitle className="text-zinc-700 dark:text-zinc-300 text-lg">
|
||||
{title}
|
||||
</CardTitle>
|
||||
)}
|
||||
<div className="flex gap-0.5">
|
||||
<h3 className="text-3xl font-bold">
|
||||
{yearlyPrice && isYearly
|
||||
? "$" + yearlyPrice
|
||||
: monthlyPrice
|
||||
? "$" + monthlyPrice
|
||||
: "Custom"}
|
||||
</h3>
|
||||
<span className="flex flex-col justify-end text-sm mb-1">
|
||||
{yearlyPrice && isYearly ? "/year" : monthlyPrice ? "/month" : null}
|
||||
</span>
|
||||
</div>
|
||||
<CardDescription className="pt-1.5 h-12">{description}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-col gap-2">
|
||||
{features.map((feature: string) => (
|
||||
<CheckItem key={feature} text={feature} />
|
||||
))}
|
||||
</CardContent>
|
||||
</div>
|
||||
<CardFooter className="mt-2">
|
||||
<Button className="relative inline-flex w-full items-center justify-center rounded-md bg-black text-white dark:bg-white px-6 font-medium dark:text-black transition-colors focus:outline-none focus:ring-2 focus:ring-slate-400 focus:ring-offset-2 focus:ring-offset-slate-50">
|
||||
<div className="absolute -inset-0.5 -z-10 rounded-lg bg-gradient-to-b from-[#c7d2fe] to-[#8678f9] opacity-75 blur" />
|
||||
{actionLabel}
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
);
|
||||
|
||||
export default PricingCard;
|
|
@ -0,0 +1,9 @@
|
|||
const PricingHeader = ({ title, subtitle }: { title: string; subtitle: string }) => (
|
||||
<section className="text-center">
|
||||
<h2 className="text-3xl font-bold">{title}</h2>
|
||||
<p className="pt-1 text-muted-foreground max-w-2xl mx-auto text-balance">{subtitle}</p>
|
||||
<br />
|
||||
</section>
|
||||
)
|
||||
|
||||
export default PricingHeader
|
|
@ -0,0 +1,20 @@
|
|||
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
|
||||
type PricingSwitchProps = {
|
||||
onSwitch: (value: string) => void;
|
||||
};
|
||||
|
||||
const PricingSwitch = ({ onSwitch }: PricingSwitchProps) => (
|
||||
<Tabs defaultValue="0" className="w-40 mx-auto" onValueChange={onSwitch}>
|
||||
<TabsList className="py-6 px-2">
|
||||
<TabsTrigger value="0" className="text-base">
|
||||
Monthly
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="1" className="text-base">
|
||||
Yearly
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
);
|
||||
|
||||
export default PricingSwitch;
|
|
@ -0,0 +1,128 @@
|
|||
import * as React from "react"
|
||||
import { ChevronDownIcon } from "@radix-ui/react-icons"
|
||||
import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu"
|
||||
import { cva } from "class-variance-authority"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const NavigationMenu = React.forwardRef<
|
||||
React.ElementRef<typeof NavigationMenuPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Root>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<NavigationMenuPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative z-10 flex max-w-max flex-1 items-center justify-center",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<NavigationMenuViewport />
|
||||
</NavigationMenuPrimitive.Root>
|
||||
))
|
||||
NavigationMenu.displayName = NavigationMenuPrimitive.Root.displayName
|
||||
|
||||
const NavigationMenuList = React.forwardRef<
|
||||
React.ElementRef<typeof NavigationMenuPrimitive.List>,
|
||||
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.List>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<NavigationMenuPrimitive.List
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"group flex flex-1 list-none items-center justify-center space-x-1",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName
|
||||
|
||||
const NavigationMenuItem = NavigationMenuPrimitive.Item
|
||||
|
||||
const navigationMenuTriggerStyle = cva(
|
||||
"group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50"
|
||||
)
|
||||
|
||||
const NavigationMenuTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof NavigationMenuPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Trigger>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<NavigationMenuPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={cn(navigationMenuTriggerStyle(), "group", className)}
|
||||
{...props}
|
||||
>
|
||||
{children}{" "}
|
||||
<ChevronDownIcon
|
||||
className="relative top-[1px] ml-1 h-3 w-3 transition duration-300 group-data-[state=open]:rotate-180"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</NavigationMenuPrimitive.Trigger>
|
||||
))
|
||||
NavigationMenuTrigger.displayName = NavigationMenuPrimitive.Trigger.displayName
|
||||
|
||||
const NavigationMenuContent = React.forwardRef<
|
||||
React.ElementRef<typeof NavigationMenuPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Content>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<NavigationMenuPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"left-0 top-0 w-full data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 md:absolute md:w-auto ",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
NavigationMenuContent.displayName = NavigationMenuPrimitive.Content.displayName
|
||||
|
||||
const NavigationMenuLink = NavigationMenuPrimitive.Link
|
||||
|
||||
const NavigationMenuViewport = React.forwardRef<
|
||||
React.ElementRef<typeof NavigationMenuPrimitive.Viewport>,
|
||||
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Viewport>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div className={cn("absolute left-0 top-full flex justify-center")}>
|
||||
<NavigationMenuPrimitive.Viewport
|
||||
className={cn(
|
||||
"origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-[var(--radix-navigation-menu-viewport-width)]",
|
||||
className
|
||||
)}
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
))
|
||||
NavigationMenuViewport.displayName =
|
||||
NavigationMenuPrimitive.Viewport.displayName
|
||||
|
||||
const NavigationMenuIndicator = React.forwardRef<
|
||||
React.ElementRef<typeof NavigationMenuPrimitive.Indicator>,
|
||||
React.ComponentPropsWithoutRef<typeof NavigationMenuPrimitive.Indicator>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<NavigationMenuPrimitive.Indicator
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<div className="relative top-[60%] h-2 w-2 rotate-45 rounded-tl-sm bg-border shadow-md" />
|
||||
</NavigationMenuPrimitive.Indicator>
|
||||
))
|
||||
NavigationMenuIndicator.displayName =
|
||||
NavigationMenuPrimitive.Indicator.displayName
|
||||
|
||||
export {
|
||||
navigationMenuTriggerStyle,
|
||||
NavigationMenu,
|
||||
NavigationMenuList,
|
||||
NavigationMenuItem,
|
||||
NavigationMenuContent,
|
||||
NavigationMenuTrigger,
|
||||
NavigationMenuLink,
|
||||
NavigationMenuIndicator,
|
||||
NavigationMenuViewport,
|
||||
}
|
|
@ -9,6 +9,7 @@ export async function markdownToHtml(tutorialCode: string) {
|
|||
.use(remarkParse)
|
||||
.use(remarkRehype)
|
||||
.use(rehypePrettyCode, {})
|
||||
// @ts-ignore
|
||||
.use(rehypeStringify)
|
||||
.process(tutorialCode);
|
||||
|
||||
|
|
|
@ -69,10 +69,19 @@ module.exports = {
|
|||
from: { height: "var(--radix-accordion-content-height)" },
|
||||
to: { height: 0 },
|
||||
},
|
||||
"background-shine": {
|
||||
from: {
|
||||
backgroundPosition: "0 0",
|
||||
},
|
||||
to: {
|
||||
backgroundPosition: "-200% 0",
|
||||
},
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
"accordion-down": "accordion-down 0.2s ease-out",
|
||||
"accordion-up": "accordion-up 0.2s ease-out",
|
||||
"background-shine": "background-shine 2s linear infinite",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue