Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Review Section Design #79

Merged
merged 4 commits into from
Nov 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 6 additions & 1 deletion PROJECT_STRUCTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
├── LICENSE
├── PROJECT_STRUCTURE.md
├── README.md
├── SECURITY.md
├── app/
│ ├── (auth)/
│ │ ├── sign-in/
Expand All @@ -28,6 +29,7 @@
│ ├── dashboard/
│ │ ├── _components/
│ │ │ ├── Header.tsx
│ │ │ ├── MobileSidebar.tsx
│ │ │ ├── SearchSection.tsx
│ │ │ ├── SideNav.tsx
│ │ │ ├── TemplateCard.tsx
Expand All @@ -51,6 +53,7 @@
│ │ └── GeistVF.woff
│ ├── globals.css
│ ├── layout.tsx
│ ├── metadata.tsx
│ ├── not-found.tsx
│ └── page.tsx
├── button.tsx
Expand Down Expand Up @@ -81,7 +84,9 @@
├── page.tsx
├── postcss.config.mjs
├── public/
│ └── logo.svg
│ ├── logo.svg
│ ├── robots.txt
│ └── sitemap.xml
├── repo_structure.txt
├── tailwind.config.ts
├── tsconfig.json
Expand Down
69 changes: 22 additions & 47 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
import type { Metadata } from "next";
import "./globals.css";
import { ClerkProvider } from "@clerk/nextjs";

import { Outfit } from "next/font/google";
import ChatbaseEmbed from "@/components/ChatbaseEmbed";
import { metadata } from "./metadata";

const inter = Outfit({ subsets: ["latin"] });

// const geistSans = localFont({
// src: "./fonts/GeistVF.woff",
// variable: "--font-geist-sans",
// weight: "100 900",
// });
// const geistMono = localFont({
// src: "./fonts/GeistMonoVF.woff",
// variable: "--font-geist-mono",
// weight: "100 900",
// });
const outfit = Outfit({ subsets: ["latin"] });

// Define metadata
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
openGraph: {
title: "Create Next App",
description: "Generated by create next app",
type: "website",
locale: "en_US",
url: "https://your-domain.com",
},
twitter: {
card: "summary_large_image",
title: "Create Next App",
description: "Generated by create next app",
},
};

