├── .eslintrc.json
├── .gitignore
├── .vscode
└── settings.json
├── README.md
├── app
├── api
│ ├── auth
│ │ └── [...nextauth]
│ │ │ └── route.ts
│ ├── chat
│ │ └── route.ts
│ └── webhook.ts
├── chat
│ └── page.tsx
├── globals.css
├── layout.tsx
└── page.tsx
├── components.json
├── components
├── BlurredClipPath.tsx
├── CompanyLogos.tsx
├── DownloadPrompt.tsx
├── Footer.tsx
├── HandleAuth.tsx
├── Header.tsx
├── Hero.tsx
├── NavbarSide.tsx
├── Pricing.tsx
├── PricingButtonOptions.tsx
├── Testimonials.tsx
├── ThemeProvider.tsx
├── ThemeToggle.tsx
└── ui
│ ├── avatar.tsx
│ ├── badge.tsx
│ ├── button.tsx
│ ├── card.tsx
│ ├── dropdown-menu.tsx
│ ├── input.tsx
│ ├── separator.tsx
│ └── sheet.tsx
├── config
└── index.ts
├── lib
├── authOptions.ts
├── formatCurrency.ts
└── utils.ts
├── next.config.mjs
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
├── abbysmith.jpg
├── app-screenshot.png
├── craignelson.jpg
├── johndoe.jpg
└── nuro-logo.png
├── tailwind.config.ts
└── tsconfig.json
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | .env
5 | /node_modules
6 | /.pnp
7 | .pnp.js
8 | .yarn/install-state.gz
9 |
10 | # testing
11 | /coverage
12 |
13 | # next.js
14 | /.next/
15 | /out/
16 |
17 | # production
18 | /build
19 |
20 | # misc
21 | .DS_Store
22 | *.pem
23 |
24 | # debug
25 | npm-debug.log*
26 | yarn-debug.log*
27 | yarn-error.log*
28 |
29 | # local env files
30 | .env*.local
31 |
32 | # vercel
33 | .vercel
34 |
35 | # typescript
36 | *.tsbuildinfo
37 | next-env.d.ts
38 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | }
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Nuro AI
2 |
3 | Nuro AI is a AI chatbot SaaS that uses Open AI and GPT-3. Users can communicate freely with Chat GPT and gather information by asking questions. Users can also login thorugh the use of NextAuth.js, implementing a Google provider to allow users to sign in with their Gmail account. A full Stripe payment process is also added, allowing users to choose a monthly plan.
4 |
5 | 
6 |
7 | ## Getting Started
8 |
9 | FIrst, clone the repository and install the necessary dependencies:
10 |
11 | ```bash
12 | npm install
13 | # or
14 | yarn install
15 | # or
16 | pnpm install
17 | ```
18 |
19 | Then run the development server:
20 |
21 | ```bash
22 | npm run dev
23 | # or
24 | yarn dev
25 | # or
26 | pnpm dev
27 | ```
28 |
29 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
30 |
31 |
--------------------------------------------------------------------------------
/app/api/auth/[...nextauth]/route.ts:
--------------------------------------------------------------------------------
1 | import { authOptions } from "@/lib/authOptions";
2 | import NextAuth from "next-auth/next";
3 |
4 | const handler = NextAuth(authOptions);
5 | export { handler as GET, handler as POST };
6 |
--------------------------------------------------------------------------------
/app/api/chat/route.ts:
--------------------------------------------------------------------------------
1 | import { OpenAIStream, StreamingTextResponse } from "ai";
2 | import { Configuration, OpenAIApi } from "openai-edge";
3 |
4 | export const runtime = "edge";
5 |
6 | const config = new Configuration({
7 | apiKey: process.env.OPENAI_KEY,
8 | });
9 |
10 | const openai = new OpenAIApi(config);
11 |
12 | export async function POST(req: Request) {
13 | const body = await req.json();
14 | const { messages } = body;
15 |
16 | //chat streaming completion
17 | const response = await openai.createChatCompletion({
18 | model: "gpt-3.5-turbo",
19 | messages,
20 | stream: true,
21 | });
22 |
23 | const stream = OpenAIStream(response);
24 | return new StreamingTextResponse(stream);
25 | }
26 |
--------------------------------------------------------------------------------
/app/api/webhook.ts:
--------------------------------------------------------------------------------
1 | import type { NextApiRequest, NextApiResponse } from "next";
2 | import getRawBody from "raw-body";
3 | import Stripe from "stripe";
4 |
5 | const stripe = new Stripe(process.env.STRIPE_SECRET_KEY as string, {
6 | apiVersion: "2023-10-16",
7 | });
8 |
9 | const endpointSecret = process.env.WEBHOOK_SECRET as string;
10 |
11 | // included to avoid 'stream.not.readable' error
12 | export const config = {
13 | api: {
14 | bodyParser: false,
15 | },
16 | };
17 |
18 | export default async function handler(
19 | req: NextApiRequest,
20 | res: NextApiResponse
21 | ) {
22 | try {
23 | console.log("req.headers", req.headers);
24 | if (req.method !== "POST") {
25 | return res.status(405).send("Must send POST request only");
26 | }
27 |
28 | const sig: any = req.headers["stripe-signature"];
29 | const rawBody = await getRawBody(req);
30 |
31 | let event;
32 |
33 | try {
34 | event = stripe.webhooks.constructEvent(rawBody, sig, endpointSecret);
35 | } catch (error: any) {
36 | return res.status(400).send(`Webhook Error: ${error.message}`);
37 | }
38 |
39 | console.log("event.type", JSON.stringify(event.type));
40 |
41 | if (event.type === "checkout.session.completed") {
42 | const sessionWithLineItems = await stripe.checkout.sessions.retrieve(
43 | (event.data.object as any).id,
44 | {
45 | expand: ["line_items"],
46 | }
47 | );
48 | const lineItems = sessionWithLineItems.line_items;
49 |
50 | if (!lineItems) return res.status(500).send("Internal Server Error");
51 |
52 | try {
53 | //save the data
54 | console.log("Order Fulfilled");
55 | console.log("data", lineItems.data);
56 | console.log(
57 | "Customer Email",
58 | (event.data.object as any).customer_details.email
59 | );
60 | console.log("Created", (event.data.object as any).created);
61 | } catch (error) {
62 | console.log("Unable to save order");
63 | }
64 | }
65 | res.status(200).end();
66 | } catch (error) {
67 | console.error(error);
68 | res.status(500).json("Internal Server Error");
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/app/chat/page.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { Button } from "@/components/ui/button";
4 | import { Input } from "@/components/ui/input";
5 | import { ArrowRightIcon, ComponentBooleanIcon } from "@radix-ui/react-icons";
6 | import { useChat } from "ai/react";
7 |
8 | export default function ChatBot() {
9 | const { messages, input, handleInputChange, handleSubmit } = useChat();
10 | return (
11 |
12 |
13 |
14 | Start your conversation with Nuro.
15 |
16 |
17 | With Nuro, you can enjoy smart, friendly, and convenient communication
18 | anytime, anywhere.
19 |
20 |
21 |
22 |
23 |
24 |
25 | Hello, ask me anything!
26 |
27 |
28 |
29 | {messages.map((message) => (
30 |
36 |
37 | {message.role === "assistant" && (
38 |
39 | )}
40 | {message.content}
41 |
42 |
43 | ))}
44 |
59 |
60 | );
61 | }
62 |
--------------------------------------------------------------------------------
/app/globals.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | @layer base {
6 | :root {
7 | --background: 0 0% 100%;
8 | --foreground: 0 0% 3.9%;
9 |
10 | --card: 0 0% 100%;
11 | --card-foreground: 0 0% 3.9%;
12 |
13 | --popover: 0 0% 100%;
14 | --popover-foreground: 0 0% 3.9%;
15 |
16 | --primary: 0 0% 9%;
17 | --primary-foreground: 0 0% 98%;
18 |
19 | --secondary: 0 0% 96.1%;
20 | --secondary-foreground: 0 0% 9%;
21 |
22 | --muted: 0 0% 96.1%;
23 | --muted-foreground: 0 0% 45.1%;
24 |
25 | --accent: 0 0% 96.1%;
26 | --accent-foreground: 0 0% 9%;
27 |
28 | --destructive: 0 84.2% 60.2%;
29 | --destructive-foreground: 0 0% 98%;
30 |
31 | --border: 0 0% 89.8%;
32 | --input: 0 0% 89.8%;
33 | --ring: 0 0% 3.9%;
34 |
35 | --radius: 0.5rem;
36 | }
37 |
38 | .dark {
39 | --background: 0 0% 3.9%;
40 | --foreground: 0 0% 98%;
41 |
42 | --card: 0 0% 3.9%;
43 | --card-foreground: 0 0% 98%;
44 |
45 | --popover: 0 0% 3.9%;
46 | --popover-foreground: 0 0% 98%;
47 |
48 | --primary: 0 0% 98%;
49 | --primary-foreground: 0 0% 9%;
50 |
51 | --secondary: 0 0% 14.9%;
52 | --secondary-foreground: 0 0% 98%;
53 |
54 | --muted: 0 0% 14.9%;
55 | --muted-foreground: 0 0% 63.9%;
56 |
57 | --accent: 0 0% 14.9%;
58 | --accent-foreground: 0 0% 98%;
59 |
60 | --destructive: 0 62.8% 30.6%;
61 | --destructive-foreground: 0 0% 98%;
62 |
63 | --border: 0 0% 14.9%;
64 | --input: 0 0% 14.9%;
65 | --ring: 0 0% 83.1%;
66 | }
67 | }
68 |
69 | @layer base {
70 | * {
71 | @apply border-border;
72 | }
73 | body {
74 | @apply bg-background text-foreground;
75 | }
76 | svg {
77 | @apply fill-foreground/60 hover:fill-foreground transition-all;
78 | }
79 | }
80 |
81 | @layer utilities {
82 | .container {
83 | @apply max-w-screen-2xl m-auto;
84 | }
85 | }
86 |
87 | .no-scrollbar {
88 | -ms-overflow-style: none; /* Internet Explorer 10+ */
89 | scrollbar-width: none; /* Firefox */
90 | }
91 | .no-scrollbar::-webkit-scrollbar {
92 | display: none; /* Safari and Chrome */
93 | }
94 |
--------------------------------------------------------------------------------
/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import Header from "@/components/Header";
2 | import { ThemeProvider } from "@/components/ThemeProvider";
3 | import type { Metadata } from "next";
4 | import { Inter } from "next/font/google";
5 | import "./globals.css";
6 |
7 | const inter = Inter({ subsets: ["latin"] });
8 |
9 | export const metadata: Metadata = {
10 | title: "Nuro — AI ChatBot",
11 | description: "An SaaS website showcasing OpenAI",
12 | icons: {
13 | icon: "/nuro-logo.png",
14 | },
15 | };
16 |
17 | export default function RootLayout({
18 | children,
19 | }: Readonly<{
20 | children: React.ReactNode;
21 | }>) {
22 | return (
23 |
24 |
25 |
31 |
32 | {children}
33 |
34 |
35 |
36 | );
37 | }
38 |
--------------------------------------------------------------------------------
/app/page.tsx:
--------------------------------------------------------------------------------
1 | import DownloadPrompt from "@/components/DownloadPrompt";
2 | import Footer from "@/components/Footer";
3 | import Hero from "@/components/Hero";
4 | import Pricing from "@/components/Pricing";
5 | import Testimonials from "@/components/Testimonials";
6 |
7 | export default function Home() {
8 | return (
9 | <>
10 |
11 |
12 |
13 |
14 |
15 | >
16 | );
17 | }
18 |
--------------------------------------------------------------------------------
/components.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://ui.shadcn.com/schema.json",
3 | "style": "new-york",
4 | "rsc": true,
5 | "tsx": true,
6 | "tailwind": {
7 | "config": "tailwind.config.ts",
8 | "css": "app/globals.css",
9 | "baseColor": "neutral",
10 | "cssVariables": true,
11 | "prefix": ""
12 | },
13 | "aliases": {
14 | "components": "@/components",
15 | "utils": "@/lib/utils"
16 | }
17 | }
--------------------------------------------------------------------------------
/components/BlurredClipPath.tsx:
--------------------------------------------------------------------------------
1 | interface BlurredClipPathProps {
2 | children?: React.ReactNode;
3 | paddingBottom?: string;
4 | top?: string;
5 | smallSizeTop?: string;
6 | translateX?: string;
7 | rotateDeg?: string;
8 | width?: string;
9 | smallSizeWidth?: string;
10 | left?: string;
11 | smallSizeLeft?: string;
12 | }
13 |
14 | export default function BlurredClipPath({
15 | children,
16 | paddingBottom,
17 | top,
18 | smallSizeTop,
19 | translateX,
20 | rotateDeg,
21 | width,
22 | smallSizeWidth,
23 | left,
24 | smallSizeLeft,
25 | }: BlurredClipPathProps) {
26 | return (
27 |
28 |
40 | {children && children}
41 |
42 | );
43 | }
44 |
--------------------------------------------------------------------------------
/components/CompanyLogos.tsx:
--------------------------------------------------------------------------------
1 | export default function CompanyLogos() {
2 | return (
3 |
4 |
17 |
25 |
33 |
49 |
60 |
61 | );
62 | }
63 |
--------------------------------------------------------------------------------
/components/DownloadPrompt.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { ArrowRightIcon, ComponentBooleanIcon } from "@radix-ui/react-icons";
4 | import { motion } from "framer-motion";
5 | import Link from "next/link";
6 | import BlurredClipPath from "./BlurredClipPath";
7 | import { Button } from "./ui/button";
8 |
9 | export default function DownloadPrompt() {
10 | return (
11 | <>
12 |
13 |
20 |
21 |
22 | Start your creative endeavours with Nuro today.
23 |
24 |
25 |
26 |
29 |
30 |
31 | Want to talk or get a demo.{" "}
32 |
36 | Get in touch.
37 |
38 |
39 |
40 |
41 |
42 |
50 | >
51 | );
52 | }
53 |
--------------------------------------------------------------------------------
/components/Footer.tsx:
--------------------------------------------------------------------------------
1 | import { FOOTER_LINKS } from "@/config";
2 | import {
3 | ComponentBooleanIcon,
4 | GitHubLogoIcon,
5 | InstagramLogoIcon,
6 | LinkedInLogoIcon,
7 | TwitterLogoIcon,
8 | } from "@radix-ui/react-icons";
9 | import Link from "next/link";
10 | import { Separator } from "./ui/separator";
11 |
12 | export default function Footer() {
13 | return (
14 |
15 |
16 |
17 | {FOOTER_LINKS.map((footer) => (
18 |
22 |
23 | {footer.title}
24 |
25 | {footer.links.map((link) => (
26 |
27 | {link.name}
28 |
29 | ))}
30 |
31 | ))}
32 |
33 |
34 |
35 |
36 |
37 | © Copyright 2024. All rights reserved.
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | );
49 | }
50 |
--------------------------------------------------------------------------------
/components/HandleAuth.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { ExitIcon, PersonIcon } from "@radix-ui/react-icons";
4 | import { signIn, signOut } from "next-auth/react";
5 | import { Avatar, AvatarFallback, AvatarImage } from "./ui/avatar";
6 | import { Button } from "./ui/button";
7 | import {
8 | DropdownMenu,
9 | DropdownMenuContent,
10 | DropdownMenuItem,
11 | DropdownMenuLabel,
12 | DropdownMenuSeparator,
13 | DropdownMenuTrigger,
14 | } from "./ui/dropdown-menu";
15 |
16 | export function Login() {
17 | const handleClick = () => {
18 | signIn("google");
19 | };
20 | return (
21 | <>
22 |
23 | >
24 | );
25 | }
26 |
27 | interface SessionProps {
28 | session: any;
29 | }
30 |
31 | export function Logout({ session }: SessionProps) {
32 | const handleClick = () => {
33 | signOut();
34 | };
35 | return (
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | {session?.user?.name}
48 |
49 | {session?.user?.email}
50 |
51 |
52 |
53 |
54 | Sign Out
55 |
56 |
57 |
58 | );
59 | }
60 |
61 | export function SignInPricingButton() {
62 | const handleClick = () => {
63 | signIn("google");
64 | };
65 | return (
66 |
69 | );
70 | }
71 |
--------------------------------------------------------------------------------
/components/Header.tsx:
--------------------------------------------------------------------------------
1 | import { authOptions } from "@/lib/authOptions";
2 | import { ComponentBooleanIcon } from "@radix-ui/react-icons";
3 | import { getServerSession } from "next-auth";
4 | import Link from "next/link";
5 | import { Login, Logout } from "./HandleAuth";
6 | import NavbarSide from "./NavbarSide";
7 | import { ModeToggle } from "./ThemeToggle";
8 |
9 | export default async function Header() {
10 | const session = await getServerSession(authOptions);
11 | return (
12 | <>
13 |
14 |
15 | Free trials are available for Standard and Essentials plans.{" "}
16 |
20 | Start for free today.
21 |
22 |
23 |
24 |
56 | >
57 | );
58 | }
59 |
--------------------------------------------------------------------------------
/components/Hero.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { ArrowRightIcon, GitHubLogoIcon } from "@radix-ui/react-icons";
4 | import { motion } from "framer-motion";
5 | import Image from "next/image";
6 | import Link from "next/link";
7 | import BlurredClipPath from "./BlurredClipPath";
8 | import { Badge } from "./ui/badge";
9 | import { Button } from "./ui/button";
10 |
11 | export default function Hero() {
12 | return (
13 | <>
14 |
15 |
21 |
25 | Find you answers, all in one space.
26 |
27 |
28 |
34 |
35 | Your answer is just one question away.
36 |
37 |
38 |
44 |
45 | Nuro allows you to have conversations with our ChatBot. Simply ask a
46 | question and watch the magic happen.
47 |
48 |
49 |
56 |
57 |
60 |
61 |
62 |
69 |
70 |
71 |
77 |
78 | Nuro works wherever you are.{" "}
79 |
83 | Try our demo.
84 |
85 |
86 |
87 |
88 |
99 |
105 |
119 |
120 |
121 | >
122 | );
123 | }
124 |
--------------------------------------------------------------------------------
/components/NavbarSide.tsx:
--------------------------------------------------------------------------------
1 | import { HamburgerMenuIcon } from "@radix-ui/react-icons";
2 | import Link from "next/link";
3 | import {
4 | Sheet,
5 | SheetContent,
6 | SheetDescription,
7 | SheetHeader,
8 | SheetTrigger,
9 | } from "./ui/sheet";
10 |
11 | export default function NavbarSide() {
12 | return (
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
24 | Demo
25 |
26 |
31 | Code
32 |
33 |
34 |
35 |
36 |
37 | );
38 | }
39 |
--------------------------------------------------------------------------------
/components/Pricing.tsx:
--------------------------------------------------------------------------------
1 | import { PRICING } from "@/config";
2 | import { formatCurrency } from "@/lib/formatCurrency";
3 | import { CheckIcon } from "@radix-ui/react-icons";
4 | import PricingButtonOptions from "./PricingButtonOptions";
5 | import { Badge } from "./ui/badge";
6 | import {
7 | Card,
8 | CardContent,
9 | CardFooter,
10 | CardHeader,
11 | CardTitle,
12 | } from "./ui/card";
13 |
14 | export default function Pricing() {
15 | return (
16 |
17 |
21 | {/* eslint-disable-next-line react/no-unescaped-entities */}
22 | Choose the plan that's right for you.
23 |
24 |
25 | Our straightforward pricing plans are tailored to meet your needs.
26 |
27 |
28 |
29 | {PRICING.map((option) => (
30 |
37 | {option.name === "Enterprise" && (
38 |
39 | Best Value
40 |
41 | )}
42 |
43 |
44 |
45 | {option.name}
46 |
47 |
48 |
49 |
50 | {option.subhead}
51 |
52 |
53 | {formatCurrency(option.pricing)}
54 |
55 | / month
56 |
57 |
58 |
59 | {option.benefits.map((benefit) => (
60 |
64 | ))}
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | ))}
73 |
74 |
75 |
76 | );
77 | }
78 |
--------------------------------------------------------------------------------
/components/PricingButtonOptions.tsx:
--------------------------------------------------------------------------------
1 | import { authOptions } from "@/lib/authOptions";
2 | import { getServerSession } from "next-auth";
3 | import Link from "next/link";
4 | import { SignInPricingButton } from "./HandleAuth";
5 | import { Button } from "./ui/button";
6 |
7 | interface PricingButtonOptionsProps {
8 | name: string;
9 | }
10 |
11 | export default async function PricingButtonOptions({
12 | name,
13 | }: PricingButtonOptionsProps) {
14 | const session = await getServerSession(authOptions);
15 | return (
16 | <>
17 | {session ? (
18 | <>
19 | {name === "Enterprise" && (
20 |
24 |
27 |
28 | )}
29 | {name === "Professional" && (
30 |
34 |
37 |
38 | )}
39 | {name === "Standard" && (
40 |
44 |
47 |
48 | )}
49 | >
50 | ) : (
51 |
52 | )}
53 | >
54 | );
55 | }
56 |
--------------------------------------------------------------------------------
/components/Testimonials.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { TESTIMONIALS } from "@/config";
4 | import { motion } from "framer-motion";
5 | import BlurredClipPath from "./BlurredClipPath";
6 | import CompanyLogos from "./CompanyLogos";
7 | import { Avatar, AvatarImage } from "./ui/avatar";
8 | import { Badge } from "./ui/badge";
9 | import {
10 | Card,
11 | CardContent,
12 | CardFooter,
13 | CardHeader,
14 | CardTitle,
15 | } from "./ui/card";
16 |
17 | export default function Testimonials() {
18 | return (
19 | <>
20 |
21 |
27 |
31 | Improve your workflow.
32 |
33 |
34 |
40 |
41 | Nuro lets people move faster, save time and create higher quality
42 | work.
43 |
44 |
45 |
52 |
53 | {TESTIMONIALS.map((review) => (
54 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | {review.review}
69 |
70 |
71 |
72 |
73 | {review.name}
74 | {review.occupation}
75 |
76 |
77 | ))}
78 |
79 |
80 |
81 |
90 |
96 |
97 |
98 | >
99 | );
100 | }
101 |
--------------------------------------------------------------------------------
/components/ThemeProvider.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { ThemeProvider as NextThemesProvider } from "next-themes";
4 | import { type ThemeProviderProps } from "next-themes/dist/types";
5 |
6 | export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
7 | return {children};
8 | }
9 |
--------------------------------------------------------------------------------
/components/ThemeToggle.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { MoonIcon, SunIcon } from "@radix-ui/react-icons";
4 | import { useTheme } from "next-themes";
5 |
6 | import { Button } from "@/components/ui/button";
7 | import {
8 | DropdownMenu,
9 | DropdownMenuContent,
10 | DropdownMenuItem,
11 | DropdownMenuTrigger,
12 | } from "@/components/ui/dropdown-menu";
13 |
14 | export function ModeToggle() {
15 | const { setTheme } = useTheme();
16 |
17 | return (
18 |
19 |
20 |
25 |
26 |
27 | setTheme("light")}>
28 | Light
29 |
30 | setTheme("dark")}>
31 | Dark
32 |
33 |
34 |
35 | );
36 | }
37 |
--------------------------------------------------------------------------------
/components/ui/avatar.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as AvatarPrimitive from "@radix-ui/react-avatar"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Avatar = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(({ className, ...props }, ref) => (
12 |
20 | ))
21 | Avatar.displayName = AvatarPrimitive.Root.displayName
22 |
23 | const AvatarImage = React.forwardRef<
24 | React.ElementRef,
25 | React.ComponentPropsWithoutRef
26 | >(({ className, ...props }, ref) => (
27 |
32 | ))
33 | AvatarImage.displayName = AvatarPrimitive.Image.displayName
34 |
35 | const AvatarFallback = React.forwardRef<
36 | React.ElementRef,
37 | React.ComponentPropsWithoutRef
38 | >(({ className, ...props }, ref) => (
39 |
47 | ))
48 | AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
49 |
50 | export { Avatar, AvatarImage, AvatarFallback }
51 |
--------------------------------------------------------------------------------
/components/ui/badge.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { cva, type VariantProps } from "class-variance-authority";
3 |
4 | import { cn } from "@/lib/utils";
5 |
6 | const badgeVariants = cva(
7 | "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
8 | {
9 | variants: {
10 | variant: {
11 | default:
12 | "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
13 | secondary:
14 | "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
15 | destructive:
16 | "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
17 | outline: "text-foreground",
18 | },
19 | },
20 | defaultVariants: {
21 | variant: "default",
22 | },
23 | }
24 | );
25 |
26 | export interface BadgeProps
27 | extends React.HTMLAttributes,
28 | VariantProps {}
29 |
30 | function Badge({ className, variant, ...props }: BadgeProps) {
31 | return (
32 |
33 | );
34 | }
35 |
36 | export { Badge, badgeVariants };
37 |
--------------------------------------------------------------------------------
/components/ui/button.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react";
2 | import { Slot } from "@radix-ui/react-slot";
3 | import { cva, type VariantProps } from "class-variance-authority";
4 |
5 | import { cn } from "@/lib/utils";
6 |
7 | const buttonVariants = cva(
8 | "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
9 | {
10 | variants: {
11 | variant: {
12 | default:
13 | "bg-primary text-primary-foreground shadow hover:bg-primary/90",
14 | destructive:
15 | "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
16 | outline:
17 | "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
18 | secondary:
19 | "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
20 | ghost: "hover:bg-accent hover:text-accent-foreground",
21 | link: "text-primary underline-offset-4 hover:underline",
22 | },
23 | size: {
24 | default: "h-9 px-4 py-2",
25 | sm: "h-8 rounded-md px-3 text-xs",
26 | lg: "h-10 rounded-md px-8",
27 | icon: "h-9 w-9",
28 | },
29 | },
30 | defaultVariants: {
31 | variant: "default",
32 | size: "default",
33 | },
34 | }
35 | );
36 |
37 | export interface ButtonProps
38 | extends React.ButtonHTMLAttributes,
39 | VariantProps {
40 | asChild?: boolean;
41 | }
42 |
43 | const Button = React.forwardRef(
44 | ({ className, variant, size, asChild = false, ...props }, ref) => {
45 | const Comp = asChild ? Slot : "button";
46 | return (
47 |
52 | );
53 | }
54 | );
55 | Button.displayName = "Button";
56 |
57 | export { Button, buttonVariants };
58 |
--------------------------------------------------------------------------------
/components/ui/card.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 |
3 | import { cn } from "@/lib/utils"
4 |
5 | const Card = React.forwardRef<
6 | HTMLDivElement,
7 | React.HTMLAttributes
8 | >(({ className, ...props }, ref) => (
9 |
17 | ))
18 | Card.displayName = "Card"
19 |
20 | const CardHeader = React.forwardRef<
21 | HTMLDivElement,
22 | React.HTMLAttributes
23 | >(({ className, ...props }, ref) => (
24 |
29 | ))
30 | CardHeader.displayName = "CardHeader"
31 |
32 | const CardTitle = React.forwardRef<
33 | HTMLParagraphElement,
34 | React.HTMLAttributes
35 | >(({ className, ...props }, ref) => (
36 |
41 | ))
42 | CardTitle.displayName = "CardTitle"
43 |
44 | const CardDescription = React.forwardRef<
45 | HTMLParagraphElement,
46 | React.HTMLAttributes
47 | >(({ className, ...props }, ref) => (
48 |
53 | ))
54 | CardDescription.displayName = "CardDescription"
55 |
56 | const CardContent = React.forwardRef<
57 | HTMLDivElement,
58 | React.HTMLAttributes
59 | >(({ className, ...props }, ref) => (
60 |
61 | ))
62 | CardContent.displayName = "CardContent"
63 |
64 | const CardFooter = React.forwardRef<
65 | HTMLDivElement,
66 | React.HTMLAttributes
67 | >(({ className, ...props }, ref) => (
68 |
73 | ))
74 | CardFooter.displayName = "CardFooter"
75 |
76 | export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
77 |
--------------------------------------------------------------------------------
/components/ui/dropdown-menu.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import * as React from "react";
4 | import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
5 | import {
6 | CheckIcon,
7 | ChevronRightIcon,
8 | DotFilledIcon,
9 | } from "@radix-ui/react-icons";
10 |
11 | import { cn } from "@/lib/utils";
12 |
13 | const DropdownMenu = DropdownMenuPrimitive.Root;
14 |
15 | const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
16 |
17 | const DropdownMenuGroup = DropdownMenuPrimitive.Group;
18 |
19 | const DropdownMenuPortal = DropdownMenuPrimitive.Portal;
20 |
21 | const DropdownMenuSub = DropdownMenuPrimitive.Sub;
22 |
23 | const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
24 |
25 | const DropdownMenuSubTrigger = React.forwardRef<
26 | React.ElementRef,
27 | React.ComponentPropsWithoutRef & {
28 | inset?: boolean;
29 | }
30 | >(({ className, inset, children, ...props }, ref) => (
31 |
40 | {children}
41 |
42 |
43 | ));
44 | DropdownMenuSubTrigger.displayName =
45 | DropdownMenuPrimitive.SubTrigger.displayName;
46 |
47 | const DropdownMenuSubContent = React.forwardRef<
48 | React.ElementRef,
49 | React.ComponentPropsWithoutRef
50 | >(({ className, ...props }, ref) => (
51 |
59 | ));
60 | DropdownMenuSubContent.displayName =
61 | DropdownMenuPrimitive.SubContent.displayName;
62 |
63 | const DropdownMenuContent = React.forwardRef<
64 | React.ElementRef,
65 | React.ComponentPropsWithoutRef
66 | >(({ className, sideOffset = 4, ...props }, ref) => (
67 |
68 |
78 |
79 | ));
80 | DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
81 |
82 | const DropdownMenuItem = React.forwardRef<
83 | React.ElementRef,
84 | React.ComponentPropsWithoutRef & {
85 | inset?: boolean;
86 | }
87 | >(({ className, inset, ...props }, ref) => (
88 |
97 | ));
98 | DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
99 |
100 | const DropdownMenuCheckboxItem = React.forwardRef<
101 | React.ElementRef,
102 | React.ComponentPropsWithoutRef
103 | >(({ className, children, checked, ...props }, ref) => (
104 |
113 |
114 |
115 |
116 |
117 |
118 | {children}
119 |
120 | ));
121 | DropdownMenuCheckboxItem.displayName =
122 | DropdownMenuPrimitive.CheckboxItem.displayName;
123 |
124 | const DropdownMenuRadioItem = React.forwardRef<
125 | React.ElementRef,
126 | React.ComponentPropsWithoutRef
127 | >(({ className, children, ...props }, ref) => (
128 |
136 |
137 |
138 |
139 |
140 |
141 | {children}
142 |
143 | ));
144 | DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
145 |
146 | const DropdownMenuLabel = React.forwardRef<
147 | React.ElementRef,
148 | React.ComponentPropsWithoutRef & {
149 | inset?: boolean;
150 | }
151 | >(({ className, inset, ...props }, ref) => (
152 |
161 | ));
162 | DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
163 |
164 | const DropdownMenuSeparator = React.forwardRef<
165 | React.ElementRef,
166 | React.ComponentPropsWithoutRef
167 | >(({ className, ...props }, ref) => (
168 |
173 | ));
174 | DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
175 |
176 | const DropdownMenuShortcut = ({
177 | className,
178 | ...props
179 | }: React.HTMLAttributes) => {
180 | return (
181 |
185 | );
186 | };
187 | DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
188 |
189 | export {
190 | DropdownMenu,
191 | DropdownMenuTrigger,
192 | DropdownMenuContent,
193 | DropdownMenuItem,
194 | DropdownMenuCheckboxItem,
195 | DropdownMenuRadioItem,
196 | DropdownMenuLabel,
197 | DropdownMenuSeparator,
198 | DropdownMenuShortcut,
199 | DropdownMenuGroup,
200 | DropdownMenuPortal,
201 | DropdownMenuSub,
202 | DropdownMenuSubContent,
203 | DropdownMenuSubTrigger,
204 | DropdownMenuRadioGroup,
205 | };
206 |
--------------------------------------------------------------------------------
/components/ui/input.tsx:
--------------------------------------------------------------------------------
1 | import * as React from "react"
2 |
3 | import { cn } from "@/lib/utils"
4 |
5 | export interface InputProps
6 | extends React.InputHTMLAttributes {}
7 |
8 | const Input = React.forwardRef(
9 | ({ className, type, ...props }, ref) => {
10 | return (
11 |
20 | )
21 | }
22 | )
23 | Input.displayName = "Input"
24 |
25 | export { Input }
26 |
--------------------------------------------------------------------------------
/components/ui/separator.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as SeparatorPrimitive from "@radix-ui/react-separator"
5 |
6 | import { cn } from "@/lib/utils"
7 |
8 | const Separator = React.forwardRef<
9 | React.ElementRef,
10 | React.ComponentPropsWithoutRef
11 | >(
12 | (
13 | { className, orientation = "horizontal", decorative = true, ...props },
14 | ref
15 | ) => (
16 |
27 | )
28 | )
29 | Separator.displayName = SeparatorPrimitive.Root.displayName
30 |
31 | export { Separator }
32 |
--------------------------------------------------------------------------------
/components/ui/sheet.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as SheetPrimitive from "@radix-ui/react-dialog"
5 | import { Cross2Icon } from "@radix-ui/react-icons"
6 | import { cva, type VariantProps } from "class-variance-authority"
7 |
8 | import { cn } from "@/lib/utils"
9 |
10 | const Sheet = SheetPrimitive.Root
11 |
12 | const SheetTrigger = SheetPrimitive.Trigger
13 |
14 | const SheetClose = SheetPrimitive.Close
15 |
16 | const SheetPortal = SheetPrimitive.Portal
17 |
18 | const SheetOverlay = React.forwardRef<
19 | React.ElementRef,
20 | React.ComponentPropsWithoutRef
21 | >(({ className, ...props }, ref) => (
22 |
30 | ))
31 | SheetOverlay.displayName = SheetPrimitive.Overlay.displayName
32 |
33 | const sheetVariants = cva(
34 | "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
35 | {
36 | variants: {
37 | side: {
38 | top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
39 | bottom:
40 | "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
41 | left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
42 | right:
43 | "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
44 | },
45 | },
46 | defaultVariants: {
47 | side: "right",
48 | },
49 | }
50 | )
51 |
52 | interface SheetContentProps
53 | extends React.ComponentPropsWithoutRef,
54 | VariantProps {}
55 |
56 | const SheetContent = React.forwardRef<
57 | React.ElementRef,
58 | SheetContentProps
59 | >(({ side = "right", className, children, ...props }, ref) => (
60 |
61 |
62 |
67 | {children}
68 |
69 |
70 | Close
71 |
72 |
73 |
74 | ))
75 | SheetContent.displayName = SheetPrimitive.Content.displayName
76 |
77 | const SheetHeader = ({
78 | className,
79 | ...props
80 | }: React.HTMLAttributes) => (
81 |
88 | )
89 | SheetHeader.displayName = "SheetHeader"
90 |
91 | const SheetFooter = ({
92 | className,
93 | ...props
94 | }: React.HTMLAttributes) => (
95 |
102 | )
103 | SheetFooter.displayName = "SheetFooter"
104 |
105 | const SheetTitle = React.forwardRef<
106 | React.ElementRef,
107 | React.ComponentPropsWithoutRef
108 | >(({ className, ...props }, ref) => (
109 |
114 | ))
115 | SheetTitle.displayName = SheetPrimitive.Title.displayName
116 |
117 | const SheetDescription = React.forwardRef<
118 | React.ElementRef,
119 | React.ComponentPropsWithoutRef
120 | >(({ className, ...props }, ref) => (
121 |
126 | ))
127 | SheetDescription.displayName = SheetPrimitive.Description.displayName
128 |
129 | export {
130 | Sheet,
131 | SheetPortal,
132 | SheetOverlay,
133 | SheetTrigger,
134 | SheetClose,
135 | SheetContent,
136 | SheetHeader,
137 | SheetFooter,
138 | SheetTitle,
139 | SheetDescription,
140 | }
141 |
--------------------------------------------------------------------------------
/config/index.ts:
--------------------------------------------------------------------------------
1 | export const TESTIMONIALS = [
2 | {
3 | imgSrc: "/johndoe.jpg",
4 | name: "John Doe",
5 | occupation: "Creative Writer • Freelance",
6 | review:
7 | '"I’ve had the pleasure of using Nuro, and what truly stood out was its natural conversational flow. Unlike other chatbots, Nuro seamlessly adapts to different contexts, making interactions feel like genuine conversations. It’s like having a knowledgeable friend on standby."',
8 | },
9 | {
10 | imgSrc: "/abbysmith.jpg",
11 | name: "Abby Smith",
12 | occupation: "Lead Project Manager • Google",
13 | review:
14 | '"Nuro seamlessly adjusts to various contexts, making interactions feel like genuine conversations. It effortlessly tailors its responses, ensuring a personalized experience for users."',
15 | },
16 | {
17 | imgSrc: "/craignelson.jpg",
18 | name: "Craig Nelson",
19 | occupation: "Sales Executive • Apple Inc.",
20 | review:
21 | '"As a user of Nuro, I’ve experienced its remarkable ability across diverse contexts, imbuing interactions with an authentic conversational quality. Its responses flow effortlessly, customizing each engagement to deliver a uniquely personalized user experience."',
22 | },
23 | ];
24 |
25 | export const PRICING = [
26 | {
27 | name: "Professional",
28 | subhead: "Perfect for owners and businesses that need creative solutions.",
29 | pricing: 25.95,
30 | benefits: [
31 | "All basic chatbot features plus advanced features: GPT-4, images, plugins, analytics, integration, etc.",
32 | "Unlimited uses of advanced features with no monthly limit or quota.",
33 | "Faster response times and lower latency.",
34 | ],
35 | },
36 | {
37 | name: "Enterprise",
38 | subhead:
39 | "The ultimate AI companion for your creative needs and aspirations.",
40 | pricing: 42.95,
41 | benefits: [
42 | "All basic and pro chatbot features plus exclusive access to GPT-4 and tools like DALL·E, Browsing, Advanced Data Analysis, etc.",
43 | "Unlimited and high-speed usage of all features with no monthly limit or quota.",
44 | "Custom data retention and security policies to protect your company data.",
45 | "Custom integrations and deployment options for your preferred channels and platforms.",
46 | ],
47 | },
48 | {
49 | name: "Standard",
50 | subhead:
51 | "Essential features for successful AI chatbot interactions and functionality.",
52 | pricing: 0,
53 | benefits: [
54 | "Basic chatbot features: text, questions, conversations.",
55 | "Community support and feedback.",
56 | ],
57 | },
58 | ];
59 |
60 | export const FOOTER_LINKS = [
61 | {
62 | title: "Product",
63 | links: [
64 | { name: "Using Nuro" },
65 | { name: "Blog" },
66 | { name: "Learn" },
67 | { name: "Pricing" },
68 | { name: "Download" },
69 | ],
70 | },
71 | {
72 | title: "Customers",
73 | links: [
74 | { name: "Showcase" },
75 | { name: "Developers" },
76 | { name: "Marketers" },
77 | { name: "Agencies & Freelancers" },
78 | { name: "Designers" },
79 | ],
80 | },
81 | {
82 | title: "Resources",
83 | links: [{ name: "Docs" }, { name: "Tutorials" }, { name: "Community" }],
84 | },
85 | {
86 | title: "Company",
87 | links: [
88 | { name: "Careers" },
89 | { name: "News" },
90 | { name: "Terms & Conditions" },
91 | { name: "Privacy Policy" },
92 | { name: "Copyright Dispute" },
93 | ],
94 | },
95 | {
96 | title: "Contact",
97 | links: [{ name: "support@nuro.com" }, { name: "(+44) 0191 4242 4242" }],
98 | },
99 | ];
100 |
--------------------------------------------------------------------------------
/lib/authOptions.ts:
--------------------------------------------------------------------------------
1 | import { NextAuthOptions } from "next-auth";
2 | import GoogleProvider from "next-auth/providers/google";
3 |
4 | const GOOGLE_CLIENT_ID = process.env.GOOGLE_CLIENT_ID!;
5 | const GOOGLE_CLIENT_SECRET = process.env.GOOGLE_CLIENT_SECRET!;
6 |
7 | export const authOptions: NextAuthOptions = {
8 | session: {
9 | strategy: "jwt",
10 | },
11 | providers: [
12 | GoogleProvider({
13 | clientId: GOOGLE_CLIENT_ID,
14 | clientSecret: GOOGLE_CLIENT_SECRET,
15 | }),
16 | ],
17 | callbacks: {
18 | async signIn({ account, profile }) {
19 | if (!profile?.email) {
20 | throw new Error("No profile found");
21 | }
22 |
23 | return true;
24 | },
25 | },
26 | };
27 |
--------------------------------------------------------------------------------
/lib/formatCurrency.ts:
--------------------------------------------------------------------------------
1 | const CURRENCY_FORMATTER = new Intl.NumberFormat(undefined, {
2 | currency: "GBP",
3 | style: "currency",
4 | });
5 |
6 | export function formatCurrency(number: number) {
7 | return CURRENCY_FORMATTER.format(number);
8 | }
9 |
--------------------------------------------------------------------------------
/lib/utils.ts:
--------------------------------------------------------------------------------
1 | import { clsx, type ClassValue } from "clsx";
2 | import { twMerge } from "tailwind-merge";
3 |
4 | export function cn(...inputs: ClassValue[]) {
5 | return twMerge(clsx(inputs));
6 | }
7 |
--------------------------------------------------------------------------------
/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {};
3 |
4 | export default nextConfig;
5 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "algo",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "@radix-ui/react-avatar": "^1.0.4",
13 | "@radix-ui/react-dialog": "^1.0.5",
14 | "@radix-ui/react-dropdown-menu": "^2.0.6",
15 | "@radix-ui/react-icons": "^1.3.0",
16 | "@radix-ui/react-separator": "^1.0.3",
17 | "@radix-ui/react-slot": "^1.0.2",
18 | "ai": "^2.2.33",
19 | "and": "^0.0.3",
20 | "class-variance-authority": "^0.7.0",
21 | "clsx": "^2.1.0",
22 | "framer-motion": "^11.0.5",
23 | "next": "14.1.0",
24 | "next-auth": "^4.24.6",
25 | "next-themes": "^0.2.1",
26 | "openai-edge": "^1.2.2",
27 | "raw-body": "^2.5.2",
28 | "react": "^18",
29 | "react-dom": "^18",
30 | "stripe": "^14.16.0",
31 | "tailwind-merge": "^2.2.1",
32 | "tailwindcss-animate": "^1.0.7"
33 | },
34 | "devDependencies": {
35 | "@types/node": "^20",
36 | "@types/react": "^18",
37 | "@types/react-dom": "^18",
38 | "autoprefixer": "^10.0.1",
39 | "eslint": "^8",
40 | "eslint-config-next": "14.1.0",
41 | "postcss": "^8",
42 | "tailwindcss": "^3.3.0",
43 | "typescript": "^5"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {},
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/public/abbysmith.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HamzaKhan-HK/Nuro-AI/8d3086ba51c4f4dd529197b5116f0e739e8f6700/public/abbysmith.jpg
--------------------------------------------------------------------------------
/public/app-screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HamzaKhan-HK/Nuro-AI/8d3086ba51c4f4dd529197b5116f0e739e8f6700/public/app-screenshot.png
--------------------------------------------------------------------------------
/public/craignelson.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HamzaKhan-HK/Nuro-AI/8d3086ba51c4f4dd529197b5116f0e739e8f6700/public/craignelson.jpg
--------------------------------------------------------------------------------
/public/johndoe.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HamzaKhan-HK/Nuro-AI/8d3086ba51c4f4dd529197b5116f0e739e8f6700/public/johndoe.jpg
--------------------------------------------------------------------------------
/public/nuro-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/HamzaKhan-HK/Nuro-AI/8d3086ba51c4f4dd529197b5116f0e739e8f6700/public/nuro-logo.png
--------------------------------------------------------------------------------
/tailwind.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "tailwindcss"
2 |
3 | const config = {
4 | darkMode: ["class"],
5 | content: [
6 | './pages/**/*.{ts,tsx}',
7 | './components/**/*.{ts,tsx}',
8 | './app/**/*.{ts,tsx}',
9 | './src/**/*.{ts,tsx}',
10 | ],
11 | prefix: "",
12 | theme: {
13 | container: {
14 | center: true,
15 | padding: "2rem",
16 | screens: {
17 | "2xl": "1400px",
18 | },
19 | },
20 | extend: {
21 | colors: {
22 | border: "hsl(var(--border))",
23 | input: "hsl(var(--input))",
24 | ring: "hsl(var(--ring))",
25 | background: "hsl(var(--background))",
26 | foreground: "hsl(var(--foreground))",
27 | primary: {
28 | DEFAULT: "hsl(var(--primary))",
29 | foreground: "hsl(var(--primary-foreground))",
30 | },
31 | secondary: {
32 | DEFAULT: "hsl(var(--secondary))",
33 | foreground: "hsl(var(--secondary-foreground))",
34 | },
35 | destructive: {
36 | DEFAULT: "hsl(var(--destructive))",
37 | foreground: "hsl(var(--destructive-foreground))",
38 | },
39 | muted: {
40 | DEFAULT: "hsl(var(--muted))",
41 | foreground: "hsl(var(--muted-foreground))",
42 | },
43 | accent: {
44 | DEFAULT: "hsl(var(--accent))",
45 | foreground: "hsl(var(--accent-foreground))",
46 | },
47 | popover: {
48 | DEFAULT: "hsl(var(--popover))",
49 | foreground: "hsl(var(--popover-foreground))",
50 | },
51 | card: {
52 | DEFAULT: "hsl(var(--card))",
53 | foreground: "hsl(var(--card-foreground))",
54 | },
55 | },
56 | borderRadius: {
57 | lg: "var(--radius)",
58 | md: "calc(var(--radius) - 2px)",
59 | sm: "calc(var(--radius) - 4px)",
60 | },
61 | keyframes: {
62 | "accordion-down": {
63 | from: { height: "0" },
64 | to: { height: "var(--radix-accordion-content-height)" },
65 | },
66 | "accordion-up": {
67 | from: { height: "var(--radix-accordion-content-height)" },
68 | to: { height: "0" },
69 | },
70 | },
71 | animation: {
72 | "accordion-down": "accordion-down 0.2s ease-out",
73 | "accordion-up": "accordion-up 0.2s ease-out",
74 | },
75 | },
76 | },
77 | plugins: [require("tailwindcss-animate")],
78 | } satisfies Config
79 |
80 | export default config
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["dom", "dom.iterable", "esnext"],
4 | "allowJs": true,
5 | "skipLibCheck": true,
6 | "strict": true,
7 | "noEmit": true,
8 | "esModuleInterop": true,
9 | "module": "esnext",
10 | "moduleResolution": "bundler",
11 | "resolveJsonModule": true,
12 | "isolatedModules": true,
13 | "jsx": "preserve",
14 | "incremental": true,
15 | "plugins": [
16 | {
17 | "name": "next"
18 | }
19 | ],
20 | "paths": {
21 | "@/*": ["./*"]
22 | }
23 | },
24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
25 | "exclude": ["node_modules"]
26 | }
27 |
--------------------------------------------------------------------------------