first commit
This commit is contained in:
20
src/app/(site)/(auth)/signin/page.tsx
Normal file
20
src/app/(site)/(auth)/signin/page.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import Signin from "@/app/components/Auth/SignIn";
|
||||
import Breadcrumb from "@/app/components/Common/Breadcrumb";
|
||||
import { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title:
|
||||
"Sign In | Property",
|
||||
};
|
||||
|
||||
const SigninPage = () => {
|
||||
return (
|
||||
<>
|
||||
<Breadcrumb pageName="Sign In Page" />
|
||||
|
||||
<Signin />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SigninPage;
|
||||
20
src/app/(site)/(auth)/signup/page.tsx
Normal file
20
src/app/(site)/(auth)/signup/page.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import SignUp from "@/app/components/Auth/SignUp";
|
||||
import Breadcrumb from "@/app/components/Common/Breadcrumb";
|
||||
import { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title:
|
||||
"Sign Up | Property",
|
||||
};
|
||||
|
||||
const SignupPage = () => {
|
||||
return (
|
||||
<>
|
||||
<Breadcrumb pageName="Sign Up Page" />
|
||||
|
||||
<SignUp />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SignupPage;
|
||||
13
src/app/(site)/documentation/page.tsx
Normal file
13
src/app/(site)/documentation/page.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Documentation } from '@/app/components/Documentation/Documentation'
|
||||
import { Metadata } from 'next'
|
||||
export const metadata: Metadata = {
|
||||
title: 'Featurs | SiEducational',
|
||||
}
|
||||
|
||||
export default function Page() {
|
||||
return (
|
||||
<>
|
||||
<Documentation />
|
||||
</>
|
||||
)
|
||||
}
|
||||
36
src/app/api/auth/[...nextauth]/route.js
Normal file
36
src/app/api/auth/[...nextauth]/route.js
Normal file
@@ -0,0 +1,36 @@
|
||||
import NextAuth from 'next-auth';
|
||||
import CredentialsProvider from 'next-auth/providers/credentials';
|
||||
import GoogleProvider from 'next-auth/providers/google';
|
||||
import GitHubProvider from 'next-auth/providers/github';
|
||||
|
||||
const handler = NextAuth({
|
||||
site: process.env.NEXTAUTH_URL || 'http://localhost:3000',
|
||||
providers: [
|
||||
GoogleProvider({
|
||||
clientId: process.env.GOOGLE_CLIENT_ID,
|
||||
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
||||
}),
|
||||
GitHubProvider({
|
||||
clientId: process.env.GITHUB_ID,
|
||||
clientSecret: process.env.GITHUB_SECRET,
|
||||
}),
|
||||
CredentialsProvider({
|
||||
name: 'credentials',
|
||||
credentials: {
|
||||
username: { label: 'Username', type: 'text' },
|
||||
password: { label: 'Password', type: 'password' },
|
||||
},
|
||||
authorize: async (credentials) => {
|
||||
// Add your own authentication logic here
|
||||
if (credentials.username === 'admin' && credentials.password === 'admin123') {
|
||||
// Return user object if credentials are valid
|
||||
return Promise.resolve({ id: 1, name: 'Admin', email: 'admin@example.com' });
|
||||
} else {
|
||||
// Return null if credentials are invalid
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
export { handler as GET, handler as POST };
|
||||
1
src/app/api/auth/[...nextauth]/users.json
Normal file
1
src/app/api/auth/[...nextauth]/users.json
Normal file
@@ -0,0 +1 @@
|
||||
[]
|
||||
286
src/app/api/data/route.ts
Normal file
286
src/app/api/data/route.ts
Normal file
@@ -0,0 +1,286 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
import { HeaderItem } from '@/app/types/menu'
|
||||
import { CourseType } from '@/app/types/course'
|
||||
import { Hourtype } from '@/app/types/hour'
|
||||
import { CourseDetailType } from '@/app/types/coursedetail'
|
||||
import { MentorType } from '@/app/types/mentor'
|
||||
import { TestimonialType } from '@/app/types/testimonial'
|
||||
import { FooterLinkType } from '@/app/types/footerlinks'
|
||||
|
||||
const HeaderData: HeaderItem[] = [
|
||||
{ label: 'Home', href: '/#Home' },
|
||||
{ label: 'Courses', href: '/#Courses' },
|
||||
{ label: 'Mentors', href: '/#mentors-section' },
|
||||
{ label: 'Testimonial', href: '/#testimonial-section' },
|
||||
{ label: 'Join', href: '/#join-section' },
|
||||
{ label: 'Contact Us', href: '/#contact' },
|
||||
{ label: 'Docs', href: '/documentation' },
|
||||
]
|
||||
|
||||
const CourseData: CourseType[] = [
|
||||
{ name: 'Mobile Development' },
|
||||
{ name: 'Web Development' },
|
||||
{ name: 'Data Science' },
|
||||
{ name: 'Cloud Computing' },
|
||||
]
|
||||
|
||||
const HourData: Hourtype[] = [
|
||||
{ name: '20hrs in a Month' },
|
||||
{ name: '30hrs in a Month' },
|
||||
{ name: '40hrs in a Month' },
|
||||
{ name: '50hrs in a Month' },
|
||||
]
|
||||
|
||||
const Companiesdata: { imgSrc: string }[] = [
|
||||
{
|
||||
imgSrc: '/images/slickCompany/airbnb.svg',
|
||||
},
|
||||
{
|
||||
imgSrc: '/images/slickCompany/hubspot.svg',
|
||||
},
|
||||
{
|
||||
imgSrc: '/images/slickCompany/microsoft.svg',
|
||||
},
|
||||
{
|
||||
imgSrc: '/images/slickCompany/google.svg',
|
||||
},
|
||||
{
|
||||
imgSrc: '/images/slickCompany/walmart.svg',
|
||||
},
|
||||
{
|
||||
imgSrc: '/images/slickCompany/fedex.svg',
|
||||
},
|
||||
]
|
||||
|
||||
const CourseDetailData: CourseDetailType[] = [
|
||||
{
|
||||
course: 'HTML, CSS, JS',
|
||||
imageSrc: '/images/courses/coursesOne.svg',
|
||||
profession: 'HTML, CSS, Javascript Development',
|
||||
price: '40',
|
||||
category: 'webdevelopment',
|
||||
},
|
||||
{
|
||||
course: 'Node.js',
|
||||
imageSrc: '/images/courses/coursesTwo.svg',
|
||||
profession: 'Backend with Node.js and Express.js',
|
||||
price: '21',
|
||||
category: 'webdevelopment',
|
||||
},
|
||||
{
|
||||
course: 'Database',
|
||||
imageSrc: '/images/courses/coursesThree.svg',
|
||||
profession: 'Learn Mongodb with Mongoose',
|
||||
price: '21',
|
||||
category: 'webdevelopment',
|
||||
},
|
||||
{
|
||||
course: 'React.js',
|
||||
imageSrc: '/images/courses/coursesFour.svg',
|
||||
profession: 'Learn React with Redux toolkit',
|
||||
price: '99',
|
||||
category: 'webdevelopment',
|
||||
},
|
||||
{
|
||||
course: 'React Native',
|
||||
imageSrc: '/images/courses/coursesOne.svg',
|
||||
profession: 'Learn React Native with Node.js',
|
||||
price: '89',
|
||||
category: 'mobiledevelopment',
|
||||
},
|
||||
{
|
||||
course: 'Swift',
|
||||
imageSrc: '/images/courses/coursesThree.svg',
|
||||
profession: 'Learn Swift from Scratch',
|
||||
price: '89',
|
||||
category: 'mobiledevelopment',
|
||||
},
|
||||
{
|
||||
course: 'Flutter',
|
||||
imageSrc: '/images/courses/coursesFour.svg',
|
||||
profession: 'Flutter App Development',
|
||||
price: '69',
|
||||
category: 'mobiledevelopment',
|
||||
},
|
||||
{
|
||||
course: 'Onsen UI',
|
||||
imageSrc: '/images/courses/coursesTwo.svg',
|
||||
profession: 'Learn Onsen Ui with HTML, CSS',
|
||||
price: '69',
|
||||
category: 'mobiledevelopment',
|
||||
},
|
||||
{
|
||||
course: 'TensorFlow',
|
||||
imageSrc: '/images/courses/coursesTwo.svg',
|
||||
profession: 'Learn TensorFlow with SQL',
|
||||
price: '99',
|
||||
category: 'datascience',
|
||||
},
|
||||
{
|
||||
course: 'AWS',
|
||||
imageSrc: '/images/courses/coursesFour.svg',
|
||||
profession: 'AWS Deep Learning AMI',
|
||||
price: '99',
|
||||
category: 'datascience',
|
||||
},
|
||||
{
|
||||
course: 'Bokeh',
|
||||
imageSrc: '/images/courses/coursesOne.svg',
|
||||
profession: 'Learn Bokeh with Python',
|
||||
price: '99',
|
||||
category: 'datascience',
|
||||
},
|
||||
{
|
||||
course: 'Scikit',
|
||||
imageSrc: '/images/courses/coursesThree.svg',
|
||||
profession: 'Scikit with Python Development',
|
||||
price: '89',
|
||||
category: 'datascience',
|
||||
},
|
||||
{
|
||||
course: 'Laas',
|
||||
imageSrc: '/images/courses/coursesThree.svg',
|
||||
profession: 'Infra-as-a-Service',
|
||||
price: '21',
|
||||
category: 'cloudcomputing',
|
||||
},
|
||||
{
|
||||
course: 'Iaas',
|
||||
imageSrc: '/images/courses/coursesFour.svg',
|
||||
profession: 'Info-as-a-Service',
|
||||
price: '29',
|
||||
category: 'cloudcomputing',
|
||||
},
|
||||
{
|
||||
course: 'Paas',
|
||||
imageSrc: '/images/courses/coursesOne.svg',
|
||||
profession: 'Platform-as-a-Service',
|
||||
price: '99',
|
||||
category: 'cloudcomputing',
|
||||
},
|
||||
{
|
||||
course: 'Saas',
|
||||
imageSrc: '/images/courses/coursesTwo.svg',
|
||||
profession: 'Software-as-a-Service',
|
||||
price: '58',
|
||||
category: 'cloudcomputing',
|
||||
},
|
||||
]
|
||||
|
||||
const MentorData: MentorType[] = [
|
||||
{
|
||||
name: 'Senior UX Designer',
|
||||
href: '#',
|
||||
imageSrc: '/images/mentor/boy1.svg',
|
||||
imageAlt: "Front of men's Basic Tee in black.",
|
||||
color: 'Shoo Thar Mein',
|
||||
},
|
||||
{
|
||||
name: 'Photoshop Instructor',
|
||||
href: '#',
|
||||
imageSrc: '/images/mentor/boy2.svg',
|
||||
imageAlt: "Front of men's Basic Tee in black.",
|
||||
color: 'Cristian Doru Barin',
|
||||
},
|
||||
{
|
||||
name: 'SEO Expert',
|
||||
href: '#',
|
||||
imageSrc: '/images/mentor/boy3.svg',
|
||||
imageAlt: "Front of men's Basic Tee in black.",
|
||||
color: 'Tanzeel Ur Rehman',
|
||||
},
|
||||
{
|
||||
name: 'UI/UX Designer',
|
||||
href: '#',
|
||||
imageSrc: '/images/mentor/boy4.svg',
|
||||
imageAlt: "Front of men's Basic Tee in black.",
|
||||
color: 'Andrew Williams',
|
||||
},
|
||||
{
|
||||
name: 'Web Development / Web Design',
|
||||
href: '#',
|
||||
imageSrc: '/images/mentor/boy5.svg',
|
||||
imageAlt: "Front of men's Basic Tee in black.",
|
||||
color: 'Brad Schiff',
|
||||
},
|
||||
{
|
||||
name: 'Adobe Certified Instructor',
|
||||
href: '#',
|
||||
imageSrc: '/images/mentor/girl1.svg',
|
||||
imageAlt: "Front of men's Basic Tee in black.",
|
||||
color: 'Daniel Walter Scott',
|
||||
},
|
||||
]
|
||||
|
||||
const TestimonialData: TestimonialType[] = [
|
||||
{
|
||||
profession: 'UX/UI Designer',
|
||||
name: 'Andrew Williams',
|
||||
imgSrc: '/images/testimonial/user-1.jpg',
|
||||
starimg: '/images/testimonial/stars.png',
|
||||
detail:
|
||||
"I have been a Junior Graphic Designer for more then 10 years. Some things are problem that I had and teach how to solve them. That's why this course is so great!",
|
||||
},
|
||||
{
|
||||
profession: 'UX/UI Designer',
|
||||
name: 'Cristian Doru Barin',
|
||||
imgSrc: '/images/testimonial/user-2.jpg',
|
||||
starimg: '/images/testimonial/stars.png',
|
||||
detail:
|
||||
"I have been a Junior Graphic Designer for more then 10 years. Some things are problem that I had and teach how to solve them. That's why this course is so great!",
|
||||
},
|
||||
{
|
||||
profession: 'UX/UI Designer',
|
||||
name: 'Tanzeel Ur Rehman',
|
||||
imgSrc: '/images/testimonial/user-3.jpg',
|
||||
starimg: '/images/testimonial/stars.png',
|
||||
detail:
|
||||
"I have been a Junior Graphic Designer for more then 10 years. Some things are problem that I had and teach how to solve them. That's why this course is so great!",
|
||||
},
|
||||
{
|
||||
profession: 'UX/UI Designer',
|
||||
name: 'Andrew Williams',
|
||||
imgSrc: '/images/testimonial/user-1.jpg',
|
||||
starimg: '/images/testimonial/stars.png',
|
||||
detail:
|
||||
"I have been a Junior Graphic Designer for more then 10 years. Some things are problem that I had and teach how to solve them. That's why this course is so great!",
|
||||
},
|
||||
]
|
||||
|
||||
const FooterLinkData: FooterLinkType[] = [
|
||||
{
|
||||
section: 'Company',
|
||||
links: [
|
||||
{ label: 'Home', href: '/#Home' },
|
||||
{ label: 'Courses', href: '/#Courses' },
|
||||
{ label: 'Mentors', href: '/#mentors-section' },
|
||||
{ label: 'Testimonial', href: '/#testimonial-section' },
|
||||
{ label: 'Join', href: '/#join-section' },
|
||||
{ label: 'Contact Us', href: '/#contact' },
|
||||
],
|
||||
},
|
||||
{
|
||||
section: 'Support',
|
||||
links: [
|
||||
{ label: 'Help center', href: '/' },
|
||||
{ label: 'Terms of service', href: '/' },
|
||||
{ label: 'Legal', href: '/' },
|
||||
{ label: 'Privacy Policy', href: '/' },
|
||||
{ label: 'Status', href: '/' },
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
export const GET = () => {
|
||||
return NextResponse.json({
|
||||
HeaderData,
|
||||
CourseData,
|
||||
HourData,
|
||||
Companiesdata,
|
||||
CourseDetailData,
|
||||
MentorData,
|
||||
TestimonialData,
|
||||
FooterLinkData,
|
||||
})
|
||||
}
|
||||
324
src/app/components/Auth/ForgotPassword/index.tsx
Normal file
324
src/app/components/Auth/ForgotPassword/index.tsx
Normal file
@@ -0,0 +1,324 @@
|
||||
"use client";
|
||||
import React from "react";
|
||||
import { useState } from "react";
|
||||
import toast from "react-hot-toast";
|
||||
import axios from "axios";
|
||||
import Loader from "@/app/components/Common/Loader";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
|
||||
const ForgotPassword = () => {
|
||||
const [email, setEmail] = useState("");
|
||||
const [loader, setLoader] = useState(false);
|
||||
|
||||
const handleSubmit = async (e: any) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (!email) {
|
||||
toast.error("Please enter your email address.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setLoader(true);
|
||||
|
||||
try {
|
||||
const res = await axios.post("/api/forgot-password/reset", {
|
||||
email: email.toLowerCase(),
|
||||
});
|
||||
|
||||
if (res.status === 404) {
|
||||
toast.error("User not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.status === 200) {
|
||||
toast.success(res.data);
|
||||
setEmail("");
|
||||
}
|
||||
|
||||
setEmail("");
|
||||
setLoader(false);
|
||||
} catch (error: any) {
|
||||
toast.error(error?.response.data);
|
||||
setLoader(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="bg-[#F4F7FF] py-14 dark:bg-dark lg:py-20">
|
||||
<div className="container">
|
||||
<div className="-mx-4 flex flex-wrap">
|
||||
<div className="w-full px-4">
|
||||
<div
|
||||
className="wow fadeInUp relative mx-auto max-w-[525px] overflow-hidden rounded-lg bg-white px-8 py-14 text-center dark:bg-dark-2 sm:px-12 md:px-[60px]"
|
||||
data-wow-delay=".15s"
|
||||
>
|
||||
<div className="mb-10 text-center">
|
||||
<Link href="/" className="mx-auto inline-block max-w-[160px]">
|
||||
<Image
|
||||
src="/images/logo/logo.svg"
|
||||
alt="logo"
|
||||
width={140}
|
||||
height={30}
|
||||
className="dark:hidden"
|
||||
/>
|
||||
<Image
|
||||
src="/images/logo/logo-white.svg"
|
||||
alt="logo"
|
||||
width={140}
|
||||
height={30}
|
||||
className="hidden dark:block"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="mb-[22px]">
|
||||
<input
|
||||
type="email"
|
||||
placeholder="Email"
|
||||
name="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
required
|
||||
className="w-full rounded-md border border-stroke bg-transparent px-5 py-3 text-base text-dark outline-hidden transition placeholder:text-dark-6 focus:border-primary focus-visible:shadow-none dark:border-dark-3 dark:text-white dark:focus:border-primary"
|
||||
/>
|
||||
</div>
|
||||
<div className="">
|
||||
<button
|
||||
type="submit"
|
||||
className="flex w-full cursor-pointer items-center justify-center rounded-md border border-primary bg-primary px-5 py-3 text-base text-white transition duration-300 ease-in-out hover:bg-blue-dark"
|
||||
>
|
||||
Send Email {loader && <Loader />}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div>
|
||||
<span className="absolute right-1 top-1">
|
||||
<svg
|
||||
width="40"
|
||||
height="40"
|
||||
viewBox="0 0 40 40"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<circle
|
||||
cx="1.39737"
|
||||
cy="38.6026"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 1.39737 38.6026)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="1.39737"
|
||||
cy="1.99122"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 1.39737 1.99122)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="13.6943"
|
||||
cy="38.6026"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 13.6943 38.6026)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="13.6943"
|
||||
cy="1.99122"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 13.6943 1.99122)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="25.9911"
|
||||
cy="38.6026"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 25.9911 38.6026)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="25.9911"
|
||||
cy="1.99122"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 25.9911 1.99122)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="38.288"
|
||||
cy="38.6026"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 38.288 38.6026)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="38.288"
|
||||
cy="1.99122"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 38.288 1.99122)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="1.39737"
|
||||
cy="26.3057"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 1.39737 26.3057)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="13.6943"
|
||||
cy="26.3057"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 13.6943 26.3057)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="25.9911"
|
||||
cy="26.3057"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 25.9911 26.3057)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="38.288"
|
||||
cy="26.3057"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 38.288 26.3057)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="1.39737"
|
||||
cy="14.0086"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 1.39737 14.0086)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="13.6943"
|
||||
cy="14.0086"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 13.6943 14.0086)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="25.9911"
|
||||
cy="14.0086"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 25.9911 14.0086)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="38.288"
|
||||
cy="14.0086"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 38.288 14.0086)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span className="absolute bottom-1 left-1">
|
||||
<svg
|
||||
width="29"
|
||||
height="40"
|
||||
viewBox="0 0 29 40"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<circle
|
||||
cx="2.288"
|
||||
cy="25.9912"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 2.288 25.9912)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="14.5849"
|
||||
cy="25.9911"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 14.5849 25.9911)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="26.7216"
|
||||
cy="25.9911"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 26.7216 25.9911)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="2.288"
|
||||
cy="13.6944"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 2.288 13.6944)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="14.5849"
|
||||
cy="13.6943"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 14.5849 13.6943)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="26.7216"
|
||||
cy="13.6943"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 26.7216 13.6943)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="2.288"
|
||||
cy="38.0087"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 2.288 38.0087)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="2.288"
|
||||
cy="1.39739"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 2.288 1.39739)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="14.5849"
|
||||
cy="38.0089"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 14.5849 38.0089)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="26.7216"
|
||||
cy="38.0089"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 26.7216 38.0089)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="14.5849"
|
||||
cy="1.39761"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 14.5849 1.39761)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="26.7216"
|
||||
cy="1.39761"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 26.7216 1.39761)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default ForgotPassword;
|
||||
67
src/app/components/Auth/MagicLink/index.tsx
Normal file
67
src/app/components/Auth/MagicLink/index.tsx
Normal file
@@ -0,0 +1,67 @@
|
||||
"use client";
|
||||
import { useState } from "react";
|
||||
import { signIn } from "next-auth/react";
|
||||
import toast from "react-hot-toast";
|
||||
import { validateEmail } from "@/utils/validateEmail";
|
||||
|
||||
const MagicLink = () => {
|
||||
const [email, setEmail] = useState("");
|
||||
const [loader, setLoader] = useState(false);
|
||||
|
||||
const handleSubmit = (e: any) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (!email) {
|
||||
return toast.error("Please enter your email address.");
|
||||
}
|
||||
|
||||
setLoader(true);
|
||||
if (!validateEmail(email)) {
|
||||
setLoader(false);
|
||||
return toast.error("Please enter a valid email address.");
|
||||
} else {
|
||||
signIn("email", {
|
||||
redirect: false,
|
||||
email: email,
|
||||
})
|
||||
.then((callback) => {
|
||||
if (callback?.ok) {
|
||||
toast.success("Email sent");
|
||||
setEmail("");
|
||||
setLoader(false);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error);
|
||||
toast.error("Unable to send email!");
|
||||
setLoader(false);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="mb-[22px]">
|
||||
<input
|
||||
type="email"
|
||||
placeholder="Email"
|
||||
name="email"
|
||||
required
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value.toLowerCase())}
|
||||
className="w-full rounded-md border border-stroke bg-transparent px-5 py-3 text-base text-dark outline-hidden transition placeholder:text-dark-6 focus:border-primary focus-visible:shadow-none dark:border-dark-3 dark:text-white dark:focus:border-primary"
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-9">
|
||||
<button
|
||||
type="submit"
|
||||
className="flex w-full cursor-pointer items-center justify-center rounded-md border border-primary bg-[#102C46] px-5 py-3 text-base text-white transition duration-300 ease-in-out hover:bg-[#102C46]"
|
||||
>
|
||||
Send Magic Link
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export default MagicLink;
|
||||
368
src/app/components/Auth/ResetPassword/index.tsx
Normal file
368
src/app/components/Auth/ResetPassword/index.tsx
Normal file
@@ -0,0 +1,368 @@
|
||||
"use client";
|
||||
import React, { useState, useEffect } from "react";
|
||||
import axios from "axios";
|
||||
import { useRouter } from "next/navigation";
|
||||
import toast from "react-hot-toast";
|
||||
import Loader from "@/app/components/Common/Loader";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
|
||||
const ResetPassword = ({ token }: { token: string }) => {
|
||||
const [data, setData] = useState({
|
||||
newPassword: "",
|
||||
ReNewPassword: "",
|
||||
});
|
||||
const [loader, setLoader] = useState(false);
|
||||
|
||||
const [user, setUser] = useState({
|
||||
email: "",
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
const verifyToken = async () => {
|
||||
try {
|
||||
const res = await axios.post(`/api/forgot-password/verify-token`, {
|
||||
token,
|
||||
});
|
||||
|
||||
if (res.status === 200) {
|
||||
setUser({
|
||||
email: res.data.email,
|
||||
});
|
||||
}
|
||||
} catch (error: any) {
|
||||
toast.error(error?.response?.data);
|
||||
router.push("/forgot-password");
|
||||
}
|
||||
};
|
||||
|
||||
verifyToken();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setData({
|
||||
...data,
|
||||
[e.target.name]: e.target.value,
|
||||
});
|
||||
};
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
setLoader(true);
|
||||
|
||||
if (data.newPassword === "") {
|
||||
toast.error("Please enter your password.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await axios.post(`/api/forgot-password/update`, {
|
||||
email: user?.email,
|
||||
password: data.newPassword,
|
||||
});
|
||||
|
||||
if (res.status === 200) {
|
||||
toast.success(res.data);
|
||||
setData({ newPassword: "", ReNewPassword: "" });
|
||||
router.push("/signin");
|
||||
}
|
||||
|
||||
setLoader(false);
|
||||
} catch (error: any) {
|
||||
toast.error(error.response.data);
|
||||
setLoader(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="bg-[#F4F7FF] py-14 dark:bg-dark lg:py-20">
|
||||
<div className="container">
|
||||
<div className="-mx-4 flex flex-wrap">
|
||||
<div className="w-full px-4">
|
||||
<div
|
||||
className="wow fadeInUp relative mx-auto max-w-[525px] overflow-hidden rounded-lg bg-white px-8 py-14 text-center dark:bg-dark-2 sm:px-12 md:px-[60px]"
|
||||
data-wow-delay=".15s"
|
||||
>
|
||||
<div className="mb-10 text-center">
|
||||
<Link href="/" className="mx-auto inline-block max-w-[160px]">
|
||||
<Image
|
||||
src="/images/logo/logo.svg"
|
||||
alt="logo"
|
||||
width={140}
|
||||
height={30}
|
||||
className="dark:hidden"
|
||||
/>
|
||||
<Image
|
||||
src="/images/logo/logo-white.svg"
|
||||
alt="logo"
|
||||
width={140}
|
||||
height={30}
|
||||
className="hidden dark:block"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="mb-[22px]">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="New password"
|
||||
name="newPassword"
|
||||
value={data?.newPassword}
|
||||
onChange={handleChange}
|
||||
required
|
||||
className="w-full rounded-md border border-stroke bg-transparent px-5 py-3 text-base text-dark outline-hidden transition placeholder:text-dark-6 focus:border-primary focus-visible:shadow-none dark:border-dark-3 dark:text-white dark:focus:border-primary"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-[22px]">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="New password"
|
||||
name="newPassword"
|
||||
value={data?.newPassword}
|
||||
onChange={handleChange}
|
||||
required
|
||||
className="w-full rounded-md border border-stroke bg-transparent px-5 py-3 text-base text-dark outline-hidden transition placeholder:text-dark-6 focus:border-primary focus-visible:shadow-none dark:border-dark-3 dark:text-white dark:focus:border-primary"
|
||||
/>
|
||||
</div>
|
||||
<div className="">
|
||||
<button
|
||||
type="submit"
|
||||
className="flex w-full cursor-pointer items-center justify-center rounded-md border border-primary bg-primary px-5 py-3 text-base text-white transition duration-300 ease-in-out hover:bg-blue-dark"
|
||||
>
|
||||
Save Password {loader && <Loader />}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div>
|
||||
<span className="absolute right-1 top-1">
|
||||
<svg
|
||||
width="40"
|
||||
height="40"
|
||||
viewBox="0 0 40 40"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<circle
|
||||
cx="1.39737"
|
||||
cy="38.6026"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 1.39737 38.6026)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="1.39737"
|
||||
cy="1.99122"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 1.39737 1.99122)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="13.6943"
|
||||
cy="38.6026"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 13.6943 38.6026)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="13.6943"
|
||||
cy="1.99122"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 13.6943 1.99122)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="25.9911"
|
||||
cy="38.6026"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 25.9911 38.6026)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="25.9911"
|
||||
cy="1.99122"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 25.9911 1.99122)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="38.288"
|
||||
cy="38.6026"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 38.288 38.6026)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="38.288"
|
||||
cy="1.99122"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 38.288 1.99122)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="1.39737"
|
||||
cy="26.3057"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 1.39737 26.3057)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="13.6943"
|
||||
cy="26.3057"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 13.6943 26.3057)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="25.9911"
|
||||
cy="26.3057"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 25.9911 26.3057)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="38.288"
|
||||
cy="26.3057"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 38.288 26.3057)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="1.39737"
|
||||
cy="14.0086"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 1.39737 14.0086)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="13.6943"
|
||||
cy="14.0086"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 13.6943 14.0086)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="25.9911"
|
||||
cy="14.0086"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 25.9911 14.0086)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="38.288"
|
||||
cy="14.0086"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 38.288 14.0086)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<span className="absolute bottom-1 left-1">
|
||||
<svg
|
||||
width="29"
|
||||
height="40"
|
||||
viewBox="0 0 29 40"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<circle
|
||||
cx="2.288"
|
||||
cy="25.9912"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 2.288 25.9912)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="14.5849"
|
||||
cy="25.9911"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 14.5849 25.9911)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="26.7216"
|
||||
cy="25.9911"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 26.7216 25.9911)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="2.288"
|
||||
cy="13.6944"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 2.288 13.6944)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="14.5849"
|
||||
cy="13.6943"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 14.5849 13.6943)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="26.7216"
|
||||
cy="13.6943"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 26.7216 13.6943)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="2.288"
|
||||
cy="38.0087"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 2.288 38.0087)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="2.288"
|
||||
cy="1.39739"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 2.288 1.39739)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="14.5849"
|
||||
cy="38.0089"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 14.5849 38.0089)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="26.7216"
|
||||
cy="38.0089"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 26.7216 38.0089)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="14.5849"
|
||||
cy="1.39761"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 14.5849 1.39761)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
<circle
|
||||
cx="26.7216"
|
||||
cy="1.39761"
|
||||
r="1.39737"
|
||||
transform="rotate(-90 26.7216 1.39761)"
|
||||
fill="#3056D3"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default ResetPassword;
|
||||
107
src/app/components/Auth/SignIn/index.tsx
Normal file
107
src/app/components/Auth/SignIn/index.tsx
Normal file
@@ -0,0 +1,107 @@
|
||||
'use client'
|
||||
import { signIn } from 'next-auth/react'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useState } from 'react'
|
||||
import toast from 'react-hot-toast'
|
||||
import SocialSignIn from '../SocialSignIn'
|
||||
import Logo from '@/app/components/Layout/Header/Logo'
|
||||
import Loader from '@/app/components/Common/Loader'
|
||||
|
||||
const Signin = () => {
|
||||
const router = useRouter()
|
||||
|
||||
const [loginData, setLoginData] = useState({
|
||||
email: '',
|
||||
password: '',
|
||||
checkboxToggle: false,
|
||||
})
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
const loginUser = (e: any) => {
|
||||
e.preventDefault()
|
||||
|
||||
setLoading(true)
|
||||
signIn('credentials', { ...loginData, redirect: false })
|
||||
.then((callback) => {
|
||||
if (callback?.error) {
|
||||
toast.error(callback?.error)
|
||||
console.log(callback?.error)
|
||||
setLoading(false)
|
||||
return
|
||||
}
|
||||
|
||||
if (callback?.ok && !callback?.error) {
|
||||
toast.success('Login successful')
|
||||
setLoading(false)
|
||||
router.push('/')
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
setLoading(false)
|
||||
console.log(err.message)
|
||||
toast.error(err.message)
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='mb-10 text-center mx-auto inline-block max-w-[160px]'>
|
||||
<Logo />
|
||||
</div>
|
||||
|
||||
<SocialSignIn />
|
||||
|
||||
<span className="z-1 relative my-8 block text-center before:content-[''] before:absolute before:h-px before:w-[40%] before:bg-black/20 before:left-0 before:top-3 after:content-[''] after:absolute after:h-px after:w-[40%] after:bg-black/20 after:top-3 after:right-0">
|
||||
<span className='text-body-secondary relative z-10 inline-block px-3 text-base text-black'>
|
||||
OR
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<form onSubmit={(e) => e.preventDefault()}>
|
||||
<div className='mb-[22px]'>
|
||||
<input
|
||||
type='email'
|
||||
placeholder='Email'
|
||||
onChange={(e) =>
|
||||
setLoginData({ ...loginData, email: e.target.value })
|
||||
}
|
||||
className='w-full rounded-md border border-solid bg-transparent px-5 py-3 text-base text-dark outline-hidden transition border-gray-200 placeholder:text-black/30 focus:border-primary focus-visible:shadow-none text-black'
|
||||
/>
|
||||
</div>
|
||||
<div className='mb-[22px]'>
|
||||
<input
|
||||
type='password'
|
||||
placeholder='Password'
|
||||
onChange={(e) =>
|
||||
setLoginData({ ...loginData, password: e.target.value })
|
||||
}
|
||||
className='w-full rounded-md border border-solid bg-transparent px-5 py-3 text-base text-dark outline-hidden transition border-gray-200 placeholder:text-black/30 focus:border-primary focus-visible:shadow-none text-black'
|
||||
/>
|
||||
</div>
|
||||
<div className='mb-9'>
|
||||
<button
|
||||
onClick={loginUser}
|
||||
type='submit'
|
||||
className='bg-primary w-full py-3 rounded-lg text-18 font-medium border text-white border-primary hover:text-primary hover:bg-transparent hover:cursor-pointer transition duration-300 ease-in-out'>
|
||||
Sign In {loading && <Loader />}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<Link
|
||||
href='/'
|
||||
className='mb-2 inline-block text-base text-primary hover:underline'>
|
||||
Forgot Password?
|
||||
</Link>
|
||||
<p className='text-body-secondary text-black text-base'>
|
||||
Not a member yet?{' '}
|
||||
<Link href='/' className='text-primary hover:underline'>
|
||||
Sign Up
|
||||
</Link>
|
||||
</p>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Signin
|
||||
112
src/app/components/Auth/SignUp/index.tsx
Normal file
112
src/app/components/Auth/SignUp/index.tsx
Normal file
@@ -0,0 +1,112 @@
|
||||
'use client'
|
||||
import Link from 'next/link'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import toast from 'react-hot-toast'
|
||||
import SocialSignUp from '../SocialSignUp'
|
||||
import Logo from '@/app/components/Layout/Header/Logo'
|
||||
import { useState } from 'react'
|
||||
import Loader from '@/app/components/Common/Loader'
|
||||
const SignUp = () => {
|
||||
const router = useRouter()
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
const handleSubmit = (e: any) => {
|
||||
e.preventDefault()
|
||||
|
||||
setLoading(true)
|
||||
const data = new FormData(e.currentTarget)
|
||||
const value = Object.fromEntries(data.entries())
|
||||
const finalData = { ...value }
|
||||
|
||||
fetch('/api/register', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(finalData),
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
toast.success('Successfully registered')
|
||||
setLoading(false)
|
||||
router.push('/signin')
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error(err.message)
|
||||
setLoading(false)
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='mb-10 text-center mx-auto inline-block max-w-[160px]'>
|
||||
<Logo />
|
||||
</div>
|
||||
|
||||
<SocialSignUp />
|
||||
|
||||
<span className="z-1 relative my-8 block text-center before:content-[''] before:absolute before:h-px before:w-[40%] before:bg-black/20 before:left-0 before:top-3 after:content-[''] after:absolute after:h-px after:w-[40%] after:bg-black/20 after:top-3 after:right-0">
|
||||
<span className='text-body-secondary relative z-10 inline-block px-3 text-base text-black'>
|
||||
OR
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className='mb-[22px]'>
|
||||
<input
|
||||
type='text'
|
||||
placeholder='Name'
|
||||
name='name'
|
||||
required
|
||||
className='w-full rounded-md border border-solid bg-transparent px-5 py-3 text-base text-dark outline-hidden transition border-gray-200 placeholder:text-black/30 focus:border-primary focus-visible:shadow-none text-black'
|
||||
/>
|
||||
</div>
|
||||
<div className='mb-[22px]'>
|
||||
<input
|
||||
type='email'
|
||||
placeholder='Email'
|
||||
name='email'
|
||||
required
|
||||
className='w-full rounded-md border border-solid bg-transparent px-5 py-3 text-base text-dark outline-hidden transition border-gray-200 placeholder:text-black/30 focus:border-primary focus-visible:shadow-none text-black'
|
||||
/>
|
||||
</div>
|
||||
<div className='mb-[22px]'>
|
||||
<input
|
||||
type='password'
|
||||
placeholder='Password'
|
||||
name='password'
|
||||
required
|
||||
className='w-full rounded-md border border-solid bg-transparent px-5 py-3 text-base text-dark outline-hidden transition border-gray-200 placeholder:text-black/30 focus:border-primary focus-visible:shadow-none text-black'
|
||||
/>
|
||||
</div>
|
||||
<div className='mb-9'>
|
||||
<button
|
||||
type='submit'
|
||||
className='flex w-full items-center text-18 font-medium justify-center rounded-md text-white bg-primary px-5 py-3 text-darkmode transition duration-300 ease-in-out hover:bg-transparent hover:text-primary border-primary border hover:cursor-pointer'>
|
||||
Sign Up {loading && <Loader />}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<p className='text-body-secondary mb-4 text-black text-base'>
|
||||
By creating an account you are agree with our{' '}
|
||||
<Link href='/#' className='text-primary hover:underline'>
|
||||
Privacy
|
||||
</Link>{' '}
|
||||
and{' '}
|
||||
<Link href='/#' className='text-primary hover:underline'>
|
||||
Policy
|
||||
</Link>
|
||||
</p>
|
||||
|
||||
<p className='text-body-secondary text-black text-base'>
|
||||
Already have an account?
|
||||
<Link href='/' className='pl-2 text-primary hover:underline'>
|
||||
Sign In
|
||||
</Link>
|
||||
</p>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default SignUp
|
||||
70
src/app/components/Auth/SocialSignIn.tsx
Normal file
70
src/app/components/Auth/SocialSignIn.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
import React from 'react'
|
||||
import { signIn } from 'next-auth/react'
|
||||
|
||||
const SocialSignIn = () => {
|
||||
return (
|
||||
<>
|
||||
<div className='flex gap-4'>
|
||||
<button
|
||||
onClick={() => signIn('google')}
|
||||
className='flex w-full items-center justify-center gap-2.5 rounded-lg p-3.5 border border-gray-200 text-black hover:bg-neutral-100 hover:cursor-pointer'>
|
||||
Sign In
|
||||
<svg
|
||||
width='23'
|
||||
height='22'
|
||||
viewBox='0 0 23 22'
|
||||
fill='none'
|
||||
xmlns='http://www.w3.org/2000/svg'>
|
||||
<g clipPath='url(#clip0_709_8846)'>
|
||||
<path
|
||||
d='M22.5001 11.2438C22.5134 10.4876 22.4338 9.73256 22.2629 8.995H11.7246V13.0771H17.9105C17.7933 13.7929 17.5296 14.478 17.1352 15.0914C16.7409 15.7047 16.224 16.2335 15.6158 16.646L15.5942 16.7827L18.9264 19.3124L19.1571 19.335C21.2772 17.4161 22.4997 14.5926 22.4997 11.2438'
|
||||
fill='#4285F4'
|
||||
/>
|
||||
<path
|
||||
d='M11.7245 22C14.755 22 17.2992 21.0221 19.1577 19.3355L15.6156 16.6464C14.6679 17.2944 13.3958 17.7467 11.7245 17.7467C10.3051 17.7385 8.92433 17.2926 7.77814 16.472C6.63195 15.6515 5.77851 14.4981 5.33892 13.1755L5.20737 13.1865L1.74255 15.8142L1.69727 15.9376C2.63043 17.7602 4.06252 19.2925 5.83341 20.3631C7.60429 21.4337 9.64416 22.0005 11.7249 22'
|
||||
fill='#34A853'
|
||||
/>
|
||||
<path
|
||||
d='M5.33889 13.1755C5.09338 12.4753 4.96669 11.7404 4.96388 11C4.9684 10.2608 5.09041 9.52685 5.32552 8.8245L5.31927 8.67868L1.81196 6.00867L1.69724 6.06214C0.910039 7.5938 0.5 9.28491 0.5 10.9999C0.5 12.7148 0.910039 14.406 1.69724 15.9376L5.33889 13.1755Z'
|
||||
fill='#FBBC05'
|
||||
/>
|
||||
<path
|
||||
d='M11.7249 4.25337C13.3333 4.22889 14.8888 4.8159 16.065 5.89121L19.2329 2.86003C17.2011 0.992106 14.5106 -0.0328008 11.7249 3.27798e-05C9.64418 -0.000452376 7.60433 0.566279 5.83345 1.63686C4.06256 2.70743 2.63046 4.23965 1.69727 6.06218L5.32684 8.82455C5.77077 7.50213 6.62703 6.34962 7.77491 5.5295C8.9228 4.70938 10.3044 4.26302 11.7249 4.25337Z'
|
||||
fill='#EB4335'
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id='clip0_709_8846'>
|
||||
<rect
|
||||
width='22'
|
||||
height='22'
|
||||
fill='white'
|
||||
transform='translate(0.5)'
|
||||
/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => signIn('github')}
|
||||
className='flex w-full items-center justify-center gap-2.5 rounded-lg p-3.5 border border-gray-200 text-black hover:bg-neutral-100 hover:cursor-pointer'>
|
||||
Sign In
|
||||
<svg
|
||||
width='22'
|
||||
height='22'
|
||||
viewBox='0 0 22 22'
|
||||
fill='none'
|
||||
xmlns='http://www.w3.org/2000/svg'>
|
||||
<path
|
||||
d='M10.9997 1.83331C5.93773 1.83331 1.83301 6.04119 1.83301 11.232C1.83301 15.3847 4.45954 18.9077 8.10178 20.1505C8.55988 20.2375 8.72811 19.9466 8.72811 19.6983C8.72811 19.4743 8.71956 18.7338 8.71567 17.9485C6.16541 18.517 5.6273 16.8395 5.6273 16.8395C5.21032 15.7532 4.60951 15.4644 4.60951 15.4644C3.77785 14.8811 4.6722 14.893 4.6722 14.893C5.59272 14.9593 6.07742 15.8615 6.07742 15.8615C6.89499 17.2984 8.22184 16.883 8.74493 16.6429C8.82718 16.0353 9.06478 15.6208 9.32694 15.3861C7.2909 15.1484 5.15051 14.3425 5.15051 10.7412C5.15051 9.71509 5.5086 8.87661 6.09503 8.21844C5.99984 7.98167 5.68611 7.02577 6.18382 5.73115C6.18382 5.73115 6.95358 5.47855 8.70532 6.69458C9.43648 6.48627 10.2207 6.3819 10.9997 6.37836C11.7787 6.3819 12.5635 6.48627 13.2961 6.69458C15.0457 5.47855 15.8145 5.73115 15.8145 5.73115C16.3134 7.02577 15.9995 7.98167 15.9043 8.21844C16.4921 8.87661 16.8477 9.715 16.8477 10.7412C16.8477 14.351 14.7033 15.146 12.662 15.3786C12.9909 15.6702 13.2838 16.2423 13.2838 17.1191C13.2838 18.3766 13.2732 19.3888 13.2732 19.6983C13.2732 19.9485 13.4382 20.2415 13.9028 20.1492C17.5431 18.905 20.1663 15.3833 20.1663 11.232C20.1663 6.04119 16.0621 1.83331 10.9997 1.83331Z'
|
||||
fill='currentColor'
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default SocialSignIn
|
||||
70
src/app/components/Auth/SocialSignUp.tsx
Normal file
70
src/app/components/Auth/SocialSignUp.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
import React from 'react'
|
||||
import { signIn } from 'next-auth/react'
|
||||
|
||||
const SocialSignUp = () => {
|
||||
return (
|
||||
<>
|
||||
<div className='flex gap-4'>
|
||||
<button
|
||||
onClick={() => signIn('google')}
|
||||
className='flex w-full items-center justify-center gap-2.5 rounded-lg p-3.5 border border-gray-200 text-black hover:bg-neutral-100 hover:cursor-pointer'>
|
||||
Sign Up
|
||||
<svg
|
||||
width='23'
|
||||
height='22'
|
||||
viewBox='0 0 23 22'
|
||||
fill='none'
|
||||
xmlns='http://www.w3.org/2000/svg'>
|
||||
<g clipPath='url(#clip0_709_8846)'>
|
||||
<path
|
||||
d='M22.5001 11.2438C22.5134 10.4876 22.4338 9.73256 22.2629 8.995H11.7246V13.0771H17.9105C17.7933 13.7929 17.5296 14.478 17.1352 15.0914C16.7409 15.7047 16.224 16.2335 15.6158 16.646L15.5942 16.7827L18.9264 19.3124L19.1571 19.335C21.2772 17.4161 22.4997 14.5926 22.4997 11.2438'
|
||||
fill='#4285F4'
|
||||
/>
|
||||
<path
|
||||
d='M11.7245 22C14.755 22 17.2992 21.0221 19.1577 19.3355L15.6156 16.6464C14.6679 17.2944 13.3958 17.7467 11.7245 17.7467C10.3051 17.7385 8.92433 17.2926 7.77814 16.472C6.63195 15.6515 5.77851 14.4981 5.33892 13.1755L5.20737 13.1865L1.74255 15.8142L1.69727 15.9376C2.63043 17.7602 4.06252 19.2925 5.83341 20.3631C7.60429 21.4337 9.64416 22.0005 11.7249 22'
|
||||
fill='#34A853'
|
||||
/>
|
||||
<path
|
||||
d='M5.33889 13.1755C5.09338 12.4753 4.96669 11.7404 4.96388 11C4.9684 10.2608 5.09041 9.52685 5.32552 8.8245L5.31927 8.67868L1.81196 6.00867L1.69724 6.06214C0.910039 7.5938 0.5 9.28491 0.5 10.9999C0.5 12.7148 0.910039 14.406 1.69724 15.9376L5.33889 13.1755Z'
|
||||
fill='#FBBC05'
|
||||
/>
|
||||
<path
|
||||
d='M11.7249 4.25337C13.3333 4.22889 14.8888 4.8159 16.065 5.89121L19.2329 2.86003C17.2011 0.992106 14.5106 -0.0328008 11.7249 3.27798e-05C9.64418 -0.000452376 7.60433 0.566279 5.83345 1.63686C4.06256 2.70743 2.63046 4.23965 1.69727 6.06218L5.32684 8.82455C5.77077 7.50213 6.62703 6.34962 7.77491 5.5295C8.9228 4.70938 10.3044 4.26302 11.7249 4.25337Z'
|
||||
fill='#EB4335'
|
||||
/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id='clip0_709_8846'>
|
||||
<rect
|
||||
width='22'
|
||||
height='22'
|
||||
fill='white'
|
||||
transform='translate(0.5)'
|
||||
/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => signIn('github')}
|
||||
className='flex w-full items-center justify-center gap-2.5 rounded-lg p-3.5 border border-gray-200 text-black hover:bg-neutral-100 hover:cursor-pointer'>
|
||||
Sign Up
|
||||
<svg
|
||||
width='22'
|
||||
height='22'
|
||||
viewBox='0 0 22 22'
|
||||
fill='none'
|
||||
xmlns='http://www.w3.org/2000/svg'>
|
||||
<path
|
||||
d='M10.9997 1.83331C5.93773 1.83331 1.83301 6.04119 1.83301 11.232C1.83301 15.3847 4.45954 18.9077 8.10178 20.1505C8.55988 20.2375 8.72811 19.9466 8.72811 19.6983C8.72811 19.4743 8.71956 18.7338 8.71567 17.9485C6.16541 18.517 5.6273 16.8395 5.6273 16.8395C5.21032 15.7532 4.60951 15.4644 4.60951 15.4644C3.77785 14.8811 4.6722 14.893 4.6722 14.893C5.59272 14.9593 6.07742 15.8615 6.07742 15.8615C6.89499 17.2984 8.22184 16.883 8.74493 16.6429C8.82718 16.0353 9.06478 15.6208 9.32694 15.3861C7.2909 15.1484 5.15051 14.3425 5.15051 10.7412C5.15051 9.71509 5.5086 8.87661 6.09503 8.21844C5.99984 7.98167 5.68611 7.02577 6.18382 5.73115C6.18382 5.73115 6.95358 5.47855 8.70532 6.69458C9.43648 6.48627 10.2207 6.3819 10.9997 6.37836C11.7787 6.3819 12.5635 6.48627 13.2961 6.69458C15.0457 5.47855 15.8145 5.73115 15.8145 5.73115C16.3134 7.02577 15.9995 7.98167 15.9043 8.21844C16.4921 8.87661 16.8477 9.715 16.8477 10.7412C16.8477 14.351 14.7033 15.146 12.662 15.3786C12.9909 15.6702 13.2838 16.2423 13.2838 17.1191C13.2838 18.3766 13.2732 19.3888 13.2732 19.6983C13.2732 19.9485 13.4382 20.2415 13.9028 20.1492C17.5431 18.905 20.1663 15.3833 20.1663 11.232C20.1663 6.04119 16.0621 1.83331 10.9997 1.83331Z'
|
||||
fill='currentColor'
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default SocialSignUp
|
||||
27
src/app/components/Breadcrumb/index.tsx
Normal file
27
src/app/components/Breadcrumb/index.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import React, { FC } from 'react';
|
||||
import Link from 'next/link';
|
||||
|
||||
interface BreadcrumbProps {
|
||||
links: { href: string; text: string }[];
|
||||
}
|
||||
|
||||
const Breadcrumb: FC<BreadcrumbProps> = ({ links }) => {
|
||||
const lastIndex = links.length - 1;
|
||||
return (
|
||||
<div className="flex items-baseline flex-wrap justify-center my-[0.9375rem] mx-0">
|
||||
{links.map((link, index) => (
|
||||
<React.Fragment key={index}>
|
||||
{index !== lastIndex ? (
|
||||
<Link href={link.href} className="no-underline flex items-center text-midnight_text dark:text-white dark:text-opacity-70 font-normal md:text-21 text-18 hover:underline after:relative after:content-[''] after:ml-2.5 after:mr-[0.8125rem] after:my-0 after:inline-block after:top-[0.0625rem] after:w-2 after:h-2 after:border-r-2 after:border-solid after:border-b-2 after:border-midnight_text dark:after:border-white after:-rotate-45">
|
||||
{link.text}
|
||||
</Link>
|
||||
) : (
|
||||
<span className="dark:text-white text-midnight_text md:text-21 text-18 mx-2.5">{link.text}</span>
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Breadcrumb;
|
||||
46
src/app/components/Common/Breadcrumb.tsx
Normal file
46
src/app/components/Common/Breadcrumb.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
import Link from "next/link";
|
||||
import { BreadcrumbProps } from "../../types/breadcrumb"; // Adjust the import path based on your project structure
|
||||
|
||||
const Breadcrumb: React.FC<BreadcrumbProps> = ({
|
||||
pageName,
|
||||
pageDescription,
|
||||
}) => {
|
||||
return (
|
||||
<div className="dark:bg-darkmode relative z-10 overflow-hidden pb-[60px] pt-[120px] md:pt-[130px] lg:pt-[160px]">
|
||||
<div className="from-stroke/0 via-stroke to-stroke/0 dark:via-dark-3 absolute bottom-0 left-0 h-px w-full bg-linear-to-r"></div>
|
||||
<div className="container mx-auto">
|
||||
<div className="-mx-4 flex flex-wrap items-center">
|
||||
<div className="w-full px-4">
|
||||
<div className="text-center">
|
||||
<h1 className="text-black mb-4 text-3xl font-bold sm:text-4xl md:text-[40px] md:leading-[1.2] dark:text-white">
|
||||
{pageName}
|
||||
</h1>
|
||||
<p className="text-black dark:text-black-6 mb-5 text-base">
|
||||
{pageDescription}
|
||||
</p>
|
||||
|
||||
<ul className="flex items-center justify-center gap-[10px]">
|
||||
<li>
|
||||
<Link
|
||||
href="/"
|
||||
className="text-black flex items-center gap-[10px] text-base font-medium dark:text-white dark:text-opacity-50"
|
||||
>
|
||||
Home
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<p className="text-body-color flex items-center gap-[10px] text-base font-medium">
|
||||
<span className="text-body-color dark:text-white dark:text-opacity-50"> / </span>
|
||||
{pageName}
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Breadcrumb;
|
||||
11
src/app/components/Common/Loader.tsx
Normal file
11
src/app/components/Common/Loader.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from "react";
|
||||
|
||||
const Loader = () => {
|
||||
return (
|
||||
<span
|
||||
className={`ml-1.5 h-4 w-4 animate-spin rounded-full border-2 border-solid border-white border-t-transparent dark:border-t-transparent`}
|
||||
></span>
|
||||
);
|
||||
};
|
||||
|
||||
export default Loader;
|
||||
11
src/app/components/Common/PreLoader.tsx
Normal file
11
src/app/components/Common/PreLoader.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
import React from "react";
|
||||
|
||||
const PreLoader = () => {
|
||||
return (
|
||||
<div className="fixed left-0 top-0 z-999999 flex h-screen w-screen items-center justify-center bg-white">
|
||||
<div className="h-16 w-16 animate-spin rounded-full border-4 border-solid border-primary border-t-transparent"></div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PreLoader;
|
||||
9
src/app/components/Common/ScrollUp.tsx
Normal file
9
src/app/components/Common/ScrollUp.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
|
||||
export default function ScrollUp() {
|
||||
useEffect(() => window.document.scrollingElement?.scrollTo(0, 0), []);
|
||||
|
||||
return null;
|
||||
}
|
||||
181
src/app/components/Contact/Form/index.tsx
Normal file
181
src/app/components/Contact/Form/index.tsx
Normal file
@@ -0,0 +1,181 @@
|
||||
'use client'
|
||||
import React from 'react'
|
||||
import { useState, useEffect } from 'react'
|
||||
|
||||
const ContactForm = () => {
|
||||
const [formData, setFormData] = useState({
|
||||
firstname: '',
|
||||
lastname: '',
|
||||
email: '',
|
||||
phnumber: '',
|
||||
Message: '',
|
||||
})
|
||||
const [submitted, setSubmitted] = useState(false)
|
||||
const [showThanks, setShowThanks] = useState(false)
|
||||
const [loader, setLoader] = useState(false)
|
||||
const [isFormValid, setIsFormValid] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
const isValid = Object.values(formData).every(
|
||||
(value) => value.trim() !== ''
|
||||
)
|
||||
setIsFormValid(isValid)
|
||||
}, [formData])
|
||||
const handleChange = (e: any) => {
|
||||
const { name, value } = e.target
|
||||
setFormData((prevData) => ({
|
||||
...prevData,
|
||||
[name]: value,
|
||||
}))
|
||||
}
|
||||
const reset = () => {
|
||||
formData.firstname = ''
|
||||
formData.lastname = ''
|
||||
formData.email = ''
|
||||
formData.phnumber = ''
|
||||
formData.Message = ''
|
||||
}
|
||||
const handleSubmit = async (e: any) => {
|
||||
e.preventDefault()
|
||||
setLoader(true)
|
||||
|
||||
// TODO: 替换为你自己的邮箱地址
|
||||
fetch('https://formsubmit.co/ajax/your-email@example.com', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
Name: formData.firstname,
|
||||
LastName: formData.lastname,
|
||||
Email: formData.email,
|
||||
PhoneNo: formData.phnumber,
|
||||
Message: formData.Message,
|
||||
}),
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
if (data.success) {
|
||||
setSubmitted(true)
|
||||
setShowThanks(true)
|
||||
reset()
|
||||
|
||||
setTimeout(() => {
|
||||
setShowThanks(false)
|
||||
}, 5000)
|
||||
}
|
||||
|
||||
reset()
|
||||
})
|
||||
.catch((error) => {
|
||||
setLoader(false)
|
||||
console.log(error.message)
|
||||
})
|
||||
}
|
||||
return (
|
||||
<section id='contact'>
|
||||
<div className='container'>
|
||||
<div className='relative'>
|
||||
<h2 className='mb-9 font-bold tracking-tight'>Get in Touch</h2>
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
className='flex flex-wrap w-full m-auto justify-between'>
|
||||
<div className='sm:flex gap-3 w-full'>
|
||||
<div className='mx-0 my-2.5 flex-1'>
|
||||
<label htmlFor='fname' className='pb-3 inline-block text-base'>
|
||||
First Name
|
||||
</label>
|
||||
<input
|
||||
id='fname'
|
||||
type='text'
|
||||
name='firstname'
|
||||
value={formData.firstname}
|
||||
onChange={handleChange}
|
||||
placeholder='John'
|
||||
className='w-full text-base px-4 rounded-2xl py-2.5 border-solid border transition-all duration-500 focus:border-primary focus:outline-0'
|
||||
/>
|
||||
</div>
|
||||
<div className='mx-0 my-2.5 flex-1'>
|
||||
<label htmlFor='lname' className='pb-3 inline-block text-base'>
|
||||
Last Name
|
||||
</label>
|
||||
<input
|
||||
id='lname'
|
||||
type='text'
|
||||
name='lastname'
|
||||
value={formData.lastname}
|
||||
onChange={handleChange}
|
||||
placeholder='Doe'
|
||||
className='w-full text-base px-4 rounded-2xl py-2.5 border-solid border transition-all duration-500 focus:border-primary focus:outline-0'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='sm:flex gap-3 w-full'>
|
||||
<div className='mx-0 my-2.5 flex-1'>
|
||||
<label htmlFor='email' className='pb-3 inline-block text-base'>
|
||||
Email address
|
||||
</label>
|
||||
<input
|
||||
id='email'
|
||||
type='email'
|
||||
name='email'
|
||||
value={formData.email}
|
||||
onChange={handleChange}
|
||||
placeholder='john.doe@example.com'
|
||||
className='w-full text-base px-4 rounded-2xl py-2.5 border-solid border transition-all duration-500 focus:border-primary focus:outline-0'
|
||||
/>
|
||||
</div>
|
||||
<div className='mx-0 my-2.5 flex-1'>
|
||||
<label
|
||||
htmlFor='Phnumber'
|
||||
className='pb-3 inline-block text-base'>
|
||||
Phone Number
|
||||
</label>
|
||||
<input
|
||||
id='Phnumber'
|
||||
type='tel'
|
||||
name='phnumber'
|
||||
placeholder='+1234567890'
|
||||
value={formData.phnumber}
|
||||
onChange={handleChange}
|
||||
className='w-full text-base px-4 rounded-2xl py-2.5 border-solid border transition-all duration-500 focus:border-primary focus:outline-0'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='w-full mx-0 my-2.5 flex-1'>
|
||||
<label htmlFor='message' className='text-base inline-block'>
|
||||
Message
|
||||
</label>
|
||||
<textarea
|
||||
id='message'
|
||||
name='Message'
|
||||
value={formData.Message}
|
||||
onChange={handleChange}
|
||||
className='w-full mt-2 rounded-2xl px-5 py-3 border-solid border transition-all duration-500 focus:border-primary focus:outline-0'
|
||||
placeholder='Anything else you wanna communicate'></textarea>
|
||||
</div>
|
||||
<div className='mx-0 my-2.5 w-full'>
|
||||
<button
|
||||
type='submit'
|
||||
disabled={!isFormValid || loader}
|
||||
className={`border leading-none px-6 text-lg font-medium py-4 rounded-full
|
||||
${
|
||||
!isFormValid || loader
|
||||
? 'bg-gray-300 text-gray-500 cursor-not-allowed'
|
||||
: 'bg-primary border-primary text-white hover:bg-transparent hover:text-primary cursor-pointer'
|
||||
}`}>
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{showThanks && (
|
||||
<div className='text-white bg-primary rounded-full px-4 text-lg mb-4.5 mt-1 absolute flex items-center gap-2'>
|
||||
Thank you for contacting us! We will get back to you soon.
|
||||
<div className='w-3 h-3 rounded-full animate-spin border-2 border-solid border-white border-t-transparent'></div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default ContactForm
|
||||
38
src/app/components/Documentation/ColorConfiguraion.tsx
Normal file
38
src/app/components/Documentation/ColorConfiguraion.tsx
Normal file
@@ -0,0 +1,38 @@
|
||||
export const ColorConfiguration = () => {
|
||||
return (
|
||||
<>
|
||||
<h3 className=" text-xl font-semibold mt-8 text-black">Colors</h3>
|
||||
<div className="p-6 rounded-md border mt-4 border-dark_border border-opacity-60">
|
||||
<p className="text-base font-medium text-muted text-opacity-60">
|
||||
<span className="font-semibold text-lg text-black">
|
||||
1. Override Colors
|
||||
</span>{" "}
|
||||
<br />
|
||||
For any change in colors : tailwind.config.ts
|
||||
</p>
|
||||
<div className="py-4 px-5 rounded-md bg-black mt-8">
|
||||
<p className="text-sm text-white/60 flex flex-col gap-2">
|
||||
<span>--color-primary: #611f69;</span>
|
||||
<span>--color-cream: #fcf5ef;</span>
|
||||
<span>--color-success: #6b9f36;</span>
|
||||
<span>--color-orange: #f9cd92;</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-6 rounded-md border mt-4 border-dark_border border-opacity-60">
|
||||
<p className="text-base font-medium text-muted text-opacity-60">
|
||||
<span className="font-semibold text-lg text-black">
|
||||
2. Override Theme Colors
|
||||
</span>{" "}
|
||||
<br />
|
||||
For change , go to : tailwind.config.ts
|
||||
</p>
|
||||
<div className="py-4 px-5 rounded-md bg-black mt-8">
|
||||
<p className="text-sm text-white/60 flex flex-col gap-2">
|
||||
<span>--color-primary: #611f69;</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
16
src/app/components/Documentation/Configuration.tsx
Normal file
16
src/app/components/Documentation/Configuration.tsx
Normal file
@@ -0,0 +1,16 @@
|
||||
import { ColorConfiguration } from "./ColorConfiguraion"
|
||||
import { LogoConfiguration } from "./LogoConfiguration"
|
||||
import { TypographyConfiguration } from "./TypographyConfiguration"
|
||||
|
||||
export const Configuration = () => {
|
||||
return (
|
||||
<>
|
||||
<div className="pb-10 md:scroll-m-[180px] scroll-m-28" id="configuration">
|
||||
<h3 className="text-2xl font-semibold mt-4 text-black" >Project Configuration</h3>
|
||||
<ColorConfiguration />
|
||||
<TypographyConfiguration />
|
||||
<LogoConfiguration />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
58
src/app/components/Documentation/DocNavigation.tsx
Normal file
58
src/app/components/Documentation/DocNavigation.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
"use client";
|
||||
import Link from "next/link";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export const DocNavigation = () => {
|
||||
const [navItem, setNavItem] = useState("version");
|
||||
|
||||
function getNavItem(item: string) {
|
||||
setNavItem(item);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
console.log(navItem);
|
||||
}, [navItem]);
|
||||
|
||||
const DocsNav = [
|
||||
{
|
||||
id: 1,
|
||||
navItem: "Package Versions",
|
||||
hash: "version",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
navItem: "Pacakge Structure",
|
||||
hash: "structure",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
navItem: "Quick Start",
|
||||
hash: "start",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
navItem: "Project Configuration",
|
||||
hash: "configuration",
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-0.5 mt-4 items-start fixed pe-4">
|
||||
{DocsNav.map((item) => {
|
||||
return (
|
||||
<Link
|
||||
key={item.id}
|
||||
href={`#${item.hash}`}
|
||||
onClick={() => getNavItem(item.hash)}
|
||||
className={`py-2.5 hover:bg-primary/20 hover:text-primary dark:hover:text-primary xl:min-w-60 lg:min-w-52 min-w-full px-4 rounded-md text-base font-medium ${item.hash === navItem
|
||||
? "bg-primary text-white"
|
||||
: "text-black/60"
|
||||
}`}
|
||||
>
|
||||
{item.navItem}
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
25
src/app/components/Documentation/Documentation.tsx
Normal file
25
src/app/components/Documentation/Documentation.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import { Configuration } from './Configuration'
|
||||
import { DocNavigation } from './DocNavigation'
|
||||
import { Introduction } from './Introduction'
|
||||
import { PackageStructure } from './PackageStructure'
|
||||
import { QuickStart } from './QuickStart'
|
||||
|
||||
export const Documentation = () => {
|
||||
return (
|
||||
<div className=''>
|
||||
<div className='container mx-auto max-w-7xl p-6 lg:pt-44 pt-16'>
|
||||
<div className='grid grid-cols-12 gap-6'>
|
||||
<div className='lg:col-span-3 col-span-12 lg:block hidden'>
|
||||
<DocNavigation />
|
||||
</div>
|
||||
<div className='lg:col-span-9 col-span-12'>
|
||||
<Introduction />
|
||||
<PackageStructure />
|
||||
<QuickStart />
|
||||
<Configuration />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
139
src/app/components/Documentation/Introduction.tsx
Normal file
139
src/app/components/Documentation/Introduction.tsx
Normal file
@@ -0,0 +1,139 @@
|
||||
'use client'
|
||||
import Image from 'next/image'
|
||||
// import nextImg from '/public/images/documentation/Categories=Nextjs.svg'
|
||||
// import reactImg from '/public/images/documentation/Categories=React.svg'
|
||||
// import tailwindImg from '/public/images/documentation/Categories=Tailwind.svg'
|
||||
// import nextauthImg from '/public/images/documentation/nextauth.png'
|
||||
// import typescriptImg from '/public/images/documentation/Categories=Typescript.svg'
|
||||
// import axiosImg from '/public/images/documentation/axios.svg'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import { useState } from 'react'
|
||||
import { DocNavigation } from './DocNavigation'
|
||||
|
||||
export const Introduction = () => {
|
||||
const [docNavbarOpen, setDocNavbarOpen] = useState(false)
|
||||
const PackageVersions = [
|
||||
{
|
||||
id: '1',
|
||||
packageName: 'NextJs',
|
||||
img: '/images/documentation/Categories=Nextjs.svg',
|
||||
version: '15.5.9',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
packageName: 'React',
|
||||
img: '/images/documentation/Categories=React.svg',
|
||||
version: '19.2.3',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
packageName: 'Tailwindcss',
|
||||
img: '/images/documentation/Categories=Tailwind.svg',
|
||||
version: '4',
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
packageName: 'NextAuth',
|
||||
img: '/images/documentation/nextauth.png',
|
||||
version: '4.24.11',
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
packageName: 'Typescript',
|
||||
img: '/images/documentation/Categories=Typescript.svg',
|
||||
version: '5',
|
||||
},
|
||||
]
|
||||
return (
|
||||
<>
|
||||
<div id='version' className='md:scroll-m-[180px] scroll-m-28'>
|
||||
{docNavbarOpen && (
|
||||
<div
|
||||
className='fixed top-0 left-0 w-full h-full bg-black bg-opacity-50 z-40'
|
||||
onClick={() => setDocNavbarOpen(false)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className='flex item-center justify-between'>
|
||||
<h3 className=' text-2xl mt-4 font-semibold mb-6 text-black'>
|
||||
Package Versions
|
||||
</h3>
|
||||
<button onClick={() => setDocNavbarOpen(true)} className='p-0'>
|
||||
{' '}
|
||||
<Icon icon='gg:menu-right' className='text-3xl lg:hidden block' />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className='w-full flex justify-between lg:gap-0 gap-6 lg:flex-nowrap flex-wrap p-6 rounded-md border border-dark_border border-opacity-60'>
|
||||
{PackageVersions &&
|
||||
PackageVersions.map((item) => {
|
||||
return (
|
||||
<div
|
||||
key={item.id}
|
||||
className='lg:w-1/5 md:w-full text-center lg:border-b-0 border-b lg:border-e lg:last:border-e-0 last:border-b-0 border-dark_border border-opacity-60'>
|
||||
<Image
|
||||
src={item.img}
|
||||
width={64}
|
||||
height={64}
|
||||
alt='npm-package'
|
||||
className=' mx-auto w-10 h-10 '
|
||||
/>
|
||||
<h5 className='text-2xl font-bold mt-3.5 text-black'>{`v${item.version}`}</h5>
|
||||
<p className='text-base font-medium text-muted'>
|
||||
{item.packageName}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<div className='mt-5'>
|
||||
<p className='text-base font-medium text-muted text-opacity-60'>
|
||||
SiEducational Tailwind NextJs Template is built with Tailwindcss and
|
||||
Nextjs.
|
||||
</p>
|
||||
<p className='text-base font-medium text-muted text-opacity-60'>
|
||||
These theme is ready to use and you can totally customize as per
|
||||
your requirement.
|
||||
</p>
|
||||
<p className='text-base font-medium text-muted text-opacity-60'>
|
||||
For Customize, You should have knowledge of NextJs, ReactJs,
|
||||
Tailwind and JSX to be able to modify these template.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={`lg:hidden block fixed top-0 right-0 h-full w-full bg-white dark:bg-dark shadow-lg transform transition-transform duration-300 max-w-xs ${
|
||||
docNavbarOpen ? 'translate-x-0' : 'translate-x-full'
|
||||
} z-50`}>
|
||||
<div className='flex items-center justify-between p-4'>
|
||||
<h2 className='text-lg font-bold text-midnight_text dark:text-black'>
|
||||
Docs Menu
|
||||
</h2>
|
||||
<button
|
||||
onClick={() => setDocNavbarOpen(false)}
|
||||
aria-label='Close mobile menu'>
|
||||
<svg
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
width='24'
|
||||
height='24'
|
||||
viewBox='0 0 24 24'
|
||||
className='dark:text-black'>
|
||||
<path
|
||||
fill='none'
|
||||
stroke='currentColor'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
strokeWidth='2'
|
||||
d='M6 18L18 6M6 6l12 12'
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<nav className='px-4'>
|
||||
<DocNavigation />
|
||||
</nav>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
32
src/app/components/Documentation/LogoConfiguration.tsx
Normal file
32
src/app/components/Documentation/LogoConfiguration.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
export const LogoConfiguration = () => {
|
||||
return (
|
||||
<>
|
||||
<h3 className=" text-xl font-semibold mt-8 text-black">Logo</h3>
|
||||
<div className="p-6 rounded-md border mt-4 border-dark_border border-opacity-60">
|
||||
<p className="text-base font-medium text-muted text-opacity-60 flex lg:flex-row flex-col">
|
||||
1. Change Logo over here :{" "}
|
||||
<span className="font-semibold text-base overflow-x-auto">
|
||||
{" "}
|
||||
src/components/Layout/Header/Logo/index.tsx
|
||||
</span>{" "}
|
||||
</p>
|
||||
<div className="py-4 px-3 rounded-md bg-black mt-8">
|
||||
<div className="text-sm text-white/60">
|
||||
<p><Link href="/"></p>
|
||||
<p><Image</p>
|
||||
<p>src="/images/logo/logo.svg"</p>
|
||||
<p>alt="logo"</p>
|
||||
<p>width={160}</p>
|
||||
<p>height={50}</p>
|
||||
<p>quality={100}</p>
|
||||
<p>
|
||||
style={width: "auto", height:
|
||||
"auto"}
|
||||
</p>
|
||||
<p>/></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
469
src/app/components/Documentation/PackageStructure.tsx
Normal file
469
src/app/components/Documentation/PackageStructure.tsx
Normal file
@@ -0,0 +1,469 @@
|
||||
import { Icon } from "@iconify/react/dist/iconify.js";
|
||||
import Image from "next/image";
|
||||
import tline from "/public/images/svgs/T-Line.svg";
|
||||
import t_half_line from "/public/images/svgs/T-half_line.svg";
|
||||
import straight_line from "/public/images/svgs/straight_group.svg";
|
||||
import small_straight_line from "/public/images/svgs/smal_straight_line.svg";
|
||||
|
||||
export const PackageStructure = () => {
|
||||
const Counts = [
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
|
||||
21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
|
||||
];
|
||||
return (
|
||||
<div id="structure" className="md:scroll-m-[130px] scroll-m-28">
|
||||
<h3 className="text-2xl font-semibold mt-8 text-black">
|
||||
Pacakge Structure
|
||||
</h3>
|
||||
<div className="rounded-md p-6 pt-3 border border-dark_border border-opacity-60 mt-6">
|
||||
<div className="flex items-center gap-4">
|
||||
<h5 className="text-base font-medium text-muted mt-3 mb-1">
|
||||
SiEducational Tailwind NextJs Template
|
||||
</h5>
|
||||
</div>
|
||||
<ul className="ps-0 md:ps-5 list-unstyled">
|
||||
<li className="py-2">
|
||||
<div className="flex items-center gap-3">
|
||||
<p className="text-xl text-black">|—</p>
|
||||
<span className="font-medium text-muted">
|
||||
<Icon
|
||||
icon="tabler:folder"
|
||||
className="text-primary text-base inline-block me-2"
|
||||
/>
|
||||
packages
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<div className="flex flex-col justify-between gap-2 mt-2">
|
||||
{Counts.slice(0, 22).map((item) => {
|
||||
return <p className="text-xl text-black">|</p>;
|
||||
})}
|
||||
</div>
|
||||
<ul className="ps-5 md:ps-5 list-unstyled">
|
||||
<li className="py-0">
|
||||
<ul className="ps-2 ps-md-3 list-unstyled">
|
||||
<li className="py-2">
|
||||
<ul className="ps-0 md:ps-5 list-unstyled">
|
||||
<li className="py-2">
|
||||
<div className="flex items-center gap-3">
|
||||
<p className="text-xl text-black">|—</p>
|
||||
<span className="font-medium text-muted">
|
||||
<Icon
|
||||
icon="tabler:folder"
|
||||
className="text-primary text-base inline-block me-2"
|
||||
/>
|
||||
markdown
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
<li className="py-2">
|
||||
<div className="flex items-center gap-3">
|
||||
<p className="text-xl text-black">|—</p>
|
||||
<span className="font-medium text-muted">
|
||||
<Icon
|
||||
icon="tabler:folder"
|
||||
className="text-primary text-base inline-block me-2"
|
||||
/>
|
||||
public
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
<li className="py-2">
|
||||
<div className="flex items-center gap-3">
|
||||
<p className="text-xl text-black">|—</p>
|
||||
<span className="font-medium text-muted">
|
||||
<Icon
|
||||
icon="tabler:folder"
|
||||
className="text-primary text-base inline-block me-2"
|
||||
/>
|
||||
src
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<div className="flex flex-col justify-between gap-2 mt-2">
|
||||
{Counts.slice(0, 22).map((item) => {
|
||||
return <p className="text-xl text-black">|</p>;
|
||||
})}
|
||||
</div>
|
||||
<ul className="ps-5 md:ps-12 list-unstyled">
|
||||
<li className="py-2">
|
||||
<div className="flex items-center gap-3">
|
||||
<p className="text-xl text-black">|—</p>
|
||||
<span className="font-medium text-muted">
|
||||
<Icon
|
||||
icon="tabler:folder"
|
||||
className="text-primary text-base inline-block me-2"
|
||||
/>
|
||||
app
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<div className="flex flex-col justify-between gap-2 mt-2">
|
||||
{Counts.slice(0, 16).map((item) => {
|
||||
return (
|
||||
<p className="text-xl text-black">|</p>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<ul className="ps-5 md:ps-12 list-unstyled red">
|
||||
<li className="py-2">
|
||||
<div className="flex items-center gap-3">
|
||||
<p className="text-xl text-black">|—</p>
|
||||
<span className="font-medium text-muted">
|
||||
<Icon
|
||||
icon="tabler:folder"
|
||||
className="text-primary text-base inline-block me-2"
|
||||
/>
|
||||
(site)
|
||||
</span>{" "}
|
||||
<span className="fs-9 text-muted ms-4">
|
||||
(Contains all the pages)
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<div className="flex flex-col justify-between gap-2 mt-2">
|
||||
{Counts.slice(0, 5).map(
|
||||
(item, index) => {
|
||||
return (
|
||||
<p
|
||||
key={index}
|
||||
className="text-xl text-black"
|
||||
>
|
||||
|
|
||||
</p>
|
||||
);
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
|
||||
<ul className="ps-5 md:ps-12 list-unstyled">
|
||||
<li className="py-2">
|
||||
<div className="flex items-center gap-3">
|
||||
<p className="text-xl text-black">
|
||||
|—
|
||||
</p>
|
||||
<span className="font-medium text-muted">
|
||||
<Icon
|
||||
icon="tabler:folder"
|
||||
className="text-primary text-base inline-block me-2"
|
||||
/>
|
||||
(auth)
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<div className="flex flex-col justify-between gap-1 mt-2">
|
||||
{Counts.slice(0, 2).map(
|
||||
(item) => {
|
||||
return (
|
||||
<p className="text-xl text-black">
|
||||
|
|
||||
</p>
|
||||
);
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
<ul className="ps-5 md:ps-12 list-unstyled">
|
||||
<li className="py-2">
|
||||
<div className="flex items-center gap-8">
|
||||
<p className="text-xl text-black">
|
||||
|
|
||||
</p>
|
||||
<div className="flex items-center gap-3">
|
||||
<p className="text-xl text-black">
|
||||
|—
|
||||
</p>
|
||||
<span className="font-medium text-muted">
|
||||
<Icon
|
||||
icon="tabler:folder"
|
||||
className="text-primary text-base inline-block me-2"
|
||||
/>
|
||||
signin
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li className="py-2">
|
||||
<div className="flex items-center gap-8">
|
||||
<p className="text-xl text-black">
|
||||
|
|
||||
</p>
|
||||
<div className="flex items-center gap-3">
|
||||
<p className="text-xl text-black">
|
||||
|—
|
||||
</p>
|
||||
<span className="font-medium text-muted">
|
||||
<Icon
|
||||
icon="tabler:folder"
|
||||
className="text-primary text-base inline-block me-2"
|
||||
/>
|
||||
signup
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li className="py-2">
|
||||
<div className="flex items-center gap-3">
|
||||
<p className="text-xl text-black">
|
||||
|—
|
||||
</p>
|
||||
<span className="font-medium text-muted">
|
||||
<Icon
|
||||
icon="tabler:folder"
|
||||
className="text-primary text-base inline-block me-2"
|
||||
/>
|
||||
documentation
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li className="py-2">
|
||||
<div className="flex items-center gap-3">
|
||||
<p className="text-xl text-black">|—</p>
|
||||
<span className="font-medium text-muted">
|
||||
<Icon
|
||||
icon="tabler:folder"
|
||||
className="text-primary text-base inline-block me-2"
|
||||
/>
|
||||
api
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<div className="flex flex-col justify-between gap-2 mt-2">
|
||||
{Counts.slice(0, 2).map((item) => {
|
||||
return (
|
||||
<p className="text-xl text-black">
|
||||
|
|
||||
</p>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<ul className="ps-5 md:ps-12 list-unstyled">
|
||||
<li className="py-2">
|
||||
<div className="flex items-center gap-8">
|
||||
<p className="text-xl text-black">
|
||||
|
|
||||
</p>
|
||||
<div className="flex items-center gap-3">
|
||||
<p className="text-xl text-black">
|
||||
|—
|
||||
</p>
|
||||
<span className="font-medium text-muted">
|
||||
<Icon
|
||||
icon="tabler:folder"
|
||||
className="text-primary text-base inline-block me-2"
|
||||
/>
|
||||
auth
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li className="py-2">
|
||||
<div className="flex items-center gap-8">
|
||||
<p className="text-xl text-black">
|
||||
|
|
||||
</p>
|
||||
<div className="flex items-center gap-3">
|
||||
<p className="text-xl text-black">
|
||||
|—
|
||||
</p>
|
||||
<span className="font-medium text-muted">
|
||||
<Icon
|
||||
icon="tabler:folder"
|
||||
className="text-primary text-base inline-block me-2"
|
||||
/>
|
||||
contex
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li className="py-2">
|
||||
<div className="flex items-center gap-3">
|
||||
<p className="text-xl text-black">|—</p>
|
||||
<span className="font-medium text-muted">
|
||||
<Icon
|
||||
icon="tabler:folder"
|
||||
className="text-primary text-base inline-block me-2"
|
||||
/>
|
||||
Context
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<div className="flex flex-col justify-between gap-2 mt-2">
|
||||
{Counts.slice(0, 1).map((item) => {
|
||||
return (
|
||||
<p className="text-xl text-black">
|
||||
|
|
||||
</p>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<ul className="ps-5 md:ps-12 list-unstyled">
|
||||
<li className="py-2">
|
||||
<div className="flex items-center gap-8">
|
||||
<p className="text-xl text-black">
|
||||
|
|
||||
</p>
|
||||
<div className="flex items-center gap-3">
|
||||
<p className="text-xl text-black">
|
||||
|—
|
||||
</p>
|
||||
<span className="font-medium text-muted flex items-center flex-wrap">
|
||||
<Icon
|
||||
icon="tabler:folder"
|
||||
className="text-primary text-base inline-block me-2"
|
||||
/>
|
||||
<p className="max-w-12 lg:max-w-full truncate">
|
||||
authDialogContext.tsx
|
||||
</p>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li className="py-2">
|
||||
<div className="flex items-center gap-3">
|
||||
<p className="text-xl text-black">|—</p>
|
||||
<span className="font-medium text-muted">
|
||||
global.css
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
<li className="py-2">
|
||||
<div className="flex items-center gap-3">
|
||||
<p className="text-xl text-black">|—</p>
|
||||
<span className="font-medium text-muted">
|
||||
layout.tsx
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
<li className="py-2">
|
||||
<div className="flex items-center gap-3">
|
||||
<p className="text-xl text-black">|—</p>
|
||||
<span className="font-medium text-muted">
|
||||
not-found.tsx
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
<li className="py-2">
|
||||
<div className="flex items-center gap-3">
|
||||
<p className="text-xl text-black">|—</p>
|
||||
<span className="font-medium text-muted">
|
||||
page.tsx
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li className="py-2">
|
||||
<div className="flex items-center flex-wrap gap-3">
|
||||
<p className="text-xl text-black">|—</p>
|
||||
<span className="font-medium text-muted">
|
||||
<Icon
|
||||
icon="tabler:folder"
|
||||
className="text-primary text-base inline-block me-2"
|
||||
/>
|
||||
components
|
||||
</span>{" "}
|
||||
<span className="fs-9 text-muted ms-4">
|
||||
(All the Components of this template.)
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
<li className="py-2">
|
||||
<div className="flex items-center gap-3">
|
||||
<p className="text-xl text-black">|—</p>
|
||||
<span className="font-medium text-muted">
|
||||
<Icon
|
||||
icon="tabler:folder"
|
||||
className="text-primary text-base inline-block me-2"
|
||||
/>
|
||||
styles
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
<li className="py-2">
|
||||
<div className="flex items-center gap-3">
|
||||
<p className="text-xl text-black">|—</p>
|
||||
<span className="font-medium text-muted">
|
||||
<Icon
|
||||
icon="tabler:folder"
|
||||
className="text-primary text-base inline-block me-2"
|
||||
/>
|
||||
types
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
<li className="py-2">
|
||||
<div className="flex items-center gap-3">
|
||||
<p className="text-xl text-black">|—</p>
|
||||
<span className="font-medium text-muted">
|
||||
<Icon
|
||||
icon="tabler:folder"
|
||||
className="text-primary text-base inline-block me-2"
|
||||
/>
|
||||
utils
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li className="py-2">
|
||||
<div className="flex items-center gap-3">
|
||||
<p className="text-xl text-black">|—</p>
|
||||
<span className="font-medium text-muted">
|
||||
<i className="ti ti-file me-2 text-primary font-bold" />
|
||||
next.config.mjs
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
<li className="py-2">
|
||||
<div className="flex items-center gap-3">
|
||||
<p className="text-xl text-black">|—</p>
|
||||
<span className="font-medium text-muted">
|
||||
<i className="ti ti-file me-2 text-primary font-bold" />
|
||||
postcss.config.mjs
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
<li className="py-2">
|
||||
<div className="flex items-center gap-3">
|
||||
<p className="text-xl text-black">|—</p>
|
||||
<span className="font-medium text-muted">
|
||||
<i className="ti ti-file me-2 text-primary font-bold" />
|
||||
package.json
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
<li className="py-2">
|
||||
<div className="flex items-center gap-3">
|
||||
<p className="text-xl text-black">|—</p>
|
||||
<span className="font-medium text-muted">
|
||||
<i className="ti ti-file me-2 text-primary font-bold" />
|
||||
tsconfig.json
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
87
src/app/components/Documentation/QuickStart.tsx
Normal file
87
src/app/components/Documentation/QuickStart.tsx
Normal file
@@ -0,0 +1,87 @@
|
||||
export const QuickStart = () => {
|
||||
return (
|
||||
<div className="pb-10 md:scroll-m-[180px] scroll-m-28" id="start">
|
||||
<h3 className=" text-black text-2xl font-semibold mt-8">Quick Start</h3>
|
||||
<div className="p-6 rounded-md border mt-6 border-dark_border border-opacity-60">
|
||||
<h6 className="text-black text-lg font-medium">1. Requirements</h6>
|
||||
<p className="text-base font-medium text-muted text-opacity-60">
|
||||
Before proceeding, you need to have the latest stable{" "}
|
||||
<a href="https://nodejs.org/" className="text-primary">
|
||||
node.js
|
||||
</a>{" "}
|
||||
</p>
|
||||
<h6 className="mt-4 mb-2 text-black font-medium text-base">
|
||||
Recommended environment:
|
||||
</h6>
|
||||
<ul className="list-disc text-muted text-opacity-60 ps-6">
|
||||
<li>node js 20+</li>
|
||||
<li>npm js 10+</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div className="p-6 rounded-md border mt-6 border-dark_border border-opacity-60">
|
||||
<h6 className="text-black text-lg font-medium">2. Install</h6>
|
||||
<p className="text-base font-medium text-muted text-opacity-60">
|
||||
Open package folder and install its dependencies. We recommanded yarn
|
||||
or npm.{" "}
|
||||
</p>
|
||||
<h6 className="mt-4 mb-2 text-black text-dark font-medium text-base">
|
||||
1) Install with npm:
|
||||
</h6>
|
||||
<div className="py-4 px-3 rounded-md bg-black">
|
||||
<p className="text-sm text-white/60">
|
||||
<span className="text-yellow-500">cd</span> project-folder
|
||||
</p>
|
||||
<p className="text-sm text-white/60 mt-2">npm install</p>
|
||||
</div>
|
||||
<h6 className="mt-4 mb-2 text-black text-dark font-medium text-base">
|
||||
1) Install with yarn:
|
||||
</h6>
|
||||
<div className="py-4 px-3 rounded-md bg-black">
|
||||
<p className="text-sm text-white/60">
|
||||
<span className="text-yellow-500">cd</span> project-folder
|
||||
</p>
|
||||
<p className="text-sm text-white/60 mt-2">yarn install</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-6 rounded-md border mt-6 border-dark_border border-opacity-60">
|
||||
<h6 className="text-black text-lg font-medium">3. Start</h6>
|
||||
<p className="text-base font-medium text-muted text-opacity-60 mb-4">
|
||||
Once npm install is done now you an run the app.
|
||||
</p>
|
||||
|
||||
<div className="py-4 px-3 rounded-md bg-black">
|
||||
<p className="text-sm text-white/60">npm run dev or yarn run dev</p>
|
||||
</div>
|
||||
<p className="text-base font-medium text-muted text-opacity-60 my-4">
|
||||
This command will start a local webserver{" "}
|
||||
<span className="dark:text-black">http://localhost:3000:</span>
|
||||
</p>
|
||||
<div className="py-4 px-3 rounded-md bg-black">
|
||||
<p className="text-sm text-white/60">
|
||||
{"> sieducational_project@2.0.0 dev"}
|
||||
</p>
|
||||
<p className="text-sm text-white/60 mt-1">{"> next dev"}</p>
|
||||
<p className="text-sm text-white/60 mt-6">{"-Next.js 14.2.4"}</p>
|
||||
<p className="text-sm text-white/60 mt-1">
|
||||
{"-Local: http://localhost:3000"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-6 rounded-md border mt-6 border-dark_border border-opacity-60">
|
||||
<h6 className="text-black text-lg font-medium">
|
||||
4. Build / Deployment
|
||||
</h6>
|
||||
<p className="text-base font-medium text-muted text-opacity-60 mb-4">
|
||||
After adding url run below command for build a app.
|
||||
</p>
|
||||
|
||||
<div className="py-4 px-3 rounded-md bg-black">
|
||||
<p className="text-sm text-white/60">npm run build or yarn build</p>
|
||||
</div>
|
||||
<p className="text-base font-medium text-muted text-opacity-60 mt-6">
|
||||
Finally, Your webiste is ready to be deployed.🥳
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
21
src/app/components/Documentation/TypographyConfiguration.tsx
Normal file
21
src/app/components/Documentation/TypographyConfiguration.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
export const TypographyConfiguration = () => {
|
||||
return (
|
||||
<>
|
||||
<h3 className="text-xl font-semibold mt-8 text-black">Typography</h3>
|
||||
<div className="p-6 rounded-md border mt-4 border-dark_border border-opacity-60">
|
||||
<p className="text-base font-medium text-muted text-opacity-60">
|
||||
1. Change Font family over here :{" "}
|
||||
<span className="font-semibold text-base">src/app/layout.tsx</span>{" "}
|
||||
</p>
|
||||
<div className="py-4 px-3 rounded-md bg-black mt-8">
|
||||
<p className="text-sm text-white/60 flex flex-col gap-2 mb-3">
|
||||
{`import { Inter } from "next/font/google";`}
|
||||
</p>
|
||||
<p className="text-sm text-white/60 flex flex-col gap-2">
|
||||
{`const font = Inter({ subsets: ["latin"] });`}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
92
src/app/components/Home/Companies/index.tsx
Normal file
92
src/app/components/Home/Companies/index.tsx
Normal file
@@ -0,0 +1,92 @@
|
||||
'use client'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import Slider from 'react-slick'
|
||||
import 'slick-carousel/slick/slick.css'
|
||||
import 'slick-carousel/slick/slick-theme.css'
|
||||
import Image from 'next/image'
|
||||
|
||||
const Companies = () => {
|
||||
const settings = {
|
||||
dots: false,
|
||||
infinite: true,
|
||||
slidesToShow: 4,
|
||||
slidesToScroll: 1,
|
||||
arrows: false,
|
||||
autoplay: true,
|
||||
speed: 2000,
|
||||
autoplaySpeed: 2000,
|
||||
cssEase: 'linear',
|
||||
responsive: [
|
||||
{
|
||||
breakpoint: 1024,
|
||||
settings: {
|
||||
slidesToShow: 4,
|
||||
slidesToScroll: 1,
|
||||
infinite: true,
|
||||
dots: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
breakpoint: 700,
|
||||
settings: {
|
||||
slidesToShow: 2,
|
||||
slidesToScroll: 1,
|
||||
infinite: true,
|
||||
dots: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
breakpoint: 500,
|
||||
settings: {
|
||||
slidesToShow: 1,
|
||||
slidesToScroll: 1,
|
||||
infinite: true,
|
||||
dots: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const [companies, setCompianes] = useState<{ imgSrc: string }[]>([])
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const res = await fetch('/api/data')
|
||||
if (!res.ok) throw new Error('Failed to fetch')
|
||||
const data = await res.json()
|
||||
setCompianes(data.Companiesdata)
|
||||
} catch (error) {
|
||||
console.error('Error fetching services:', error)
|
||||
}
|
||||
}
|
||||
fetchData()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<section>
|
||||
<div className='container mx-auto max-w-7xl px-4'>
|
||||
<h2 className='text-lg mb-10 text-black/40 text-center'>
|
||||
Trusted by companies of all sizes
|
||||
</h2>
|
||||
<div>
|
||||
<Slider {...settings}>
|
||||
{companies.map((item, i) => (
|
||||
<div key={i}>
|
||||
<Image
|
||||
src={item.imgSrc}
|
||||
alt={item.imgSrc}
|
||||
width={100}
|
||||
height={50}
|
||||
className='w-auto'
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</Slider>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default Companies
|
||||
252
src/app/components/Home/Courses/index.tsx
Normal file
252
src/app/components/Home/Courses/index.tsx
Normal file
@@ -0,0 +1,252 @@
|
||||
'use client'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import Image from 'next/image'
|
||||
import { CourseDetailType } from '@/app/types/coursedetail'
|
||||
import CourseDetailSkeleton from '../../Skeleton/CourseDetail'
|
||||
import Link from 'next/link'
|
||||
|
||||
interface Name {
|
||||
imageSrc: string
|
||||
course: string
|
||||
price: string
|
||||
profession: string
|
||||
category:
|
||||
| 'webdevelopment'
|
||||
| 'mobiledevelopment'
|
||||
| 'datascience'
|
||||
| 'cloudcomputing'
|
||||
}
|
||||
|
||||
const NamesList = () => {
|
||||
// -------------------------------------------------------------
|
||||
const [courseDetail, setCourseDetail] = useState<CourseDetailType[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const res = await fetch('/api/data')
|
||||
if (!res.ok) throw new Error('Failed to fetch.')
|
||||
const data = await res.json()
|
||||
setCourseDetail(data.CourseDetailData)
|
||||
} catch (error) {
|
||||
console.error('Error fetching services:', error)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
fetchData()
|
||||
}, [])
|
||||
// -------------------------------------------------------------
|
||||
|
||||
const [selectedButton, setSelectedButton] = useState<
|
||||
| 'mobiledevelopment'
|
||||
| 'webdevelopment'
|
||||
| 'datascience'
|
||||
| 'cloudcomputing'
|
||||
| 'all'
|
||||
| null
|
||||
>('webdevelopment')
|
||||
const mobileDevelopment = courseDetail.filter(
|
||||
(name) => name.category === 'mobiledevelopment'
|
||||
)
|
||||
const webDevelopment = courseDetail.filter(
|
||||
(name) => name.category === 'webdevelopment'
|
||||
)
|
||||
const dataScience = courseDetail.filter(
|
||||
(name) => name.category === 'datascience'
|
||||
)
|
||||
const cloudComputing = courseDetail.filter(
|
||||
(name) => name.category === 'cloudcomputing'
|
||||
)
|
||||
|
||||
let selectedNames: Name[] = []
|
||||
if (selectedButton === 'mobiledevelopment') {
|
||||
selectedNames = mobileDevelopment
|
||||
} else if (selectedButton === 'webdevelopment') {
|
||||
selectedNames = webDevelopment
|
||||
} else if (selectedButton === 'datascience') {
|
||||
selectedNames = dataScience
|
||||
} else if (selectedButton === 'cloudcomputing') {
|
||||
selectedNames = cloudComputing
|
||||
}
|
||||
|
||||
const nameElements = selectedNames.map((name, index) => (
|
||||
<div id='Courses' key={index} className='shadow-lg rounded-xl group flex'>
|
||||
<div className='py-5 lg:py-0 flex flex-col'>
|
||||
<div className='overflow-hidden rounded-lg bg-gray-100'>
|
||||
<Image
|
||||
src={name.imageSrc}
|
||||
alt={name.course}
|
||||
width={700}
|
||||
height={700}
|
||||
className='h-full w-full object-cover object-center group-hover:scale-125 transition duration-300 ease-in-out'
|
||||
/>
|
||||
</div>
|
||||
<div className='p-4 flex flex-col justify-between gap-5 flex-1'>
|
||||
<div className="flex flex-col gap-5">
|
||||
<div className='flex items-center justify-between'>
|
||||
<p className='block font-normal text-gray-900'>{name.course}</p>
|
||||
<div className='block text-lg font-semibold text-success border-solid border-2 border-success rounded-md px-1'>
|
||||
<p>${name.price}</p>
|
||||
</div>
|
||||
</div>
|
||||
<Link href={'/'}>
|
||||
<p
|
||||
aria-hidden='true'
|
||||
className='text-xl font-semibold group-hover:text-primary group-hover:cursor-pointer'>
|
||||
{name.profession}
|
||||
</p>
|
||||
</Link>
|
||||
</div>
|
||||
<div className='flex justify-between border-solid border-2 rounded-md p-2'>
|
||||
<p>12 Classes</p>
|
||||
<div className='flex flex-row space-x-4'>
|
||||
<div className='flex'>
|
||||
<Image
|
||||
src={'/images/courses/account.svg'}
|
||||
width={18}
|
||||
height={20}
|
||||
alt='circle'
|
||||
/>
|
||||
<p className='text-lightgrey ml-1'>120</p>
|
||||
</div>
|
||||
<div className='flex'>
|
||||
<Image
|
||||
src={'/images/courses/Star.svg'}
|
||||
width={18}
|
||||
height={20}
|
||||
alt='star'
|
||||
/>
|
||||
<p className='ml-1'>4.5</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
|
||||
return (
|
||||
<section id='courses-section'>
|
||||
<div className='container mx-auto max-w-7xl px-4'>
|
||||
<div className='flex flex-col sm:flex-row justify-between sm:items-center gap-5 mb-4'>
|
||||
<h2 className='font-bold tracking-tight'>Popular Courses</h2>
|
||||
<div>
|
||||
<button className='bg-transparent cursor-pointer hover:bg-primary text-primary font-medium hover:text-white py-3 px-4 border border-primary hover:border-transparent rounded-sm duration-300'>
|
||||
Explore Classes
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex nowhitespace space-x-5 rounded-xl bg-white p-1 overflow-x-auto mb-4'>
|
||||
{/* FOR DESKTOP VIEW */}
|
||||
<button
|
||||
onClick={() => setSelectedButton('webdevelopment')}
|
||||
className={
|
||||
'bg-white' +
|
||||
(selectedButton === 'webdevelopment'
|
||||
? 'text-black border-b-2 border-yellow-200'
|
||||
: 'text-black/40') +
|
||||
' pb-2 text-lg hidden sm:block hover:cursor-pointer'
|
||||
}>
|
||||
Web Development
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setSelectedButton('mobiledevelopment')}
|
||||
className={
|
||||
'bg-white ' +
|
||||
(selectedButton === 'mobiledevelopment'
|
||||
? 'text-black border-b-2 border-yellow-200'
|
||||
: 'text-black/40') +
|
||||
' pb-2 text-lg hidden sm:block hover:cursor-pointer'
|
||||
}>
|
||||
Mobile Development
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setSelectedButton('datascience')}
|
||||
className={
|
||||
'bg-white ' +
|
||||
(selectedButton === 'datascience'
|
||||
? 'text-black border-b-2 border-yellow-200'
|
||||
: 'text-black/40') +
|
||||
' pb-2 text-lg hidden sm:block hover:cursor-pointer'
|
||||
}>
|
||||
Data Science
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setSelectedButton('cloudcomputing')}
|
||||
className={
|
||||
'bg-white ' +
|
||||
(selectedButton === 'cloudcomputing'
|
||||
? 'text-black border-b-2 border-yellow-200'
|
||||
: 'text-black/40') +
|
||||
' pb-2 text-lg hidden sm:block hover:cursor-pointer'
|
||||
}>
|
||||
Cloud Computing
|
||||
</button>
|
||||
|
||||
{/* FOR MOBILE VIEW */}
|
||||
<Icon
|
||||
icon='solar:global-line-duotone'
|
||||
onClick={() => setSelectedButton('webdevelopment')}
|
||||
className={
|
||||
'text-5xl sm:hidden block ' +
|
||||
(selectedButton === 'webdevelopment'
|
||||
? 'border-b-2 border-yellow-200'
|
||||
: 'text-gray-400')
|
||||
}
|
||||
/>
|
||||
|
||||
<Icon
|
||||
icon='solar:smartphone-line-duotone'
|
||||
onClick={() => setSelectedButton('mobiledevelopment')}
|
||||
className={
|
||||
'text-5xl sm:hidden block ' +
|
||||
(selectedButton === 'mobiledevelopment'
|
||||
? 'border-b-2 border-yellow-200'
|
||||
: 'text-gray-400')
|
||||
}
|
||||
/>
|
||||
|
||||
<Icon
|
||||
icon='solar:database-line-duotone'
|
||||
onClick={() => setSelectedButton('datascience')}
|
||||
className={
|
||||
'text-5xl sm:hidden block ' +
|
||||
(selectedButton === 'datascience'
|
||||
? 'border-b-2 border-yellow-200'
|
||||
: 'text-gray-400')
|
||||
}
|
||||
/>
|
||||
|
||||
<Icon
|
||||
icon='solar:cloud-line-duotone'
|
||||
onClick={() => setSelectedButton('cloudcomputing')}
|
||||
className={
|
||||
'text-5xl sm:hidden block ' +
|
||||
(selectedButton === 'cloudcomputing'
|
||||
? 'border-b-2 border-yellow-200'
|
||||
: 'text-gray-400')
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div className='grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-8'>
|
||||
{loading ? (
|
||||
Array.from({ length: 4 }).map((_, i) => (
|
||||
<CourseDetailSkeleton key={i} />
|
||||
))
|
||||
) : nameElements.length > 0 ? (
|
||||
nameElements
|
||||
) : (
|
||||
<p>No data to show</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default NamesList
|
||||
91
src/app/components/Home/Hero/Dropdownone.tsx
Normal file
91
src/app/components/Home/Hero/Dropdownone.tsx
Normal file
@@ -0,0 +1,91 @@
|
||||
'use client'
|
||||
import { Fragment, useEffect, useState } from 'react'
|
||||
import {
|
||||
Listbox,
|
||||
ListboxButton,
|
||||
ListboxOptions,
|
||||
ListboxOption,
|
||||
Transition,
|
||||
} from '@headlessui/react'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import { CourseType } from '@/app/types/course'
|
||||
|
||||
const Dropdown = () => {
|
||||
const [course, setCourse] = useState<CourseType[]>([])
|
||||
const [selected, setSelected] = useState<CourseType | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const res = await fetch('/api/data')
|
||||
if (!res.ok) throw new Error('Failed to fetch')
|
||||
const data = await res.json()
|
||||
setCourse(data.CourseData)
|
||||
setSelected(data.CourseData[0])
|
||||
} catch (error) {
|
||||
console.error('Error fetching services:', error)
|
||||
}
|
||||
}
|
||||
fetchData()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className='w-full'>
|
||||
<p className='text-lg text-gray-500'>What do you want to learn?</p>
|
||||
<Listbox value={selected} onChange={setSelected}>
|
||||
<div className='relative mt-1'>
|
||||
<ListboxButton className='relative w-full cursor-default rounded-lg bg-white text-xl py-2 pr-10 text-left focus:outline-hidden focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-orange-300 sm:text-sm hover:cursor-pointer'>
|
||||
<span className='block truncate text-xl font-semibold '>
|
||||
{selected?.name}
|
||||
</span>
|
||||
<span className='pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2'>
|
||||
<Icon
|
||||
icon='tabler:chevron-down'
|
||||
className='text-gray-400 text-xl inline-block me-2'
|
||||
/>
|
||||
</span>
|
||||
</ListboxButton>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
leave='transition ease-in duration-100'
|
||||
leaveFrom='opacity-100'
|
||||
leaveTo='opacity-0'>
|
||||
<ListboxOptions className='absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-hidden sm:text-sm'>
|
||||
{course.map((person, personIdx) => (
|
||||
<ListboxOption
|
||||
key={personIdx}
|
||||
className={({ active }) =>
|
||||
`relative cursor-default select-none py-2 pl-10 pr-4 ${
|
||||
active ? 'bg-amber-100 text-amber-900' : 'text-gray-900'
|
||||
}`
|
||||
}
|
||||
value={person}>
|
||||
{({ selected }) => (
|
||||
<>
|
||||
<span
|
||||
className={`block truncate ${
|
||||
selected ? 'font-medium' : 'font-normal'
|
||||
}`}>
|
||||
{person.name}
|
||||
</span>
|
||||
{selected ? (
|
||||
<span className='absolute inset-y-0 left-0 flex items-center pl-3 text-amber-600'>
|
||||
<Icon
|
||||
icon='tabler:check'
|
||||
className='text-xl inline-block me-2'
|
||||
/>
|
||||
</span>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</ListboxOption>
|
||||
))}
|
||||
</ListboxOptions>
|
||||
</Transition>
|
||||
</div>
|
||||
</Listbox>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Dropdown
|
||||
91
src/app/components/Home/Hero/Dropdowntwo.tsx
Normal file
91
src/app/components/Home/Hero/Dropdowntwo.tsx
Normal file
@@ -0,0 +1,91 @@
|
||||
'use client'
|
||||
import { Fragment, useEffect, useState } from 'react'
|
||||
import {
|
||||
Listbox,
|
||||
ListboxButton,
|
||||
ListboxOptions,
|
||||
ListboxOption,
|
||||
Transition,
|
||||
} from '@headlessui/react'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import { Hourtype } from '@/app/types/hour'
|
||||
|
||||
const Dropdown = () => {
|
||||
const [hour, setHour] = useState<Hourtype[]>([])
|
||||
const [selected, setSelected] = useState<Hourtype | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const res = await fetch('/api/data')
|
||||
if (!res.ok) throw new Error('Failed to fetch')
|
||||
const data = await res.json()
|
||||
setHour(data.HourData)
|
||||
setSelected(data.HourData[0])
|
||||
} catch (error) {
|
||||
console.error('Error fetching services:', error)
|
||||
}
|
||||
}
|
||||
fetchData()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className='w-full'>
|
||||
<p className='text-lg text-gray-500'>Hours you going to invest?</p>
|
||||
<Listbox value={selected} onChange={setSelected}>
|
||||
<div className='relative mt-1'>
|
||||
<ListboxButton className='relative w-full cursor-default rounded-lg bg-white text-xl py-2 pr-10 text-left focus:outline-hidden focus-visible:border-indigo-500 focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75 focus-visible:ring-offset-2 focus-visible:ring-offset-orange-300 sm:text-sm hover:cursor-pointer'>
|
||||
<span className='block truncate text-xl font-semibold '>
|
||||
{selected?.name}
|
||||
</span>
|
||||
<span className='pointer-events-none absolute inset-y-0 right-0 flex items-center pr-2'>
|
||||
<Icon
|
||||
icon='tabler:chevron-down'
|
||||
className='text-gray-400 text-xl inline-block me-2'
|
||||
/>
|
||||
</span>
|
||||
</ListboxButton>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
leave='transition ease-in duration-100'
|
||||
leaveFrom='opacity-100'
|
||||
leaveTo='opacity-0'>
|
||||
<ListboxOptions className='absolute mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-hidden sm:text-sm'>
|
||||
{hour.map((person, personIdx) => (
|
||||
<ListboxOption
|
||||
key={personIdx}
|
||||
className={({ active }) =>
|
||||
`relative cursor-default select-none py-2 pl-10 pr-4 ${
|
||||
active ? 'bg-amber-100 text-amber-900' : 'text-gray-900'
|
||||
}`
|
||||
}
|
||||
value={person}>
|
||||
{({ selected }) => (
|
||||
<>
|
||||
<span
|
||||
className={`block truncate ${
|
||||
selected ? 'font-medium' : 'font-normal'
|
||||
}`}>
|
||||
{person.name}
|
||||
</span>
|
||||
{selected ? (
|
||||
<span className='absolute inset-y-0 left-0 flex items-center pl-3 text-amber-600'>
|
||||
<Icon
|
||||
icon='tabler:check'
|
||||
className='text-xl inline-block me-2'
|
||||
/>
|
||||
</span>
|
||||
) : null}
|
||||
</>
|
||||
)}
|
||||
</ListboxOption>
|
||||
))}
|
||||
</ListboxOptions>
|
||||
</Transition>
|
||||
</div>
|
||||
</Listbox>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Dropdown
|
||||
103
src/app/components/Home/Hero/index.tsx
Normal file
103
src/app/components/Home/Hero/index.tsx
Normal file
@@ -0,0 +1,103 @@
|
||||
import Link from 'next/link'
|
||||
import Dropdownone from './Dropdownone'
|
||||
import Dropdowntwo from './Dropdowntwo'
|
||||
import Image from 'next/image'
|
||||
|
||||
const Banner = () => {
|
||||
return (
|
||||
<section id='Home' className='bg-banner-image pt-28 pb-20'>
|
||||
<div className='relative px-6 lg:px-8'>
|
||||
<div className='container'>
|
||||
<div className='flex flex-col gap-4 text-center'>
|
||||
<h1 className='leading-tight font-bold tracking-tight max-w-4xl mx-auto'>
|
||||
Advance your engineering skills with our courses
|
||||
</h1>
|
||||
<p className='text-lg leading-8 text-black'>
|
||||
Build skills with our courses and mentor from world-class
|
||||
companies.
|
||||
</p>
|
||||
<div className='backdrop-blur-md bg-white/30 border border-white/30 rounded-lg shadow-lg p-6 w-fit mx-auto'>
|
||||
<div className='flex items-center justify-center gap-8'>
|
||||
<div className='hidden sm:block -space-x-2 overflow-hidden'>
|
||||
<Image
|
||||
className='inline-block h-12 w-12 rounded-full ring-2 ring-white'
|
||||
src='https://images.unsplash.com/photo-1491528323818-fdd1faba62cc?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80'
|
||||
alt='img1'
|
||||
width={12}
|
||||
height={12}
|
||||
/>
|
||||
|
||||
<Image
|
||||
className='inline-block h-12 w-12 rounded-full ring-2 ring-white'
|
||||
src='https://images.unsplash.com/photo-1550525811-e5869dd03032?ixlib=rb-1.2.1&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80'
|
||||
alt='img2'
|
||||
width={12}
|
||||
height={12}
|
||||
/>
|
||||
<Image
|
||||
className='inline-block h-12 w-12 rounded-full ring-2 ring-white'
|
||||
src='https://images.unsplash.com/photo-1500648767791-00dcc994a43e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2.25&w=256&h=256&q=80'
|
||||
alt='img3'
|
||||
width={12}
|
||||
height={12}
|
||||
/>
|
||||
<Image
|
||||
className='inline-block h-12 w-12 rounded-full ring-2 ring-white'
|
||||
src='https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80'
|
||||
alt='img4'
|
||||
width={12}
|
||||
height={12}
|
||||
/>
|
||||
<Image
|
||||
className='inline-block h-12 w-12 rounded-full ring-2 ring-white'
|
||||
src='https://images.unsplash.com/photo-1550525811-e5869dd03032?ixlib=rb-1.2.1&auto=format&fit=facearea&facepad=2&w=256&h=256&q=80'
|
||||
alt='img5'
|
||||
width={12}
|
||||
height={12}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div className='flex justify-center sm:justify-start'>
|
||||
<h3 className='text-2xl font-semibold mr-2'>4.6</h3>
|
||||
<Image
|
||||
src={'/images/banner/Stars.svg'}
|
||||
alt='stars-icon'
|
||||
width={32}
|
||||
height={32}
|
||||
className='w-[60%]'
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className='text-sm'>Rated by 25k on google.</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* DROPDOWN BUTTONS */}
|
||||
|
||||
<div className='mx-auto max-w-4xl mt-12 p-6 lg:max-w-4xl lg:px-8 bg-white rounded-lg boxshadow'>
|
||||
<div className='grid grid-cols-1 gap-y-10 gap-x-6 sm:grid-cols-2 lg:grid-cols-8 xl:gap-x-8'>
|
||||
<div className='col-span-3'>
|
||||
<Dropdownone />
|
||||
</div>
|
||||
<div className='col-span-3'>
|
||||
<Dropdowntwo />
|
||||
</div>
|
||||
<div className='col-span-3 sm:col-span-2 mt-2'>
|
||||
<Link href={'/#courses-section'}>
|
||||
<button className='bg-primary w-full hover:bg-transparent hover:text-primary duration-300 border border-primary text-white font-bold py-4 px-3 rounded-sm hover:cursor-pointer'>
|
||||
Start
|
||||
</button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default Banner
|
||||
79
src/app/components/Home/Mentor/index.tsx
Normal file
79
src/app/components/Home/Mentor/index.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
'use client'
|
||||
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { MentorType } from '@/app/types/mentor'
|
||||
import MentorSkeleton from '../../Skeleton/Mentor'
|
||||
|
||||
const Mentor = () => {
|
||||
const [mentor, setMentor] = useState<MentorType[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const res = await fetch('/api/data')
|
||||
if (!res.ok) throw new Error('Failed to fetch')
|
||||
const data = await res.json()
|
||||
setMentor(data.MentorData)
|
||||
} catch (error) {
|
||||
console.error('Error fetching services:', error)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
fetchData()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<section id='mentors-section'>
|
||||
<div className='container'>
|
||||
<div className='flex flex-col sm:flex-row gap-5 justify-between sm:items-center mb-12'>
|
||||
<h2 className='font-bold tracking-tight'>Meet with our Mentors</h2>
|
||||
<div>
|
||||
<button className='bg-transparent cursor-pointer hover:bg-primary text-primary font-medium hover:text-white py-3 px-4 border border-primary hover:border-transparent rounded-sm duration-300'>
|
||||
Explore 10+ our Mentor
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6 xl:gap-8'>
|
||||
{loading
|
||||
? Array.from({ length: 6 }).map((_, i) => (
|
||||
<MentorSkeleton key={i} />
|
||||
))
|
||||
: mentor.map((item, index) => (
|
||||
<div key={index} className='group relative shadow-lg'>
|
||||
<div className='min-h-80 w-full overflow-hidden rounded-lg bg-gray-200 lg:h-80'>
|
||||
<Image
|
||||
src={item.imageSrc}
|
||||
alt={item.imageAlt}
|
||||
width={700}
|
||||
height={700}
|
||||
className='h-full w-full object-cover object-center lg:h-full lg:w-full '
|
||||
/>
|
||||
</div>
|
||||
<div className='my-4 flex justify-center '>
|
||||
<div>
|
||||
<div className='border border-white rounded-lg -mt-8 bg-white p-2 shadow-mentorShadow flex items-center justify-center'>
|
||||
<Link
|
||||
href='/'
|
||||
className='text-sm text-gray-700 text-center'>
|
||||
{item.name}
|
||||
</Link>
|
||||
</div>
|
||||
<p className='mt-3 text-2xl font-semibold text-black/80 text-center'>
|
||||
{item.color}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default Mentor
|
||||
45
src/app/components/Home/Newsletter/index.tsx
Normal file
45
src/app/components/Home/Newsletter/index.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import Image from 'next/image'
|
||||
|
||||
const Newsletter = () => {
|
||||
return (
|
||||
<section id='join-section' className='-mb-64'>
|
||||
<div className='relative z-10'>
|
||||
<div className='mx-auto max-w-2xl py-16 md:py-24 px-4 sm:px-6 md:max-w-7xl lg:px-24 bg-orange rounded-lg bg-newsletter bg-contain bg-no-repeat bg-right-bottom'>
|
||||
<div className='grid grid-cols-1 gap-y-10 gap-x-6 sm:grid-cols-2 xl:gap-x-8'>
|
||||
<div>
|
||||
<h3 className='text-5xl font-bold mb-3'> Join Our Newsletter </h3>
|
||||
<h4 className='text-lg font-medium mb-7'>
|
||||
Subscribe our newsletter for discounts, promo and many more.
|
||||
</h4>
|
||||
<div className='flex gap-2'>
|
||||
<input
|
||||
type='Email address'
|
||||
name='q'
|
||||
className='py-4 w-full text-base px-4 bg-white transition-all duration-500 focus:border-primary focus:outline-1 rounded-lg pl-4'
|
||||
placeholder='Enter your email'
|
||||
autoComplete='off'
|
||||
/>
|
||||
<button className='bg-primary cursor-pointer hover:bg-transparent border border-primary hover:text-primary text-white font-medium py-2 px-4 rounded-sm'>
|
||||
Subscribe
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className='hidden sm:block'>
|
||||
<div className='float-right -mt-32'>
|
||||
<Image
|
||||
src={'/images/newsletter/Free.svg'}
|
||||
alt='bgimg'
|
||||
width={64}
|
||||
height={64}
|
||||
className='w-auto'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default Newsletter
|
||||
110
src/app/components/Home/Testimonial/index.tsx
Normal file
110
src/app/components/Home/Testimonial/index.tsx
Normal file
@@ -0,0 +1,110 @@
|
||||
'use client'
|
||||
import { useEffect, useState } from 'react'
|
||||
import Image from 'next/image'
|
||||
import Slider from 'react-slick'
|
||||
import 'slick-carousel/slick/slick.css'
|
||||
import 'slick-carousel/slick/slick-theme.css'
|
||||
import { TestimonialType } from '@/app/types/testimonial'
|
||||
import TestimonialSkeleton from '../../Skeleton/Testimonial'
|
||||
|
||||
// CAROUSEL SETTINGS
|
||||
|
||||
const Testimonial = () => {
|
||||
const [testimonial, setTestimonial] = useState<TestimonialType[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const res = await fetch('/api/data')
|
||||
if (!res.ok) throw new Error('Failed to fetch')
|
||||
const data = await res.json()
|
||||
setTestimonial(data.TestimonialData)
|
||||
} catch (error) {
|
||||
console.error('Error fetching services:', error)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
fetchData()
|
||||
}, [])
|
||||
|
||||
const settings = {
|
||||
dots: true,
|
||||
infinite: true,
|
||||
slidesToShow: 3,
|
||||
slidesToScroll: 1,
|
||||
arrows: false,
|
||||
autoplay: false,
|
||||
cssEase: 'linear',
|
||||
responsive: [
|
||||
{
|
||||
breakpoint: 1200,
|
||||
settings: {
|
||||
slidesToShow: 2,
|
||||
},
|
||||
},
|
||||
{
|
||||
breakpoint: 800,
|
||||
settings: {
|
||||
slidesToShow: 1,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
return (
|
||||
<section id='testimonial-section' className='bg-cream'>
|
||||
<div className='container'>
|
||||
<div className='flex flex-col sm:flex-row gap-5 justify-between sm:items-center mb-6'>
|
||||
<h2 className='font-bold tracking-tight'>
|
||||
What Our Happy <br /> Students Says
|
||||
</h2>
|
||||
<div>
|
||||
<button className='bg-transparent cursor-pointer hover:bg-primary text-primary font-semibold hover:text-white py-3 px-4 border border-primary hover:border-transparent rounded-sm duration-300'>
|
||||
Give Your Review
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p className='text-lg font-medium mb-6'>
|
||||
Build skills with our courses and mentor <br /> from world-class
|
||||
companies.
|
||||
</p>
|
||||
<Slider {...settings}>
|
||||
{loading
|
||||
? Array.from({ length: 3 }).map((_, i) => (
|
||||
<TestimonialSkeleton key={i} />
|
||||
))
|
||||
: testimonial.map((items, i) => (
|
||||
<div key={i}>
|
||||
<div className='bg-white m-4 pt-8 px-12 pb-10 text-center rounded-lg'>
|
||||
<div className="relative z-0 flex justify-center items-center before:absolute before:bg-[url('/images/testimonial/greenpic.svg')] before:h-6 before:w-6 before:bottom-0 before:z-10 before:left-54%">
|
||||
<Image
|
||||
src={items.imgSrc}
|
||||
alt='gaby'
|
||||
width={64}
|
||||
height={64}
|
||||
className='inline-block rounded-full ring-2 ring-white relative'
|
||||
/>
|
||||
</div>
|
||||
<p className='text-sm pt-4 pb-2'>{items.profession}</p>
|
||||
<p className='text-2xl font-semibold pb-3'>{items.name}</p>
|
||||
<Image
|
||||
src={items.starimg}
|
||||
alt='stars-img'
|
||||
className='m-auto pb-6 w-[30%]'
|
||||
width={32}
|
||||
height={32}
|
||||
/>
|
||||
<p className='text-lg font-medium leading-7'>
|
||||
{items.detail}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</Slider>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default Testimonial
|
||||
139
src/app/components/Layout/Footer/index.tsx
Normal file
139
src/app/components/Layout/Footer/index.tsx
Normal file
@@ -0,0 +1,139 @@
|
||||
'use client'
|
||||
|
||||
import Link from 'next/link'
|
||||
import Image from 'next/image'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { FooterLinkType } from '@/app/types/footerlinks'
|
||||
|
||||
const Footer = () => {
|
||||
const [footerlink, SetFooterlink] = useState<FooterLinkType[]>([])
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const res = await fetch('/api/data')
|
||||
if (!res.ok) throw new Error('Failed to fetch')
|
||||
const data = await res.json()
|
||||
SetFooterlink(data.FooterLinkData)
|
||||
} catch (error) {
|
||||
console.error('Error fetching services:', error)
|
||||
}
|
||||
}
|
||||
fetchData()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className='bg-primary' id='first-section'>
|
||||
<div className='container pt-60 pb-10'>
|
||||
<div className='grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-12 gap-16 xl:gap-8'>
|
||||
<div className='col-span-4 flex flex-col gap-5'>
|
||||
<div>
|
||||
<Image
|
||||
src='/images/logo/logo2.svg'
|
||||
alt='Logo'
|
||||
width={48}
|
||||
height={64}
|
||||
/>
|
||||
</div>
|
||||
<p className='text-white text-lg font-medium leading-7'>
|
||||
{' '}
|
||||
Level up your skills, and get dream <br /> job with passion.{' '}
|
||||
</p>
|
||||
<div className='flex gap-4'>
|
||||
<Link
|
||||
href='/'
|
||||
className='bg-white/20 rounded-full p-2 text-white hover:bg-cream hover:text-primary duration-300'>
|
||||
<Icon
|
||||
icon='tabler:brand-instagram'
|
||||
className='text-2xl inline-block'
|
||||
/>
|
||||
</Link>
|
||||
<Link
|
||||
href='/'
|
||||
className='bg-white/20 rounded-full p-2 text-white hover:bg-cream hover:text-primary duration-300'>
|
||||
<Icon
|
||||
icon='tabler:brand-dribbble'
|
||||
className='text-2xl inline-block'
|
||||
/>
|
||||
</Link>
|
||||
<Link
|
||||
href='/'
|
||||
className='bg-white/20 rounded-full p-2 text-white hover:bg-cream hover:text-primary duration-300'>
|
||||
<Icon
|
||||
icon='tabler:brand-twitter-filled'
|
||||
className='text-2xl inline-block'
|
||||
/>
|
||||
</Link>
|
||||
<Link
|
||||
href='/'
|
||||
className='bg-white/20 rounded-full p-2 text-white hover:bg-cream hover:text-primary duration-300'>
|
||||
<Icon
|
||||
icon='tabler:brand-youtube-filled'
|
||||
className='text-2xl inline-block'
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* CLOUMN-2/3 */}
|
||||
<div className='col-span-4'>
|
||||
<div className='flex gap-20'>
|
||||
{footerlink.map((product, i) => (
|
||||
<div key={i} className='group relative col-span-2'>
|
||||
<p className='text-white text-xl font-semibold mb-9'>
|
||||
{product.section}
|
||||
</p>
|
||||
<ul>
|
||||
{product.links.map((item, i) => (
|
||||
<li key={i} className='mb-3'>
|
||||
<Link
|
||||
href={item.href}
|
||||
className='text-white/60 hover:text-white text-sm font-normal mb-6'>
|
||||
{item.label}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
{/* CLOUMN-4 */}
|
||||
|
||||
<div className='col-span-4'>
|
||||
<h3 className='text-white text-xl font-semibold mb-6'>
|
||||
Stay up to date
|
||||
</h3>
|
||||
<div className='relative text-white focus-within:text-white flex flex-row-reverse w-[50%] lg:w-full'>
|
||||
<input
|
||||
type='Email address'
|
||||
name='q'
|
||||
className='py-4 text-sm w-full text-white bg-white/15 rounded-md pl-4 focus:outline-hidden bg-emailbg focus:text-white'
|
||||
placeholder='Your email address'
|
||||
autoComplete='off'
|
||||
/>
|
||||
<div className='absolute inset-y-0 right-0 flex items-center pr-2'>
|
||||
<button
|
||||
type='submit'
|
||||
className='p-1 focus:outline-hidden focus:shadow-outline'>
|
||||
<Icon
|
||||
icon='tabler:send'
|
||||
className='text-white text-2xl inline-block me-2'
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='py-3'>
|
||||
<h3 className='text-center text-white/60'>
|
||||
@2025 - All Rights Reserved
|
||||
</h3>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Footer
|
||||
19
src/app/components/Layout/Header/Logo/index.tsx
Normal file
19
src/app/components/Layout/Header/Logo/index.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
|
||||
const Logo: React.FC = () => {
|
||||
return (
|
||||
<Link href="/">
|
||||
<Image
|
||||
src="/images/logo/logo.svg"
|
||||
alt="logo"
|
||||
width={160}
|
||||
height={50}
|
||||
style={{ width: "auto", height: "auto" }}
|
||||
quality={100}
|
||||
/>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
export default Logo;
|
||||
70
src/app/components/Layout/Header/Navigation/HeaderLink.tsx
Normal file
70
src/app/components/Layout/Header/Navigation/HeaderLink.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
'use client'
|
||||
import { useState } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { HeaderItem } from '../../../../types/menu'
|
||||
import { usePathname } from 'next/navigation'
|
||||
|
||||
const HeaderLink: React.FC<{ item: HeaderItem }> = ({ item }) => {
|
||||
const [submenuOpen, setSubmenuOpen] = useState(false)
|
||||
const path = usePathname()
|
||||
const handleMouseEnter = () => {
|
||||
if (item.submenu) {
|
||||
setSubmenuOpen(true)
|
||||
}
|
||||
}
|
||||
const handleMouseLeave = () => {
|
||||
setSubmenuOpen(false)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className='relative'
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}>
|
||||
<Link
|
||||
href={item.href}
|
||||
className={`text-base flex font-medium hover:text-primary capitalized ${
|
||||
path === item.href ? 'text-primary ' : 'text-black'
|
||||
}`}>
|
||||
{item.label}
|
||||
{item.submenu && (
|
||||
<svg
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
width='1.5em'
|
||||
height='1.5em'
|
||||
viewBox='0 0 24 24'>
|
||||
<path
|
||||
fill='none'
|
||||
stroke='currentColor'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
strokeWidth='1.5'
|
||||
d='m7 10l5 5l5-5'
|
||||
/>
|
||||
</svg>
|
||||
)}
|
||||
</Link>
|
||||
{submenuOpen && (
|
||||
<div
|
||||
className={`absolute py-2 left-0 mt-0.5 w-60 bg-white dark:bg-darklight dark:text-white shadow-lg rounded-lg `}
|
||||
data-aos='fade-up'
|
||||
data-aos-duration='500'>
|
||||
{item.submenu?.map((subItem, index) => (
|
||||
<Link
|
||||
key={index}
|
||||
href={subItem.href}
|
||||
className={`block px-4 py-2 ${
|
||||
path === subItem.href
|
||||
? 'bg-primary text-white'
|
||||
: 'text-black dark:text-white hover:bg-primary'
|
||||
}`}>
|
||||
{subItem.label}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default HeaderLink
|
||||
@@ -0,0 +1,52 @@
|
||||
import { useState } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { HeaderItem } from '../../../../types/menu'
|
||||
|
||||
const MobileHeaderLink: React.FC<{ item: HeaderItem }> = ({ item }) => {
|
||||
const [submenuOpen, setSubmenuOpen] = useState(false)
|
||||
|
||||
const handleToggle = () => {
|
||||
setSubmenuOpen(!submenuOpen)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='relative w-full'>
|
||||
<Link
|
||||
href={item.href}
|
||||
onClick={item.submenu ? handleToggle : undefined}
|
||||
className='flex items-center justify-between w-full py-2 text-black focus:outline-hidden'>
|
||||
{item.label}
|
||||
{item.submenu && (
|
||||
<svg
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
width='1.5em'
|
||||
height='1.5em'
|
||||
viewBox='0 0 24 24'>
|
||||
<path
|
||||
fill='none'
|
||||
stroke='currentColor'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
strokeWidth='1.5'
|
||||
d='m7 10l5 5l5-5'
|
||||
/>
|
||||
</svg>
|
||||
)}
|
||||
</Link>
|
||||
{submenuOpen && item.submenu && (
|
||||
<div className='bg-white p-2 w-full'>
|
||||
{item.submenu.map((subItem, index) => (
|
||||
<Link
|
||||
key={index}
|
||||
href={subItem.href}
|
||||
className='block py-2 text-gray-500 hover:bg-gray-200'>
|
||||
{subItem.label}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default MobileHeaderLink
|
||||
33
src/app/components/Layout/Header/ThemeToggler.tsx
Normal file
33
src/app/components/Layout/Header/ThemeToggler.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
'use client'
|
||||
import { useTheme } from "next-themes";
|
||||
|
||||
const ThemeToggler = () => {
|
||||
const { theme, setTheme } = useTheme();
|
||||
return (
|
||||
<button
|
||||
aria-label="theme toggler"
|
||||
onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
|
||||
className="text-body-color flex h-8 w-8 items-center justify-center duration-300 dark:text-white"
|
||||
>
|
||||
<span>
|
||||
<svg
|
||||
viewBox="0 0 16 16"
|
||||
className="hidden h-[22px] w-[22px] fill-current dark:block"
|
||||
>
|
||||
<path d="M4.50663 3.2267L3.30663 2.03337L2.36663 2.97337L3.55996 4.1667L4.50663 3.2267ZM2.66663 7.00003H0.666626V8.33337H2.66663V7.00003ZM8.66663 0.366699H7.33329V2.33337H8.66663V0.366699V0.366699ZM13.6333 2.97337L12.6933 2.03337L11.5 3.2267L12.44 4.1667L13.6333 2.97337ZM11.4933 12.1067L12.6866 13.3067L13.6266 12.3667L12.4266 11.1734L11.4933 12.1067ZM13.3333 7.00003V8.33337H15.3333V7.00003H13.3333ZM7.99996 3.6667C5.79329 3.6667 3.99996 5.46003 3.99996 7.6667C3.99996 9.87337 5.79329 11.6667 7.99996 11.6667C10.2066 11.6667 12 9.87337 12 7.6667C12 5.46003 10.2066 3.6667 7.99996 3.6667ZM7.33329 14.9667H8.66663V13H7.33329V14.9667ZM2.36663 12.36L3.30663 13.3L4.49996 12.1L3.55996 11.16L2.36663 12.36Z" />
|
||||
</svg>
|
||||
|
||||
<svg
|
||||
viewBox="0 0 23 23"
|
||||
className="h-[30px] w-[30px] fill-current dark:hidden"
|
||||
>
|
||||
<g clipPath="url(#clip0_40_125)">
|
||||
<path d="M16.6111 15.855C17.591 15.1394 18.3151 14.1979 18.7723 13.1623C16.4824 13.4065 14.1342 12.4631 12.6795 10.4711C11.2248 8.47905 11.0409 5.95516 11.9705 3.84818C10.8449 3.9685 9.72768 4.37162 8.74781 5.08719C5.7759 7.25747 5.12529 11.4308 7.29558 14.4028C9.46586 17.3747 13.6392 18.0253 16.6111 15.855Z" />
|
||||
</g>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
|
||||
export default ThemeToggler;
|
||||
213
src/app/components/Layout/Header/index.tsx
Normal file
213
src/app/components/Layout/Header/index.tsx
Normal file
@@ -0,0 +1,213 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import Logo from './Logo'
|
||||
import HeaderLink from '../Header/Navigation/HeaderLink'
|
||||
import MobileHeaderLink from '../Header/Navigation/MobileHeaderLink'
|
||||
import Signin from '@/app/components/Auth/SignIn'
|
||||
import SignUp from '@/app/components/Auth/SignUp'
|
||||
import { Icon } from '@iconify/react/dist/iconify.js'
|
||||
import { HeaderItem } from '@/app/types/menu'
|
||||
|
||||
const Header: React.FC = () => {
|
||||
const [headerData, setHeaderData] = useState<HeaderItem[]>([])
|
||||
|
||||
const [navbarOpen, setNavbarOpen] = useState(false)
|
||||
const [sticky, setSticky] = useState(false)
|
||||
const [isSignInOpen, setIsSignInOpen] = useState(false)
|
||||
const [isSignUpOpen, setIsSignUpOpen] = useState(false)
|
||||
|
||||
const navbarRef = useRef<HTMLDivElement>(null)
|
||||
const signInRef = useRef<HTMLDivElement>(null)
|
||||
const signUpRef = useRef<HTMLDivElement>(null)
|
||||
const mobileMenuRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const res = await fetch('/api/data')
|
||||
if (!res.ok) throw new Error('Failed to fetch')
|
||||
const data = await res.json()
|
||||
setHeaderData(data.HeaderData)
|
||||
} catch (error) {
|
||||
console.error('Error fetching services:', error)
|
||||
}
|
||||
}
|
||||
fetchData()
|
||||
}, [])
|
||||
|
||||
const handleScroll = () => {
|
||||
setSticky(window.scrollY >= 10)
|
||||
}
|
||||
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (
|
||||
signInRef.current &&
|
||||
!signInRef.current.contains(event.target as Node)
|
||||
) {
|
||||
setIsSignInOpen(false)
|
||||
}
|
||||
if (
|
||||
signUpRef.current &&
|
||||
!signUpRef.current.contains(event.target as Node)
|
||||
) {
|
||||
setIsSignUpOpen(false)
|
||||
}
|
||||
if (
|
||||
mobileMenuRef.current &&
|
||||
!mobileMenuRef.current.contains(event.target as Node) &&
|
||||
navbarOpen
|
||||
) {
|
||||
setNavbarOpen(false)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('scroll', handleScroll)
|
||||
document.addEventListener('mousedown', handleClickOutside)
|
||||
return () => {
|
||||
window.removeEventListener('scroll', handleScroll)
|
||||
document.removeEventListener('mousedown', handleClickOutside)
|
||||
}
|
||||
}, [navbarOpen, isSignInOpen, isSignUpOpen])
|
||||
|
||||
useEffect(() => {
|
||||
if (isSignInOpen || isSignUpOpen || navbarOpen) {
|
||||
document.body.style.overflow = 'hidden'
|
||||
} else {
|
||||
document.body.style.overflow = ''
|
||||
}
|
||||
}, [isSignInOpen, isSignUpOpen, navbarOpen])
|
||||
|
||||
return (
|
||||
<header
|
||||
className={`fixed top-0 z-40 w-full transition-all duration-300 ${
|
||||
sticky ? ' shadow-lg bg-white py-4' : 'shadow-none py-4'
|
||||
}`}>
|
||||
<div>
|
||||
<div className='container mx-auto max-w-7xl px-4 flex items-center justify-between'>
|
||||
<Logo />
|
||||
<nav className='hidden lg:flex grow items-center gap-8 justify-start ml-14'>
|
||||
{headerData.map((item, index) => (
|
||||
<HeaderLink key={index} item={item} />
|
||||
))}
|
||||
</nav>
|
||||
<div className='flex items-center gap-4'>
|
||||
<button
|
||||
className='hidden lg:block bg-transparent text-primary border hover:bg-primary border-primary hover:text-white duration-300 px-6 py-2 rounded-lg hover:cursor-pointer'
|
||||
onClick={() => {
|
||||
setIsSignInOpen(true)
|
||||
}}>
|
||||
Sign In
|
||||
</button>
|
||||
{isSignInOpen && (
|
||||
<div className='fixed top-0 left-0 w-full h-full bg-black/50 flex items-center justify-center z-50'>
|
||||
<div
|
||||
ref={signInRef}
|
||||
className='relative mx-auto w-full max-w-md overflow-hidden rounded-lg px-8 pt-14 pb-8 text-center bg-dark_grey/90 backdrop-blur-md bg-white'>
|
||||
<button
|
||||
onClick={() => setIsSignInOpen(false)}
|
||||
className='absolute top-0 right-0 mr-8 mt-8 dark:invert'
|
||||
aria-label='Close Sign In Modal'>
|
||||
<Icon
|
||||
icon='material-symbols:close-rounded'
|
||||
width={24}
|
||||
height={24}
|
||||
className='text-black hover:text-primary inline-block hover:cursor-pointer'
|
||||
/>
|
||||
</button>
|
||||
<Signin />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<button
|
||||
className='hidden lg:block bg-primary text-white text-base font-medium hover:bg-transparent duration-300 hover:text-primary border border-primary px-6 py-2 rounded-lg hover:cursor-pointer'
|
||||
onClick={() => {
|
||||
setIsSignUpOpen(true)
|
||||
}}>
|
||||
Sign Up
|
||||
</button>
|
||||
{isSignUpOpen && (
|
||||
<div className='fixed top-0 left-0 w-full h-full bg-black/50 flex items-center justify-center z-50'>
|
||||
<div
|
||||
ref={signUpRef}
|
||||
className='relative mx-auto bg-white w-full max-w-md overflow-hidden rounded-lg bg-dark_grey/90 backdrop-blur-md px-8 pt-14 pb-8 text-center'>
|
||||
<button
|
||||
onClick={() => setIsSignUpOpen(false)}
|
||||
className='absolute top-0 right-0 mr-8 mt-8 dark:invert'
|
||||
aria-label='Close Sign Up Modal'>
|
||||
<Icon
|
||||
icon='material-symbols:close-rounded'
|
||||
width={24}
|
||||
height={24}
|
||||
className='text-black hover:text-primary inline-block hover:cursor-pointer'
|
||||
/>
|
||||
</button>
|
||||
<SignUp />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<button
|
||||
onClick={() => setNavbarOpen(!navbarOpen)}
|
||||
className='block lg:hidden p-2 rounded-lg'
|
||||
aria-label='Toggle mobile menu'>
|
||||
<span className='block w-6 h-0.5 bg-black'></span>
|
||||
<span className='block w-6 h-0.5 bg-black mt-1.5'></span>
|
||||
<span className='block w-6 h-0.5 bg-black mt-1.5'></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{navbarOpen && (
|
||||
<div className='fixed top-0 left-0 w-full h-full bg-black/50 z-40' />
|
||||
)}
|
||||
<div
|
||||
ref={mobileMenuRef}
|
||||
className={`lg:hidden fixed top-0 right-0 h-full w-full bg-white shadow-lg transform transition-transform duration-300 max-w-xs ${
|
||||
navbarOpen ? 'translate-x-0' : 'translate-x-full'
|
||||
} z-50`}>
|
||||
<div className='flex items-center justify-between p-4'>
|
||||
<h2 className='text-lg font-bold text-midnight_text'>
|
||||
<Logo />
|
||||
</h2>
|
||||
{/* */}
|
||||
<button
|
||||
onClick={() => setNavbarOpen(false)}
|
||||
className='bg-black/30 rounded-full p-1 text-white'
|
||||
aria-label='Close menu Modal'>
|
||||
<Icon
|
||||
icon={'material-symbols:close-rounded'}
|
||||
width={24}
|
||||
height={24}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<nav className='flex flex-col items-start p-4'>
|
||||
{headerData.map((item, index) => (
|
||||
<MobileHeaderLink key={index} item={item} />
|
||||
))}
|
||||
<div className='mt-4 flex flex-col gap-4 w-full'>
|
||||
<button
|
||||
className='bg-primary text-white px-4 py-2 rounded-lg border border-primary hover:text-primary hover:bg-transparent hover:cursor-pointer transition duration-300 ease-in-out'
|
||||
onClick={() => {
|
||||
setIsSignInOpen(true)
|
||||
setNavbarOpen(false)
|
||||
}}>
|
||||
Sign In
|
||||
</button>
|
||||
<button
|
||||
className='bg-primary text-white px-4 py-2 rounded-lg border border-primary hover:text-primary hover:bg-transparent hover:cursor-pointer transition duration-300 ease-in-out'
|
||||
onClick={() => {
|
||||
setIsSignUpOpen(true)
|
||||
setNavbarOpen(false)
|
||||
}}>
|
||||
Sign Up
|
||||
</button>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
||||
export default Header
|
||||
77
src/app/components/NotFound/index.tsx
Normal file
77
src/app/components/NotFound/index.tsx
Normal file
File diff suppressed because one or more lines are too long
40
src/app/components/ScrollToTop/index.tsx
Normal file
40
src/app/components/ScrollToTop/index.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
'use client'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
export default function ScrollToTop() {
|
||||
const [isVisible, setIsVisible] = useState(false)
|
||||
|
||||
const scrollToTop = () => {
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: 'smooth',
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const toggleVisibility = () => {
|
||||
if (window.pageYOffset > 300) {
|
||||
setIsVisible(true)
|
||||
} else {
|
||||
setIsVisible(false)
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', toggleVisibility)
|
||||
|
||||
return () => window.removeEventListener('scroll', toggleVisibility)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
{isVisible && (
|
||||
<div
|
||||
onClick={scrollToTop}
|
||||
aria-label='scroll to top'
|
||||
className='fixed bottom-8 right-8 z-999 back-to-top flex h-10 w-10 cursor-pointer items-center justify-center rounded-md bg-[#102C46] text-white shadow-md transition duration-300 ease-in-out hover:bg-dark'>
|
||||
<span className='mt-[6px] h-3 w-3 rotate-45 border-l border-t border-white'></span>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
20
src/app/components/SharedComponent/HeroSub/index.tsx
Normal file
20
src/app/components/SharedComponent/HeroSub/index.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import React, { FC } from "react";
|
||||
|
||||
interface HeroSubProps {
|
||||
title: string;
|
||||
}
|
||||
|
||||
const HeroSub: FC<HeroSubProps> = ({ title }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<section className="py-40 bg-herosub-bg bg-no-repeat bg-cover lg:mt-40 sm:mt-44 mt-20">
|
||||
<div className="container mx-auto lg:max-w-(--breakpoint-xl) px-4">
|
||||
<h2 className="text-white md:text-56 text-36 font-medium">{title}</h2>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default HeroSub;
|
||||
25
src/app/components/SharedComponent/Volunteer/index.tsx
Normal file
25
src/app/components/SharedComponent/Volunteer/index.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import Link from "next/link";
|
||||
|
||||
const Volunteer = () => {
|
||||
return (
|
||||
<section className="py-28 bg-volunteer-bg bg-no-repeat bg-cover">
|
||||
<div className="container mx-auto lg:max-w-(--breakpoint-xl) px-4">
|
||||
<div className="text-center">
|
||||
<h2 className="text-30 font-medium text-white mb-6">
|
||||
Become a Volunteer
|
||||
</h2>
|
||||
<p className="text-16 text-white lg:max-w-60% mx-auto mb-6">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscelit. Nam malesu dolor sit amet, consectetur adipiscelit. consectetur adipiscelit. Nam malesu dolor.
|
||||
</p>
|
||||
<div className="flex justify-center ">
|
||||
<Link href="#" className="text-white bg-linear-to-r from-error to-warning px-7 py-5 hover:from-transparent hover:to-transparent border border-transparent hover:border-error hover:text-error rounded-sm">
|
||||
Donate now
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default Volunteer;
|
||||
29
src/app/components/Skeleton/CourseDetail/index.tsx
Normal file
29
src/app/components/Skeleton/CourseDetail/index.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
const CourseDetailSkeleton = () => {
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<div
|
||||
role='status'
|
||||
className='max-w-md animate-pulse overflow-hidden rounded-lg bg-gray-100 p-8 mb-28'>
|
||||
<svg
|
||||
className='w-full h-30 text-gray-200 my-10'
|
||||
aria-hidden='true'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
fill='currentColor'
|
||||
viewBox='0 0 20 18'>
|
||||
<path d='M18 0H2a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2Zm-5.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3Zm4.376 10.481A1 1 0 0 1 16 15H4a1 1 0 0 1-.895-1.447l3.5-7A1 1 0 0 1 7.468 6a.965.965 0 0 1 .9.5l2.775 4.757 1.546-1.887a1 1 0 0 1 1.618.1l2.541 4a1 1 0 0 1 .028 1.011Z' />
|
||||
</svg>
|
||||
<div className='h-2 bg-gray-200 rounded-full dark:bg-gray-700 mb-2.5'></div>
|
||||
<div className='h-2 bg-gray-200 rounded-full dark:bg-gray-700 max-w-[330px] mb-2.5'></div>
|
||||
|
||||
<div className='h-2 bg-gray-200 rounded-full dark:bg-gray-700 mb-2.5'></div>
|
||||
<div className='h-2 bg-gray-200 rounded-full dark:bg-gray-700 max-w-[330px] mb-2.5'></div>
|
||||
<div className='h-2 bg-gray-200 rounded-full dark:bg-gray-700 max-w-[300px] mb-2.5'></div>
|
||||
<span className='sr-only'>Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default CourseDetailSkeleton
|
||||
25
src/app/components/Skeleton/Mentor/index.tsx
Normal file
25
src/app/components/Skeleton/Mentor/index.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
const MentorSkeleton = () => {
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<div
|
||||
role='status'
|
||||
className='max-w-md animate-pulse overflow-hidden rounded-lg bg-gray-100 p-8'>
|
||||
<svg
|
||||
className='w-full h-30 text-gray-200 my-10'
|
||||
aria-hidden='true'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
fill='currentColor'
|
||||
viewBox='0 0 20 18'>
|
||||
<path d='M18 0H2a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2Zm-5.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3Zm4.376 10.481A1 1 0 0 1 16 15H4a1 1 0 0 1-.895-1.447l3.5-7A1 1 0 0 1 7.468 6a.965.965 0 0 1 .9.5l2.775 4.757 1.546-1.887a1 1 0 0 1 1.618.1l2.541 4a1 1 0 0 1 .028 1.011Z' />
|
||||
</svg>
|
||||
<div className='h-2 bg-gray-200 rounded-full dark:bg-gray-700 mb-2.5'></div>
|
||||
<div className='h-2 bg-gray-200 rounded-full dark:bg-gray-700 mb-2.5'></div>
|
||||
<span className='sr-only'>Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default MentorSkeleton
|
||||
33
src/app/components/Skeleton/Testimonial/index.tsx
Normal file
33
src/app/components/Skeleton/Testimonial/index.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
const TestimonialSkeleton = () => {
|
||||
return (
|
||||
<>
|
||||
<div className="m-4">
|
||||
<div
|
||||
role='status'
|
||||
className='max-w-md animate-pulse overflow-hidden rounded-lg bg-white p-8'>
|
||||
<svg
|
||||
className='w-20 h-20 mx-auto text-gray-200 my-5'
|
||||
aria-hidden='true'
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
fill='currentColor'
|
||||
viewBox='0 0 20 18'>
|
||||
<path d='M18 0H2a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2Zm-5.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3Zm4.376 10.481A1 1 0 0 1 16 15H4a1 1 0 0 1-.895-1.447l3.5-7A1 1 0 0 1 7.468 6a.965.965 0 0 1 .9.5l2.775 4.757 1.546-1.887a1 1 0 0 1 1.618.1l2.541 4a1 1 0 0 1 .028 1.011Z' />
|
||||
</svg>
|
||||
<div className='h-2 w-28 mx-auto bg-gray-200 rounded-full dark:bg-gray-700'></div>
|
||||
<div className='h-2 w-48 mx-auto bg-gray-200 rounded-full dark:bg-gray-700 max-w-[330px] my-5'></div>
|
||||
|
||||
<div className='h-2 w-24 mx-auto bg-gray-200 rounded-full dark:bg-gray-700 mb-10'></div>
|
||||
<div className='h-2 bg-gray-200 rounded-full dark:bg-gray-700 max-w-[330px] mb-2.5'></div>
|
||||
<div className='h-2 bg-gray-200 rounded-full dark:bg-gray-700 max-w-[330px] mb-2.5'></div>
|
||||
<div className='h-2 bg-gray-200 rounded-full dark:bg-gray-700 max-w-[330px] mb-2.5'></div>
|
||||
<div className='h-2 bg-gray-200 rounded-full dark:bg-gray-700 max-w-[330px] mb-2.5'></div>
|
||||
<div className='h-2 bg-gray-200 rounded-full dark:bg-gray-700 max-w-[330px] mb-2.5'></div>
|
||||
<div className='h-2 bg-gray-200 rounded-full dark:bg-gray-700 max-w-[330px] mb-2.5'></div>
|
||||
<span className='sr-only'>Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default TestimonialSkeleton
|
||||
BIN
src/app/favicon.ico
Normal file
BIN
src/app/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 177 KiB |
57
src/app/globals.css
Normal file
57
src/app/globals.css
Normal file
@@ -0,0 +1,57 @@
|
||||
@import 'tailwindcss';
|
||||
|
||||
@custom-variant dark (&:is(.dark *));
|
||||
|
||||
@theme {
|
||||
--shadow-mentor-shadow: 0px 4px 20px rgba(110, 127, 185, 0.1);
|
||||
|
||||
--inset-54\%: 54%;
|
||||
|
||||
--color-primary: #611f69;
|
||||
--color-cream: #fcf5ef;
|
||||
--color-success: #6b9f36;
|
||||
--color-orange: #f9cd92;
|
||||
|
||||
--background-image-banner-image: url('/images/banner/background.png');
|
||||
--background-image-newsletter: url('/images/newsletter/hands.svg');
|
||||
}
|
||||
|
||||
/*
|
||||
The default border color has changed to `currentColor` in Tailwind CSS v4,
|
||||
so we've added these compatibility styles to make sure everything still
|
||||
looks the same as it did with Tailwind CSS v3.
|
||||
|
||||
If we ever want to remove these styles, we need to add an explicit border
|
||||
color utility to any element that depends on these defaults.
|
||||
*/
|
||||
@layer base {
|
||||
*,
|
||||
::after,
|
||||
::before,
|
||||
::backdrop,
|
||||
::file-selector-button {
|
||||
border-color: var(--color-gray-200, currentColor);
|
||||
}
|
||||
|
||||
section {
|
||||
@apply py-14
|
||||
}
|
||||
|
||||
h1 {
|
||||
@apply text-black md:text-7xl sm:text-6xl text-5xl
|
||||
}
|
||||
|
||||
h2 {
|
||||
@apply text-black sm:text-5xl text-4xl
|
||||
}
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
.container {
|
||||
@apply max-w-7xl mx-auto w-full px-4;
|
||||
}
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
26
src/app/layout.tsx
Normal file
26
src/app/layout.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { Inter } from "next/font/google";
|
||||
import "./globals.css";
|
||||
import Header from "@/app/components/Layout/Header";
|
||||
import Footer from "@/app/components/Layout/Footer";
|
||||
import ScrollToTop from "@/app/components/ScrollToTop";
|
||||
import Aoscompo from "@/utils/aos";
|
||||
const font = Inter({ subsets: ["latin"] });
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="en" suppressHydrationWarning>
|
||||
<body className={`${font.className}`}>
|
||||
<Aoscompo>
|
||||
<Header />
|
||||
{children}
|
||||
<Footer />
|
||||
</Aoscompo>
|
||||
<ScrollToTop />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
20
src/app/not-found.tsx
Normal file
20
src/app/not-found.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
import HeroSub from "@/app/components/SharedComponent/HeroSub";
|
||||
import NotFound from "@/app/components/NotFound";
|
||||
import { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "404 Page | Venus ",
|
||||
};
|
||||
|
||||
const ErrorPage = () => {
|
||||
return (
|
||||
<>
|
||||
<HeroSub
|
||||
title="404"
|
||||
/>
|
||||
<NotFound />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ErrorPage;
|
||||
26
src/app/page.tsx
Normal file
26
src/app/page.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import React from 'react'
|
||||
import Hero from '@/app/components/Home/Hero'
|
||||
import Companies from '@/app/components/Home/Companies'
|
||||
import NamesList from '@/app/components/Home/Courses'
|
||||
import Mentor from '@/app/components/Home/Mentor'
|
||||
import Testimonial from '@/app/components/Home/Testimonial'
|
||||
import Newsletter from '@/app/components/Home/Newsletter'
|
||||
import { Metadata } from 'next'
|
||||
import ContactForm from './components/Contact/Form'
|
||||
export const metadata: Metadata = {
|
||||
title: 'Si Educational',
|
||||
}
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<main>
|
||||
<Hero />
|
||||
<Companies />
|
||||
<NamesList />
|
||||
<Mentor />
|
||||
<Testimonial />
|
||||
<ContactForm />
|
||||
<Newsletter />
|
||||
</main>
|
||||
)
|
||||
}
|
||||
8
src/app/types/blog.ts
Normal file
8
src/app/types/blog.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
export type Blog = {
|
||||
id?: number;
|
||||
title?: string;
|
||||
slug?: string;
|
||||
excerpt?: string;
|
||||
coverImage: string;
|
||||
date: string;
|
||||
};
|
||||
9
src/app/types/breadcrumb.ts
Normal file
9
src/app/types/breadcrumb.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export interface BreadcrumbProps {
|
||||
pageName: string;
|
||||
pageDescription?: string;
|
||||
}
|
||||
|
||||
export interface BreadcrumbLink {
|
||||
href: string;
|
||||
text: string;
|
||||
}
|
||||
3
src/app/types/course.ts
Normal file
3
src/app/types/course.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export type CourseType = {
|
||||
name: string
|
||||
}
|
||||
11
src/app/types/coursedetail.ts
Normal file
11
src/app/types/coursedetail.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
export type CourseDetailType = {
|
||||
course: string
|
||||
imageSrc: string
|
||||
profession: string
|
||||
price: string
|
||||
category:
|
||||
| 'mobiledevelopment'
|
||||
| 'webdevelopment'
|
||||
| 'datascience'
|
||||
| 'cloudcomputing'
|
||||
}
|
||||
9
src/app/types/footerlinks.ts
Normal file
9
src/app/types/footerlinks.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
export type Link = {
|
||||
label: string
|
||||
href: string
|
||||
}
|
||||
|
||||
export type FooterLinkType = {
|
||||
section: string
|
||||
links: Link[]
|
||||
}
|
||||
3
src/app/types/hour.ts
Normal file
3
src/app/types/hour.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export type Hourtype = {
|
||||
name: string
|
||||
}
|
||||
7
src/app/types/mentor.ts
Normal file
7
src/app/types/mentor.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export type MentorType = {
|
||||
name: string
|
||||
href: string
|
||||
imageSrc: string
|
||||
imageAlt: string
|
||||
color: string
|
||||
}
|
||||
10
src/app/types/menu.ts
Normal file
10
src/app/types/menu.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
export type SubmenuItem = {
|
||||
label: string
|
||||
href: string
|
||||
}
|
||||
|
||||
export type HeaderItem = {
|
||||
label: string
|
||||
href: string
|
||||
submenu?: SubmenuItem[]
|
||||
}
|
||||
7
src/app/types/testimonial.ts
Normal file
7
src/app/types/testimonial.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export type TestimonialType = {
|
||||
profession: string
|
||||
name: string
|
||||
imgSrc: string
|
||||
starimg: string
|
||||
detail: string
|
||||
}
|
||||
20
src/utils/aos.tsx
Normal file
20
src/utils/aos.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
'use client'
|
||||
import { useEffect } from "react";
|
||||
import AOS from "aos"
|
||||
import 'aos/dist/aos.css';
|
||||
|
||||
const Aoscompo = ({children}:any) => {
|
||||
useEffect(() => {
|
||||
AOS.init({
|
||||
duration: 800,
|
||||
once: false,
|
||||
})
|
||||
}, [])
|
||||
return (
|
||||
<div>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Aoscompo
|
||||
7
src/utils/validateEmail.ts
Normal file
7
src/utils/validateEmail.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export const validateEmail = (email: string) => {
|
||||
return String(email)
|
||||
.toLowerCase()
|
||||
.match(
|
||||
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|.(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user