export default function RootLayout({
Expand All @@ -30,40 +30,15 @@ export default function RootLayout({
children: React.ReactNode;
}) {
return (
<ClerkProvider
publishableKey={process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY}
>
<html lang="en">
<head>
<title>{metadata.title}</title>
<meta name="description" content={metadata.description} />
<meta name="keywords" content={metadata.keywords} />
<meta name="author" content={metadata.author} />

{/* Open Graph Meta Tags */}
<meta property="og:title" content={metadata.openGraph.title} />
<meta property="og:description" content={metadata.openGraph.description} />
<meta property="og:url" content={metadata.openGraph.url} />
<meta property="og:site_name" content={metadata.openGraph.siteName} />
<meta property="og:type" content={metadata.openGraph.type} />
<meta property="og:image" content={metadata.openGraph.images[0].url} />
<meta property="og:image:width" content={metadata.openGraph.images[0].width.toString()} />
<meta property="og:image:height" content={metadata.openGraph.images[0].height.toString()} />
<meta property="og:image:alt" content={metadata.openGraph.images[0].alt} />

{/* Twitter Meta Tags */}
<meta name="twitter:card" content={metadata.twitter.card} />
<meta name="twitter:title" content={metadata.twitter.title} />
<meta name="twitter:description" content={metadata.twitter.description} />
<meta name="twitter:image" content={metadata.twitter.images[0]} />
<meta name="twitter:site" content={metadata.twitter.site} />
<meta name="twitter:creator" content={metadata.twitter.creator} />
</head>
<body className={`${inter.className} antialiased`}>
{children}
<ChatbaseEmbed />
<ClerkProvider>
<html lang="en">
<body className={outfit.className}>
<main>
{children}
<ChatbaseEmbed />
</main>
</body>
</html>
</ClerkProvider>
);
}
}
2 changes: 2 additions & 0 deletions app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import PublicHeader from "@/components/public-header";
import PublicFooter from "@/components/public-footer";
import FaqPage from "@/components/ui/faq";
import Pricing from "@/components/ui/pricing";
import Review from '@/components/ui/review'
const words = [
{
text: "AI-Powered",
Expand Down Expand Up @@ -279,6 +280,7 @@ export default function LandingPage() {
</main>
<FaqPage/>
<Pricing/>
<Review/>
<PublicFooter />
</div>
);
Expand Down
172 changes: 172 additions & 0 deletions components/ui/review.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
'use client';
import React, { useState, useEffect } from 'react';
import { Sparkles, Brain, Zap, Stars, ArrowRight, ArrowLeft } from 'lucide-react';
import Image from 'next/image';

interface Testimonial {
text: string;
author: string;
position: string;
emotion: string;
techStack: string[];
aiContribution: string;
image: string; // New property for image path
}

const testimonials: Testimonial[] = [
{
text: "This AI is a game-changer! It's like having an entire research team that never sleeps. My productivity has skyrocketed.",
author: "Liam Garcia",
position: "AI Enthusiast",
emotion: "amazed",
techStack: ["Deep Learning", "Predictive Analytics", "GPT"],
aiContribution: "Productivity boosted by 400%",
image: "/Liam.jpg" // Path to specific image
},
{
text: "It feels like magic – the AI adjusts to each project’s needs! From generating fresh ideas to refining complex designs, it's my creative assistant.",
author: "Ava Thompson",
position: "Tech Explorer",
emotion: "inspired",
techStack: ["Adaptive Learning", "Creative Networks", "AI Art"],
aiContribution: "Idea generation speed increased by 5x",
image: "/ava.jpg"
},
{
text: "I never imagined AI could understand niche industry jargon so well. It’s been a lifesaver for technical content creation.",
author: "James Kim",
position: "Content AI Specialist",
emotion: "mindblown",
techStack: ["Natural Language Processing", "Custom GPT Models", "Content AI"],
aiContribution: "Content accuracy improved by 85%",
image: "/james.jpg"
},
{
text: "AI-powered insights have made data visualization so intuitive! Now I can see trends that would’ve taken weeks to identify before.",
author: "Sophia Perez",
position: "Data Enthusiast",
emotion: "amazed",
techStack: ["Data Visualization", "Machine Learning", "NLP"],
aiContribution: "Data insight speed increased by 3x",
image: "/sophia.jpg"
},
{
text: "Using AI to handle customer support queries was the best decision ever! It provides immediate responses, keeping customers happy and saving me time.",
author: "Zara Williams",
position: "Customer Support Lead",
emotion: "inspired",
techStack: ["Customer AI", "Sentiment Analysis", "Automated Support"],
aiContribution: "Customer response time reduced by 75%",
image: "/zara.jpg"
}
];

const AITestimonialExperience: React.FC = () => {
const [currentIndex, setCurrentIndex] = useState<number>(0);
const [isAutoPlaying, setIsAutoPlaying] = useState<boolean>(true);
const [hoveredTech, setHoveredTech] = useState<string | null>(null);

useEffect(() => {
let interval: NodeJS.Timeout;
if (isAutoPlaying) {
interval = setInterval(() => {
setCurrentIndex((prev) => (prev + 1) % testimonials.length);
}, 5000);
}
return () => clearInterval(interval);
}, [isAutoPlaying]);

const currentTestimonial = testimonials[currentIndex];

const getEmotionColor = (emotion: string) => {
const colors: Record<string, string> = {
amazed: 'bg-purple-600',
inspired: 'bg-blue-600',
mindblown: 'bg-green-600'
};
return colors[emotion] || 'bg-gray-600';
};

return (
<div className="min-h-screen bg-[#0D131F] text-white p-8">
<div className="max-w-4xl mx-auto rounded-2xl bg-black/30 backdrop-blur-lg p-8">
<div className="flex items-center justify-center mb-8">
<Sparkles className="w-8 h-8 mr-2 text-[#704EF8]" />
<h2 className="text-4xl font-bold bg-clip-text text-transparent bg-white">
Our Success Stories
</h2>
</div>

<div className="space-y-6 transition-opacity duration-500">
<div className="relative hover:transform hover:scale-105 transition-transform duration-300">
<blockquote className="text-2xl font-light italic text-center mb-8">
"{currentTestimonial.text}"
</blockquote>
<div className="absolute -top-4 -left-4">
<Brain className="w-8 h-8 text-[#704EF8]" />
</div>
</div>

<div className="flex flex-col items-center space-y-4">
<div className="relative">
<Image
src={currentTestimonial.image} // Dynamically set image for each testimonial
alt={currentTestimonial.author}
className="w-20 h-20 rounded-full ring-4 ring-[#704EF8] transition-transform duration-300 hover:scale-110"
width={200}
height={200}
/>
<div className={`absolute -bottom-2 -right-2 w-6 h-6 rounded-full ${getEmotionColor(currentTestimonial.emotion)} flex items-center justify-center`}>
<Stars className="w-4 h-4" />
</div>
</div>

<div className="text-center">
<h3 className="text-xl font-semibold">{currentTestimonial.author}</h3>
<p className="text-white">{currentTestimonial.position}</p>
</div>
</div>

<div className="space-y-4">
<div className="flex flex-wrap gap-2 justify-center">
{currentTestimonial.techStack.map((tech) => (
<div
key={tech}
className="px-3 py-1 rounded-full bg-[#4F46E5] border border-[#4F46E5] cursor-pointer
transition-all duration-300 hover:bg-[#4338CA] hover:scale-105"
onMouseEnter={() => setHoveredTech(tech)}
onMouseLeave={() => setHoveredTech(null)}
>
<span className="text-sm">{tech}</span>
</div>
))}
</div>

<div className="flex items-center justify-center space-x-2 text-sm text-white">
<Zap className="w-4 h-4" />
<span>{currentTestimonial.aiContribution}</span>
</div>
</div>
</div>

<div className="flex justify-center space-x-4 mt-8">
<button
onClick={() => setCurrentIndex((prev) => (prev - 1 + testimonials.length) % testimonials.length)}
className="p-3 rounded-full bg-[#704EF8] hover:bg-[#4338CA] transition-colors duration-300"
>
<ArrowLeft/>
</button>

<button
onClick={() => setCurrentIndex((prev) => (prev + 1) % testimonials.length)}
className="p-3 rounded-full bg-[#704EF8] hover:bg-[#4338CA] transition-colors duration-300"
>
<ArrowRight />
</button>
</div>
</div>
</div>
);
};

export default AITestimonialExperience;
11 changes: 8 additions & 3 deletions middleware.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";
import { NextResponse } from "next/server";

const isPublicRoute = createRouteMatcher(["/", "/sign-in(.*)", "/sign-up(.*)"]);

export default clerkMiddleware((auth, request) => {
export default clerkMiddleware(async (auth, request) => {
if (!isPublicRoute(request)) {
auth().protect();
const session = await auth();
if (!session.userId) {
return new NextResponse("Unauthorized", { status: 401 });
}
}
return NextResponse.next();
});

export const config = {
Expand All @@ -15,4 +20,4 @@ export const config = {
// Always run for API routes
"/(api|trpc)(.*)",
],
};
};
Loading
Loading