feat: Copied from template
This commit is contained in:
Binary file not shown.
Before Width: | Height: | Size: 25 KiB |
@@ -1,26 +0,0 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
:root {
|
||||
--background: #ffffff;
|
||||
--foreground: #171717;
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--font-sans: var(--font-geist-sans);
|
||||
--font-mono: var(--font-geist-mono);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--background: #0a0a0a;
|
||||
--foreground: #ededed;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--background);
|
||||
color: var(--foreground);
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
@@ -1,34 +1,30 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Geist, Geist_Mono } from "next/font/google";
|
||||
import "./globals.css";
|
||||
import { ThemeProvider } from "next-themes";
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: "--font-geist-sans",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
const geistMono = Geist_Mono({
|
||||
variable: "--font-geist-mono",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
import "@/styles/globals.css";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Create Next App",
|
||||
description: "Generated by create next app",
|
||||
title: "Next JS SaaS Starter Template",
|
||||
description: "Next JS SaaS Starter Template",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<html lang="en" suppressHydrationWarning>
|
||||
<head>
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Inter+Tight:ital,wght@0,100..900;1,100..900&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
</head>
|
||||
<body className="bg-white dark:bg-black min-h-screen">
|
||||
<ThemeProvider attribute="class" defaultTheme="dark" enableSystem>
|
||||
{children}
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
147
src/app/page.tsx
147
src/app/page.tsx
@@ -1,103 +1,56 @@
|
||||
import Header from "@/components/Header";
|
||||
import Hero from "@/components/Hero";
|
||||
import Features from "@/components/Features";
|
||||
import Section from "@/components/Section";
|
||||
import Footer from "@/components/Footer";
|
||||
import Customers from "@/components/Customers";
|
||||
import Image from "next/image";
|
||||
import Accordion from "@/components/Accordion";
|
||||
import Reviews from "@/components/Reviews";
|
||||
import Download from "@/components/Download";
|
||||
|
||||
export default function Home() {
|
||||
export default function Page() {
|
||||
return (
|
||||
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
|
||||
<main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start">
|
||||
<Image
|
||||
className="dark:invert"
|
||||
src="/next.svg"
|
||||
alt="Next.js logo"
|
||||
width={180}
|
||||
height={38}
|
||||
priority
|
||||
/>
|
||||
<ol className="list-inside list-decimal text-sm/6 text-center sm:text-left font-[family-name:var(--font-geist-mono)]">
|
||||
<li className="mb-2 tracking-[-.01em]">
|
||||
Get started by editing{" "}
|
||||
<code className="bg-black/[.05] dark:bg-white/[.06] px-1 py-0.5 rounded font-[family-name:var(--font-geist-mono)] font-semibold">
|
||||
src/app/page.tsx
|
||||
</code>
|
||||
.
|
||||
</li>
|
||||
<li className="tracking-[-.01em]">
|
||||
Save and see your changes instantly.
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<div className="flex gap-4 items-center flex-col sm:flex-row">
|
||||
<a
|
||||
className="rounded-full border border-solid border-transparent transition-colors flex items-center justify-center bg-foreground text-background gap-2 hover:bg-[#383838] dark:hover:bg-[#ccc] font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 sm:w-auto"
|
||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
className="dark:invert"
|
||||
src="/vercel.svg"
|
||||
alt="Vercel logomark"
|
||||
width={20}
|
||||
height={20}
|
||||
/>
|
||||
Deploy now
|
||||
</a>
|
||||
<a
|
||||
className="rounded-full border border-solid border-black/[.08] dark:border-white/[.145] transition-colors flex items-center justify-center hover:bg-[#f2f2f2] dark:hover:bg-[#1a1a1a] hover:border-transparent font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 w-full sm:w-auto md:w-[158px]"
|
||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Read our docs
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
<footer className="row-start-3 flex gap-[24px] flex-wrap items-center justify-center">
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/file.svg"
|
||||
alt="File icon"
|
||||
width={16}
|
||||
height={16}
|
||||
<div className="flex flex-col min-h-screen bg-white dark:bg-black">
|
||||
<Header />
|
||||
<main>
|
||||
<Hero />
|
||||
<Features />
|
||||
<Section
|
||||
leftHalf={
|
||||
<>
|
||||
<h2 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl text-gray-900 dark:text-white mb-4">
|
||||
Effortlessly highlight the key features of your app
|
||||
</h2>
|
||||
<p className="text-xl font-light">
|
||||
Our app makes it easy to showcase your key features. With customizable sections, you can highlight the
|
||||
most important aspects of your product. More to come.
|
||||
</p>
|
||||
</>
|
||||
}
|
||||
rightHalf={
|
||||
<Image src={"/products/phone.png"} alt="section-image" width={500} height={100} className="w-1/2 h-auto" />
|
||||
}
|
||||
/>
|
||||
Learn
|
||||
</a>
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/window.svg"
|
||||
alt="Window icon"
|
||||
width={16}
|
||||
height={16}
|
||||
<Customers />
|
||||
<Section
|
||||
leftHalf={<Accordion />}
|
||||
rightHalf={
|
||||
<div className="flex flex-col justify-end">
|
||||
<h2 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl text-gray-900 dark:text-white mb-4">
|
||||
Highlight the key features
|
||||
</h2>
|
||||
<p className="text-xl font-light">
|
||||
Talk about some of the key features of your app that you want to highlight. Use the beautiful accordion
|
||||
to highlight the key features of your app.
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
Examples
|
||||
</a>
|
||||
<a
|
||||
className="flex items-center gap-2 hover:underline hover:underline-offset-4"
|
||||
href="https://nextjs.org?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<Image
|
||||
aria-hidden
|
||||
src="/globe.svg"
|
||||
alt="Globe icon"
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
Go to nextjs.org →
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
<Reviews />
|
||||
<Download />
|
||||
</main>
|
||||
<Footer />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
84
src/components/Accordion.tsx
Normal file
84
src/components/Accordion.tsx
Normal file
@@ -0,0 +1,84 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { FaChevronDown } from "react-icons/fa";
|
||||
|
||||
interface AccordionItemProps {
|
||||
title: string;
|
||||
content: string;
|
||||
isOpen: boolean;
|
||||
toggleOpen: () => void;
|
||||
}
|
||||
|
||||
const AccordionItem: React.FC<AccordionItemProps> = ({ title, content, isOpen, toggleOpen }) => {
|
||||
return (
|
||||
<div className="mb-4">
|
||||
<div
|
||||
className={`w-full rounded-lg overflow-hidden ${
|
||||
isOpen ? "bg-black dark:bg-gray-900" : "bg-black dark:bg-gray-900"
|
||||
}`}
|
||||
>
|
||||
<button className="w-full text-left p-4 flex justify-between items-center" onClick={toggleOpen}>
|
||||
<span className="text-xl font-semibold text-white dark:text-white">{title}</span>
|
||||
<span className={`transform transition-transform duration-300 ${isOpen ? "rotate-180" : ""}`}>
|
||||
<FaChevronDown className="text-2xl text-white" />
|
||||
</span>
|
||||
</button>
|
||||
<div
|
||||
className={`overflow-hidden transition-[max-height] duration-300 ease-in-out ${
|
||||
isOpen ? "max-h-[1000px]" : "max-h-0"
|
||||
}`}
|
||||
>
|
||||
<div className="p-4">
|
||||
<p className="text-white font-light">{content}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const defaultAccordionItems = [
|
||||
{
|
||||
title: "Sign up for free",
|
||||
content: "Start your journey with a 34-day trial. It's easy no credit card or commitment.",
|
||||
},
|
||||
{
|
||||
title: "Easy to use",
|
||||
content:
|
||||
"Our app is designed to be user-friendly and easy to use. We want to provide a seamless experience for your users so that you can focus on what matters.",
|
||||
},
|
||||
{
|
||||
title: "Focus on what matters",
|
||||
content:
|
||||
"With better financial management, you can reduce money-related stress and focus on what matters most. More to come.",
|
||||
},
|
||||
];
|
||||
|
||||
interface AccordionProps {
|
||||
items?: { title: string; content: string }[];
|
||||
}
|
||||
|
||||
const Accordion: React.FC<AccordionProps> = ({ items = defaultAccordionItems }) => {
|
||||
const [openIndex, setOpenIndex] = useState<number | null>(0);
|
||||
|
||||
const toggleItem = (index: number) => {
|
||||
setOpenIndex(openIndex === index ? null : index);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-[90%]">
|
||||
{items.map((item, index) => (
|
||||
<AccordionItem
|
||||
key={index}
|
||||
title={item.title}
|
||||
content={item.content}
|
||||
isOpen={openIndex === index}
|
||||
toggleOpen={() => toggleItem(index)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Accordion;
|
45
src/components/Customers.tsx
Normal file
45
src/components/Customers.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import React from "react";
|
||||
import {
|
||||
AiOutlineDiscord,
|
||||
AiTwotoneBug,
|
||||
AiTwotoneExperiment,
|
||||
AiOutlineTaobao,
|
||||
AiOutlineSpotify,
|
||||
AiOutlineCode,
|
||||
AiOutlineCiCircle,
|
||||
} from "react-icons/ai";
|
||||
|
||||
const icons = [
|
||||
{ Icon: AiTwotoneBug },
|
||||
{ Icon: AiOutlineDiscord },
|
||||
{ Icon: AiTwotoneExperiment },
|
||||
{ Icon: AiOutlineTaobao },
|
||||
{ Icon: AiOutlineSpotify },
|
||||
{ Icon: AiOutlineCode },
|
||||
{ Icon: AiOutlineCiCircle },
|
||||
];
|
||||
|
||||
const Customers: React.FC = () => {
|
||||
return (
|
||||
<div className="w-full py-12 bg-black dark:bg-white">
|
||||
<div className="w-full px-4 md:px-8 lg:px-16">
|
||||
<div className="flex flex-col md:flex-row items-center justify-between max-w-7xl mx-auto">
|
||||
<div className="flex flex-col md:flex-row w-full">
|
||||
<h2 className="text-xl sm:text-2xl md:text-3xl lg:text-4xl font-bold mb-6 md:mb-0 text-white dark:text-black text-center md:text-left md:w-1/3">
|
||||
Over 50,000 people rely on our app for their daily needs
|
||||
</h2>
|
||||
<div className="flex flex-wrap justify-center md:justify-end gap-6 md:w-2/3">
|
||||
{icons.map(({ Icon }, index) => (
|
||||
<div key={index} className="flex flex-col items-center justify-center">
|
||||
<Icon className="text-3xl md:text-4xl text-white dark:text-black" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Customers;
|
47
src/components/Download.tsx
Normal file
47
src/components/Download.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { FaApple, FaGooglePlay } from "react-icons/fa";
|
||||
|
||||
const Download: React.FC = () => (
|
||||
<section className="container mx-auto py-24 px-4 md:px-6">
|
||||
<div className="flex flex-col md:flex-row items-center gap-8">
|
||||
<div className="w-full md:w-1/2 order-1 p-4 flex justify-center md:justify-start items-center">
|
||||
<Image
|
||||
src="/products/phone1.png"
|
||||
alt="Financial app interface"
|
||||
width={500}
|
||||
height={500}
|
||||
className="w-1/2 h-auto mx-auto md:mx-0"
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full md:w-1/2 order-2 flex justify-center md:justify-end">
|
||||
<div className="flex flex-col justify-center">
|
||||
<h2 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl text-gray-900 dark:text-white mb-4">
|
||||
Download our app
|
||||
</h2>
|
||||
<p className="text-xl text-gray-800 dark:text-gray-300 mb-6 font-light">
|
||||
Download the state of the art app and start saving time and money. More to come.
|
||||
</p>
|
||||
<div className="flex space-x-4">
|
||||
<Link
|
||||
href="#"
|
||||
className="download-button bg-black dark:bg-white text-white dark:text-black px-5 py-2 rounded-md text-base flex items-center space-x-2 transition-colors duration-200"
|
||||
>
|
||||
<FaApple className="text-2xl" />
|
||||
<span>App Store</span>
|
||||
</Link>
|
||||
<Link
|
||||
href="#"
|
||||
className="download-button bg-black dark:bg-white text-white dark:text-black px-5 py-2 rounded-md text-base flex items-center space-x-2 transition-colors duration-200"
|
||||
>
|
||||
<FaGooglePlay className="text-2xl" />
|
||||
<span>Google Play</span>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
||||
export default Download;
|
15
src/components/FeatureCard.tsx
Normal file
15
src/components/FeatureCard.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import React from "react";
|
||||
import {IconType} from 'react-icons';
|
||||
|
||||
// @ts-expect-error Component 类型不识别
|
||||
const FeatureCard: ({icon: Icon, title, description}: { icon: IconType; title: string; description: string }) => React.JSX.Element = ({ icon: Icon, title, description }) => {
|
||||
return (
|
||||
<div className="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-md transition-colors duration-200">
|
||||
<Icon size={34} />
|
||||
<h3 className="text-xl font-semibold mb-2 text-gray-800 dark:text-white">{title}</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300 font-light">{description}</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FeatureCard;
|
62
src/components/Features.tsx
Normal file
62
src/components/Features.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
import React from "react";
|
||||
import FeatureCard from "./FeatureCard";
|
||||
import { FaReact, FaDatabase, FaShieldAlt } from "react-icons/fa";
|
||||
import { SiNextdotjs, SiTailwindcss, SiStripe } from "react-icons/si";
|
||||
|
||||
const Features = () => {
|
||||
const features = [
|
||||
{
|
||||
icon: SiNextdotjs,
|
||||
title: "Next.js 14",
|
||||
description: "App dir, Routing, Layouts, components, and more.",
|
||||
},
|
||||
{
|
||||
icon: FaReact,
|
||||
title: "React 18",
|
||||
description: "Server and Client Components. using hooks and context.",
|
||||
},
|
||||
{
|
||||
icon: FaDatabase,
|
||||
title: "Database",
|
||||
description: "Postgres basic database and other cool features to come.",
|
||||
},
|
||||
{
|
||||
icon: SiTailwindcss,
|
||||
title: "Components",
|
||||
description: "Awesome components built with Tailwind CSS and more to come.",
|
||||
},
|
||||
{
|
||||
icon: FaShieldAlt,
|
||||
title: "Authentication",
|
||||
description: "Talk about your authentication features built into your app.",
|
||||
},
|
||||
{
|
||||
icon: SiStripe,
|
||||
title: "Subscriptions",
|
||||
description: "Talk about your subscription features and how they work.",
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<section className="container mx-auto px-4 py-12 bg-gray-50 dark:bg-gray-900 transition-colors duration-200 rounded-lg">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div className="text-center">
|
||||
<h2 className="text-2xl sm:text-3xl md:text-4xl lg:text-5xl text-gray-900 dark:text-white mb-4">Features</h2>
|
||||
<p className="mt-8 text-xl text-gray-600 dark:text-gray-300 font-light">
|
||||
Highlight cool features of your app using the beautifully designed custom cards with icons. You can use any
|
||||
icon you want.
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-10">
|
||||
<div className="grid grid-cols-1 gap-8 sm:grid-cols-2 lg:grid-cols-3">
|
||||
{features.map((feature, index) => (
|
||||
<FeatureCard key={index} {...feature} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Features;
|
32
src/components/Footer.tsx
Normal file
32
src/components/Footer.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import { AiOutlineFacebook, AiOutlineX, AiOutlineGithub, AiOutlineLinkedin, AiOutlineInstagram } from "react-icons/ai";
|
||||
|
||||
const Footer = () => {
|
||||
const currentYear = new Date().getFullYear();
|
||||
|
||||
return (
|
||||
<footer className="bg-white border-t border-gray-100 dark:bg-black shadow-xs text-gray-400 py-6 dark:border-t dark:border-gray-800">
|
||||
<div className="container mx-auto px-4 flex flex-wrap justify-center sm:justify-between items-center text-sm">
|
||||
<p className="ml-4">© {currentYear} Your Company Name. All rights reserved.</p>
|
||||
<div className="flex space-x-4 mt-2 mr-4 sm:mt-0">
|
||||
<a href="#" aria-label="Facebook" className="hover:text-gray-300">
|
||||
<AiOutlineFacebook className="w-5 h-5" />
|
||||
</a>
|
||||
<a href="#" aria-label="LinkedIn" className="hover:text-gray-300">
|
||||
<AiOutlineLinkedin className="w-5 h-5" />
|
||||
</a>
|
||||
<a href="#" aria-label="Instagram" className="hover:text-gray-300">
|
||||
<AiOutlineInstagram className="w-5 h-5" />
|
||||
</a>
|
||||
<a href="#" aria-label="X (formerly Twitter)" className="hover:text-gray-300">
|
||||
<AiOutlineX className="w-5 h-5" />
|
||||
</a>
|
||||
<a href="#" aria-label="GitHub" className="hover:text-gray-300">
|
||||
<AiOutlineGithub className="w-5 h-5" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
};
|
||||
|
||||
export default Footer;
|
35
src/components/Header.tsx
Normal file
35
src/components/Header.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import Link from "next/link";
|
||||
import ThemeSwitch from "./ThemeSwitch";
|
||||
|
||||
export default function Header() {
|
||||
return (
|
||||
<header className="bg-white dark:bg-black shadow-xs dark:border-b dark:border-gray-800">
|
||||
<div className="container mx-auto px-4 py-4 flex justify-between items-center">
|
||||
<div className="flex items-center">
|
||||
<span className="text-xl dark:text-gray-100">Next JS Starter Template</span>
|
||||
</div>
|
||||
<nav className="flex items-center">
|
||||
<ul className="flex space-x-2 mr-2">
|
||||
<li>
|
||||
<Link
|
||||
href="/"
|
||||
className="text-sm text-gray-800 dark:text-white px-4 py-2 rounded-md hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
|
||||
>
|
||||
Home
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link
|
||||
href="/contact"
|
||||
className="text-sm text-gray-800 dark:text-white px-4 py-2 rounded-md hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
|
||||
>
|
||||
Contact
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
<ThemeSwitch />
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
28
src/components/Hero.tsx
Normal file
28
src/components/Hero.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import Link from "next/link";
|
||||
|
||||
export default function Hero() {
|
||||
return (
|
||||
<section className="text-center my-32 mx-4 sm:mx-8 md:mx-16 lg:mx-24">
|
||||
<h1 className="font-sans text-3xl tracking-tighter sm:text-5xl md:text-6xl lg:text-7xl dark:text-white mb-6">
|
||||
NextJS Template amazing hero!
|
||||
</h1>
|
||||
<p className="text-xl text-gray-600 dark:text-gray-300 mb-12 font-light">
|
||||
Add something about your saas service or app here.
|
||||
</p>
|
||||
<div className="flex justify-center space-x-6">
|
||||
<Link
|
||||
href="/get-started"
|
||||
className="bg-black text-white dark:bg-white dark:text-black px-5 py-2 rounded-md text-base font-semibold hover:bg-gray-800 dark:hover:bg-gray-200 transition duration-300"
|
||||
>
|
||||
Get Started
|
||||
</Link>
|
||||
<Link
|
||||
href="https://github.com"
|
||||
className="bg-black text-white dark:bg-white dark:text-black px-5 py-2 rounded-md text-base font-semibold hover:bg-gray-800 dark:hover:bg-gray-200 transition duration-300"
|
||||
>
|
||||
Download
|
||||
</Link>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
37
src/components/Review.tsx
Normal file
37
src/components/Review.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import { FaStar } from "react-icons/fa";
|
||||
|
||||
interface ReviewProps {
|
||||
rating: number;
|
||||
title: string;
|
||||
content: string;
|
||||
author: string;
|
||||
designation: string;
|
||||
}
|
||||
|
||||
const Review: React.FC<ReviewProps> = ({
|
||||
rating = 5,
|
||||
title = "Default Title",
|
||||
content = "Default content for the review.",
|
||||
author = "John Doe",
|
||||
designation = "Customer",
|
||||
}) => {
|
||||
return (
|
||||
<div className="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-md">
|
||||
<div className="flex items-center mb-2">
|
||||
{[...Array(5)].map((_, i) => (
|
||||
<FaStar key={i} className={`w-5 h-5 ${i < rating ? "text-green-500" : "text-gray-300"}`} />
|
||||
))}
|
||||
</div>
|
||||
<h3 className="text-xl mb-2 text-gray-900 dark:text-white">{title}</h3>
|
||||
<p className="text-gray-600 dark:text-gray-300 mb-4 font-light">{content}</p>
|
||||
<div className="flex items-center">
|
||||
<div>
|
||||
<p className="text-gray-900 dark:text-white">{author}</p>
|
||||
<p className="text-sm text-gray-500 dark:text-gray-400 font-light">{designation}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Review;
|
67
src/components/Reviews.tsx
Normal file
67
src/components/Reviews.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import Review from "./Review";
|
||||
|
||||
interface ReviewData {
|
||||
rating: number;
|
||||
title: string;
|
||||
content: string;
|
||||
author: string;
|
||||
designation: string;
|
||||
}
|
||||
|
||||
interface ReviewsProps {
|
||||
reviews?: ReviewData[];
|
||||
}
|
||||
|
||||
const defaultReviews: ReviewData[] = [
|
||||
{
|
||||
rating: 5,
|
||||
title: "Best app ever!",
|
||||
content:
|
||||
"This app has been a game-changer for me! It's made tracking my daily activities so much easier. I love how intuitive and user-friendly it is.",
|
||||
author: "Jonas Aly",
|
||||
designation: "Founder @ Company",
|
||||
},
|
||||
{
|
||||
rating: 5,
|
||||
title: "Super helpful to stay organized",
|
||||
content:
|
||||
"I can't thank this app enough for helping me stay on top of my tasks. The reminders have saved me from missing important deadlines, and I'm much more organized now.",
|
||||
author: "Mark Bures",
|
||||
designation: "Businessman",
|
||||
},
|
||||
{
|
||||
rating: 5,
|
||||
title: "Great app that saves time",
|
||||
content:
|
||||
"The app's integration with my other tools is seamless. I can easily check my progress and activities without having to switch between multiple platforms.",
|
||||
author: "William Kolas",
|
||||
designation: "Student",
|
||||
},
|
||||
{
|
||||
rating: 4,
|
||||
title: "Seriously life changing app!",
|
||||
content:
|
||||
"The insights and reports have been eye-opening. I now have a better understanding of my habits and can make adjustments to improve my productivity.",
|
||||
author: "Andrew Chan",
|
||||
designation: "Manager @ AB Company",
|
||||
},
|
||||
];
|
||||
|
||||
const Reviews: React.FC<ReviewsProps> = ({ reviews = defaultReviews }) => {
|
||||
return (
|
||||
<section className="py-24 bg-gray-50 dark:bg-gray-900">
|
||||
<div className="container mx-auto px-4">
|
||||
<h2 className="text-3xl sm:text-3xl md:text-4xl lg:text-5xl text-center mb-8 text-gray-900 dark:text-white">
|
||||
A beautiful reviews section
|
||||
</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 pt-8">
|
||||
{reviews.map((review, index) => (
|
||||
<Review key={index} {...review} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Reviews;
|
19
src/components/Section.tsx
Normal file
19
src/components/Section.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import React from 'react';
|
||||
|
||||
interface SectionProps {
|
||||
leftHalf: React.ReactNode;
|
||||
rightHalf: React.ReactNode;
|
||||
}
|
||||
|
||||
const Section: React.FC<SectionProps> = ({ leftHalf, rightHalf }) => {
|
||||
return (
|
||||
<section className="container mx-auto py-24 px-4 md:px-6">
|
||||
<div className="flex flex-col md:flex-row items-center gap-8">
|
||||
<div className="w-full md:w-1/2 order-2 md:order-1 p-4 justify-center md:justify-start">{leftHalf}</div>
|
||||
<div className="w-full md:w-1/2 order-1 md:order-2 flex justify-center md:justify-end">{rightHalf}</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Section;
|
29
src/components/ThemeSwitch.tsx
Normal file
29
src/components/ThemeSwitch.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTheme } from "next-themes";
|
||||
import { AiOutlineSun, AiOutlineMoon } from "react-icons/ai";
|
||||
|
||||
const ThemeSwitch = () => {
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const { theme, setTheme, resolvedTheme } = useTheme();
|
||||
|
||||
// When mounted on client, now we can show the UI
|
||||
useEffect(() => setMounted(true), []);
|
||||
|
||||
if (!mounted) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={() => setTheme(theme === "dark" || resolvedTheme === "dark" ? "light" : "dark")}
|
||||
className="p-2 text-gray-800 dark:text-gray-200 bg-transparent dark:bg-black"
|
||||
aria-label="Toggle Dark Mode"
|
||||
>
|
||||
{theme === "dark" || resolvedTheme === "dark" ? <AiOutlineSun size={20} /> : <AiOutlineMoon size={20} />}
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default ThemeSwitch;
|
1
src/styles/globals.css
Normal file
1
src/styles/globals.css
Normal file
@@ -0,0 +1 @@
|
||||
@import "tailwindcss";
|
Reference in New Issue
Block a user