This commit is contained in:
Tben
2023-07-20 21:21:46 +01:00
parent 55424a7d78
commit 164ad29628
40 changed files with 1500 additions and 386 deletions
+28 -97
View File
@@ -1,120 +1,51 @@
"use client";
import React from "react";
// import TextShuffler from "../../components/actions/TextShuffler";
import { about, genericScroll } from "../(utils)/animate";
export default function AboutSection() {
React.useEffect(about, []);
React.useEffect(genericScroll, []);
const webDevStack = require("../(utils)/web-dev-stack.json");
const uiStack = require("../(utils)/ui-ux-stack.json");
const [targetStack, setTargetStack] = React.useState("dev");
return (
<div
className="max-w-6xl w-full flex flex-col items-center pb-40 generic-scroll"
// id="about-section"
className="max-w-6xl w-full flex flex-col items-center pb-40 generic-scroll page-section"
id="about-section"
>
{/* <span
className="text-[300px] uppercase absolute whitespace-nowrap opacity-5 -mt-64 z-[-1]"
id="about-me-label"
>
About Me
</span> */}
<div className="h-44"></div>
<div className="flex flex-col xl:flex-row w-full gap-8">
<div className="w-full xl:w-[40%]">
<h2>
About Me
{/* <TextShuffler textInput="About Me" /> */}
</h2>
<div className="w-full xl:w-[40%]"></div>
<div className="flex flex-col-reverse xl:flex-col items-start gap-4">
<div className="flex flex-col w-full gap-2">
<div
className={"p-4 cursor-pointer hover:opacity-60" + (targetStack.match(/dev/i) ? " bg-[#343680] w-full xl:w-[120%]" : " w-full border border-solid border-white/10")}
onClick={() => {
setTargetStack("dev");
}}
<div className="w-full xl:w-[60%] px-4 md:px-6 py-2">
<section className="flex flex-col items-start gap-4">
<h2 className="mb-0">About Me</h2>
<span className="text-[24px]">Quick learner, adaptable, problem solver, curious. I strive to know the system, rather than the status quo. I thrive in difficult situations and complex hurdles: problem solving is now second nature to me.</span>
<hr className="w-full my-8" />
<h3 className="m-0">_ Code Ben</h3>
<span className="text-[24px]">In the last two years I've developed from a complete designer to pro software engineer. After countless days of writing code, debugging, testing, building projects, server administration, deployment, CI/CD, etc, I've developed the most vital skill of all: adaptability. The ability to asimilate new knowledge at record pace: the tech industry moves really fast: you either keepup, or fall behind.</span>
<hr className="w-full my-8" />
<h3 className="m-0">Graphic Design</h3>
<span className="text-[24px]">
After spending about 5 years in the design industry, I've picked up a few vital concepts about UI/UX design. My design path still sips into my developer life: and I must say, it's the perfect harmony of modern tech. Some of my designs can be found on my{" "}
<a
href="https://99designs.com/profiles/tben"
target="_blank"
>
<div>Web Dev Stack</div>
</div>
<div
className={"p-4 cursor-pointer hover:opacity-60" + (targetStack.match(/design/i) ? " bg-[#343680] w-full xl:w-[120%]" : " w-full border border-solid border-white/10")}
onClick={() => {
setTargetStack("design");
}}
>
<div>UI/UX Stack</div>
</div>
</div>
<span className="hero-sub-text">
Quick learner, adaptable, problem solver, curious. I strive to know the system, rather than the status quo. My credo is: no problem too great, no knowledge too vast, no logic too complex. I thrive in difficult situations and complex hurdles: problem solving is now second nature to me: if you can think it, it can be done.
{/* <TextShuffler
textInput="Quick learner, adaptable, problem solver, curious. I strive to know the system, rather than the status quo. My credo is: no problem too great, no knowledge too vast, no logic too complex. I thrive in difficult situations and complex hurdles: problem solving is now second nature to me: if you can think it, it can be done."
delay={500}
/> */}
99designs
</a>{" "}
profile.
</span>
<hr className="w-full my-8" />
<a
href="/about"
className="button"
className="button outlined"
>
Learn More About Me
</a>
</div>
</div>
<div className="w-full xl:w-[60%] bg-[#343680] px-4 md:px-6 py-2">
<section>
<h3>
{targetStack?.match(/dev/i) ? "Web Dev Tech Stack" : "UI/UX tech stack"}
{/* <TextShuffler textInput={targetStack?.match(/dev/i) ? "Web Dev Tech Stack" : "UI/UX tech stack"} /> */}
</h3>
<hr />
<ul style={{ maxWidth: "800px" }}>
{targetStack?.match(/dev/i) ? (
<React.Fragment>
{webDevStack.map((item: { title: string; description: string }, index: number) => (
<li
key={index}
className="mb-4"
>
<h4 className="m-0">
{item.title}
{/* <TextShuffler textInput={item.title} /> */}
</h4>
<span className="opacity-80">
{item.description}
{/* <TextShuffler textInput={item.description} /> */}
</span>
</li>
))}
</React.Fragment>
) : (
<React.Fragment>
{uiStack.map((item: { title: string; description: string }, index: number) => (
<li
key={index}
className="mb-4"
>
<h4 className="m-0">
{item.title}
{/* <TextShuffler textInput={item.title} /> */}
</h4>
<span className="opacity-80">
{item.description}
{/* <TextShuffler textInput={item.description} /> */}
</span>
</li>
))}
</React.Fragment>
)}
</ul>
</section>
</div>
</div>
+65
View File
@@ -0,0 +1,65 @@
"use client";
import React from "react";
import submitContactForm from "../../functions/frontend/submitContactForm";
export default function ContactForm() {
let [success, setSuccess] = React.useState<string | null>(null);
let [loading, setLoading] = React.useState<boolean>(false);
return (
<form
autoComplete="on"
onSubmit={(e: any) => {
submitContactForm(e, setSuccess, setLoading);
}}
>
{loading && <div className="flex items-center justify-center w-full h-full absolute top-0 left-0 bg-[black]/90">Sending Mail...</div>}
<input
type="text"
placeholder="Your Name"
autoComplete="name"
required
/>
<input
type="email"
placeholder="Your Email Address"
autoComplete="email"
required
/>
<textarea
name="message"
id="contact-form-message"
cols={30}
rows={10}
placeholder="Message"
></textarea>
<button type="submit">Submit</button>
{success === "Success" && (
<div className="message-response">
Success!!!{" "}
<button
onClick={() => {
window.location.reload();
}}
>
Reload
</button>
</div>
)}
{success === "Failed" && (
<div className="message-response failed">
Failed{" "}
<button
onClick={() => {
window.location.reload();
}}
>
Reload
</button>
</div>
)}
</form>
);
}
+24
View File
@@ -0,0 +1,24 @@
"use client";
import React from "react";
import ContactForm from "./ContactForm";
export default function ContactMeSection() {
return (
<div className="max-w-6xl w-full flex items-start page-section">
<div className="flex flex-col items-start gap-6 w-full xl:w-[50%]">
<div className="h-[150px]"></div>
<h2 className="m-0">Why So Serious?</h2>
<div className="flex flex-col-reverse xl:flex-col items-start gap-4">
<span className="text-[24px]">Let's talk.</span>
</div>
<div className="flex flex-col items-start w-full">
<ContactForm />
</div>
</div>
</div>
);
}
+108 -60
View File
@@ -2,73 +2,121 @@
import React from "react";
import Image from "next/image";
// import TextShuffler from "../../components/actions/TextShuffler";
import { hero } from "../(utils)/animate";
import { gsap } from "gsap";
import { TextPlugin } from "gsap/all";
gsap.registerPlugin(TextPlugin);
export default function Hero() {
React.useEffect(hero, []);
/**
* Handle animations on page load
*/
React.useEffect(() => {
const tl = gsap.timeline();
gsap.fromTo(
"#hero-sub-text",
{
opacity: 0,
},
{
opacity: 1,
delay: 0.5,
duration: 1,
}
);
gsap.to("#hero-text", {
text: "I'm Benjamin Toby, a Software Engineer and UI/UX expert.",
delay: 1,
duration: 2,
ease: "none",
});
gsap.fromTo(
".hero-button-link",
{
opacity: 0,
},
{
opacity: 1,
delay: 3,
duration: 1,
stagger: 0.5,
}
);
}, []);
/**
* Render component
*/
return (
<div>
<div className="flex flex-col xl:flex-row items-center gap-0 justify-center -mt-[80px] xl:-mt-[160px] w-full relative pt-[500px] md:pt-0">
<div
className="rounded-full max-w-[450px] absolute md:relative top-0 flex items-center md:items-start justify-center mr-0 xl:-mr-14 bg-[#3e3f9c] overflow-hidden"
id="main-image"
>
<Image
src="/images/my-photo.png"
// fill
width={500}
height={562}
objectFit="contain"
alt="Benjamin Toby Image"
className="contrast-[140%] grayscale mt-20 flex items-center justify-center"
/>
</div>
<div
className="flex flex-col xl:flex-row items-center gap-0 justify-center -mt-[80px] xl:-mt-[160px] w-full relative pt-[500px] xl:pt-0 page-section"
id="hero-content-wrapper"
>
<div
className="rounded-full max-w-[450px] absolute md:relative flex items-center md:items-start justify-center mr-0 xl:-mr-14 bg-[#3e3f9c] overflow-hidden"
id="main-image"
style={{
transform: "scale(1.2)",
}}
>
<img
src="/images/my-photo-stroked.png"
width={500}
height={562}
alt="Benjamin Toby Image"
className="contrast-[140%] grayscale mt-20 flex items-center justify-center object-contain"
/>
</div>
<div
className="max-w-2xl relative z-10 mt-10 w-full"
id="hero-text-section"
<div
className="max-w-2xl relative z-10 mt-10 w-full"
id="hero-text-section"
>
<span
id="hero-sub-text"
className="uppercase bg-primary_light text-[black]/80 font-bold px-3 py-1 tracking-wide"
>
<h1
className="text-4xl md:text-5xl leading-snug"
id="hero-text"
Howdy Tech Enthusiasts
</span>
<div className="h-4"></div>
<h1
className="text-5xl md:text-[58px] leading-[1.1] gsap-text h-auto xl:h-[220px]"
id="hero-text"
></h1>
<div className="h-10 flex xl:hidden"></div>
<div className="gap-4 flex items-center flex-wrap">
<a
href="https://www.linkedin.com/in/benjamin-toby/"
target="_blank"
className="button grow hero-button-link"
>
I'm Benjamin Toby, a Software Engineer and UI/UX expert
{/* Fullstack developer, UI/UX designer, Software Engineer, welcome */}
{/* <TextShuffler
textInput="I'm Benjamin Toby, a Software Engineer and UI/UX expert"
delay={500}
/> */}
</h1>
<div className="gap-4 flex items-center flex-wrap">
<a
href="/documents/Resume-Benjamin-Toby-Linkedin.pdf"
download={true}
className="button grow"
>
See my resume
</a>
<a
href="https://www.linkedin.com/in/benjamin-toby/"
target="_blank"
className="button grow"
>
Linkedin
</a>
<a
href="/contact"
style={{
backgroundColor: "transparent",
color: "white",
border: "2px solid white",
}}
className="button grow"
>
Contact Me
</a>
</div>
Linkedin
</a>
<a
href="/contact"
style={{
backgroundColor: "transparent",
color: "white",
border: "2px solid white",
}}
className="button grow hero-button-link"
>
Contact Me
</a>
<a
href="/documents/Resume-Benjamin-Toby-Linkedin.pdf"
download={true}
className="button grow hero-button-link"
style={{
backgroundColor: "transparent",
color: "white",
border: "2px solid white",
}}
>
See my resume
</a>
</div>
</div>
</div>
+56
View File
@@ -0,0 +1,56 @@
"use client";
import React from "react";
import Hero from "./Hero";
import AboutSection from "./AboutSection";
import { gsap } from "gsap";
import MyWorkSection from "./MyWorkSection";
import MySkillsSection from "./MySkillsSection";
import homepageTimeline from "../(utils)/homepage-timeline";
import { SiteContext } from "../../layouts/general_layout/GeneralLayout";
import ContactMeSection from "./ContactMeSection";
export type Project = {
id: number;
title: string;
description: string;
image?: string | null;
url?: string | null;
full_description?: string;
};
type ChildProps = {
projects: Project[];
};
export default function HomepageComponent({ projects }: ChildProps) {
const layoutContext: any = React.useContext(SiteContext);
if (layoutContext) layoutContext.projects = projects;
const comp = React.useRef<HTMLDivElement>(null);
React.useLayoutEffect(() => {
let ctx = gsap.context(() => {
homepageTimeline({ componentRef: comp });
}, comp);
return () => ctx.revert();
}, []);
return (
<React.Fragment>
<div
id="homepage-content-wrapper"
ref={comp}
className="z-0 xl:z-50 mb-32"
>
<Hero />
<AboutSection />
<MySkillsSection />
<MyWorkSection />
<ContactMeSection />
</div>
</React.Fragment>
);
}
+91
View File
@@ -0,0 +1,91 @@
"use client";
import React from "react";
export default function MySkillsSection() {
const webDevStack = require("../(utils)/web-dev-stack.json");
const uiStack = require("../(utils)/ui-ux-stack.json");
const [targetStack, setTargetStack] = React.useState("dev");
return (
<div className="max-w-6xl w-full flex flex-col items-start pb-40 page-section">
<div className="h-[200px]"></div>
<div className="flex flex-col items-start gap-6 w-full xl:w-[50%]">
<h2 className="mb-0">My Skills</h2>
<span className="text-[24px]">I am well-versed in the full spectrum of design and development. While I started out as a designer, I have focused more on development in the last few years.</span>
<hr className="w-full" />
<h3 className="m-0">{targetStack?.match(/dev/i) ? "Web Dev Tech Stack" : "UI/UX tech stack"}</h3>
<div className="flex flex-col xl:flex-col items-start gap-4 w-full">
{/* <h3>My Tech Stack</h3> */}
<div className="flex flex-wrap w-full gap-2">
<div
className={"p-4 text-lg cursor-pointer grow" + (targetStack.match(/dev/i) ? " bg-[white] text-[black] font-bold" : " border border-solid border-white/10 hover:opacity-60 ")}
onClick={() => {
setTargetStack("dev");
}}
>
<div>Web Dev Stack</div>
</div>
<div
className={"p-4 text-lg cursor-pointer grow" + (targetStack.match(/design/i) ? " bg-[white] text-[black] font-bold" : " border border-solid border-white/10 hover:opacity-60 ")}
onClick={() => {
setTargetStack("design");
}}
>
<div>UI/UX Stack</div>
</div>
</div>
</div>
<ul
style={{ maxWidth: "800px" }}
className="pl-6"
>
{targetStack?.match(/dev/i) ? (
<React.Fragment>
{webDevStack.map((item: { title: string; description: string }, index: number) => (
<li
key={index}
className="mb-4"
>
<h4 className="m-0 text-[#BBE572]">
{item.title}
{/* <TextShuffler textInput={item.title} /> */}
</h4>
<span className="opacity-80">
{item.description}
{/* <TextShuffler textInput={item.description} /> */}
</span>
</li>
))}
</React.Fragment>
) : (
<React.Fragment>
{uiStack.map((item: { title: string; description: string }, index: number) => (
<li
key={index}
className="mb-4"
>
<h4 className="m-0 text-[#BBE572]">
{item.title}
{/* <TextShuffler textInput={item.title} /> */}
</h4>
<span className="opacity-80">
{item.description}
{/* <TextShuffler textInput={item.description} /> */}
</span>
</li>
))}
</React.Fragment>
)}
</ul>
</div>
</div>
);
}
+36
View File
@@ -0,0 +1,36 @@
"use client";
import React from "react";
import { Project } from "./HomepageComponent";
import Image from "next/image";
export default function MyWorkCard({ project }: { project: Project }) {
return (
<div className="bg-primary/10 w-full shadow-2xl shadow-[black] relative">
<Image
src={project.image || ""}
alt={project.title}
width={600}
height={300}
className="object-cover object-top w-full"
/>
<div className="p-10">
<h3 className="m-0">{project.title}</h3>
<p className="m-0">{project.description}</p>
</div>
<a
href={project.url || "#"}
className="absolute z-10 top-[340px] right-6 w-10 h-10 p-2 flex items-center justify-center rounded-full bg-[white]"
target="_blank"
>
<img
src="/images/external-link-dark.png"
alt="External Link Icon"
width={20}
height={20}
/>
</a>
</div>
);
}
+43
View File
@@ -0,0 +1,43 @@
"use client";
import React from "react";
import { SiteContext } from "../../layouts/general_layout/GeneralLayout";
import { Project } from "./HomepageComponent";
import MyWorkCard from "./MyWorkCard";
export default function MyWorkSection() {
const layoutContext: { projects: Project[] } = React.useContext(SiteContext);
const projects = layoutContext.projects;
return (
<div className="max-w-6xl w-full flex flex-col xl:flex-row items-start pb-40 page-section">
<div className="flex flex-col items-start gap-6 w-full xl:w-[50%]"></div>
<div className="flex flex-col items-start gap-6 w-full xl:w-[50%]">
<div className="h-[150px]"></div>
<h2 className="m-0">Some of my work</h2>
<div className="flex flex-col-reverse xl:flex-col items-start gap-4">
<span className="text-[24px]">I've been creating and building awesome designs and applications since 2016. Here are a few picks:</span>
</div>
<div className="flex flex-col items-start gap-44 mt-10">
{projects &&
projects.slice(0, 4).map((project, index) => (
<MyWorkCard
key={index}
project={project}
/>
))}
</div>
<a
href="/work"
className="button outlined mt-10"
>
See More
</a>
</div>
</div>
);
}
+206
View File
@@ -0,0 +1,206 @@
import { gsap } from "gsap";
import { ScrollTrigger, Observer } from "gsap/all";
gsap.registerPlugin(ScrollTrigger, Observer);
export default function homepageTimeline({ componentRef }: { componentRef: { current: HTMLDivElement | null } }) {
const mm = gsap.matchMedia();
const compHeight = componentRef.current?.clientHeight;
const isMobile = window.innerWidth <= 1200;
// mm.add("(min-width: 1200px)", () => {
ScrollTrigger.create({
trigger: "#homepage-content-wrapper",
start: "200px 200px",
end: "bottom 90%",
pin: isMobile ? null : "#main-image",
scrub: 1,
onUpdate: (self) => {
const scrollTop = compHeight ? self.progress * compHeight : 0;
/**
* Origin
*/
if (scrollTop < 300) {
gsap.to("nav", {
opacity: 1,
pointerEvents: "visible",
});
gsap.to("header", {
zIndex: 2000,
});
} else {
gsap.to("nav", {
opacity: 0,
pointerEvents: "none",
});
gsap.to("header", {
zIndex: 0,
});
}
/**
* Handle Mobile
*/
if (isMobile && scrollTop > 100) {
gsap.to("#main-image", {
opacity: 0.1,
duration: 1,
});
} else {
gsap.to("#main-image", {
opacity: 1,
duration: 1,
});
}
},
onEnter: (self) => {},
});
/**
* Position color map
*/
const positionColorMap: {
x: number;
imgBg: string;
bodyBg: string;
duration: number;
imgSrc?: string;
}[] = [
{
x: 0,
imgBg: "#3e3f9c",
bodyBg: "#181515",
duration: 1,
imgSrc: "/images/my-photo-stroked.png",
},
{
x: -50,
imgBg: "#FE6847",
bodyBg: "#292a6b",
duration: 1,
imgSrc: "/images/my-photo-3.png",
},
{
x: 600,
imgBg: "#688e26",
bodyBg: "#181515",
duration: 1.2,
imgSrc: "/images/programming-laptop.png",
},
{
x: -50,
imgBg: "#1B2CC1",
bodyBg: "#091540",
duration: 1.2,
imgSrc: "/images/projects-section-image.png",
},
{
x: 600,
imgBg: "#ffd87d",
bodyBg: "#3e3f9c",
duration: 1.2,
imgSrc: "/images/why-so-serious.png",
},
];
/**
* Activate section based on scroll position
*/
const sections = document.querySelectorAll(".page-section");
function activateSection(index: number) {
const targetPositionMap = positionColorMap[index];
if (!targetPositionMap) {
return;
}
gsap.to("#main-image", {
backgroundColor: targetPositionMap.imgBg,
x: isMobile ? 0 : targetPositionMap.x,
duration: targetPositionMap.duration,
});
gsap.to("body", {
backgroundColor: targetPositionMap.bodyBg,
});
if (targetPositionMap?.imgSrc) {
changeMainImage(targetPositionMap.imgSrc);
}
if (index >= 2) {
gsap.to("#main-image img", {
filter: "none",
duration: 1,
});
} else {
gsap.to("#main-image img", {
filter: "grayscale(100%) contrast(140%)",
duration: 1,
});
}
}
sections.forEach((section, index) => {
ScrollTrigger.create({
trigger: section,
start: "top 50%",
end: "bottom 50%",
onEnter: (self) => {
activateSection(index);
},
onEnterBack: (self) => {
activateSection(index);
},
});
});
/**
* Entry animation timeline
*/
const entryTimeline = gsap.timeline();
entryTimeline
.from("#main-image", {
clipPath: "circle(0)",
})
.to("#main-image", {
clipPath: "circle(100%)",
scale: 1,
duration: 1,
});
gsap.to("#main-image", {
y: 20,
repeat: -1,
duration: 2,
yoyo: true,
ease: "sine.inOut",
});
// });
}
async function changeMainImage(src: string) {
const mainImage: HTMLImageElement | null = document.querySelector("#main-image img");
if (mainImage) {
await gsap.to(mainImage, {
opacity: 0,
duration: 0.5,
});
if (mainImage) {
mainImage.setAttribute("src", src);
mainImage.onload = () => {
gsap.to(mainImage, {
opacity: 1,
duration: 0.5,
delay: 0.5,
});
};
}
}
}
-4
View File
@@ -1,8 +1,4 @@
[
{
"title": "HTML, CSS, Javascript",
"description": "The basics, the bedrock of all websites."
},
{
"title": "HTML, CSS, Javascript",
"description": "The basics, the bedrock of all websites."
+2 -17
View File
@@ -1,37 +1,22 @@
"use client";
import React from "react";
// import TextShuffler from "../../../components/actions/TextShuffler";
import { appear, genericScroll } from "../../(utils)/animate";
export default function Hero() {
React.useEffect(genericScroll, []);
React.useEffect(appear, []);
return (
<div className="-mt-10 appear">
<div
className="flex flex-col items-start gap-0 max-w-6xl w-full relative generic-scroll"
// id="hero-text-section"
>
<span className="text-primary-light text-lg">
About Me
{/* <TextShuffler textInput="About Me" /> */}
</span>
<span className="text-primary-light text-lg">About Me</span>
<h1
className="text-5xl leading-snug"
id="hero-text"
>
Ben of All Trades, Master of All
{/* <TextShuffler textInput="Ben of All Trade, Master of All" /> */}
</h1>
<span className="hero-sub-text">
Quick learner, adaptable, problem solver, curious. I strive to know the system, rather than the status quo. My credo is: no problem too great, no knowledge too vast, no logic too complex. I thrive in difficult situations and complex hurdles: problem solving is now second nature to me: if you can think it, it can be done.
{/* <TextShuffler
textInput="Quick learner, adaptable, problem solver, curious. I strive to know the system, rather than the status quo. My credo is: no problem too great, no knowledge too vast, no logic too complex. I thrive in difficult situations and complex hurdles: problem solving is now second nature to me: if you can think it, it can be done."
delay={1000}
/> */}
</span>
<span className="hero-sub-text">Quick learner, adaptable, problem solver, curious. I strive to know the system, rather than the status quo. My credo is: no problem too great, no knowledge too vast, no logic too complex. I thrive in difficult situations and complex hurdles: problem solving is now second nature to me: if you can think it, it can be done.</span>
</div>
</div>
);
-5
View File
@@ -1,13 +1,8 @@
"use client";
import React from "react";
// import TextShuffler from "../../../components/actions/TextShuffler";
import { appear, genericScroll } from "../../(utils)/animate";
export default function MoreAboutMe() {
React.useEffect(genericScroll, []);
React.useEffect(appear, []);
const webDevStack = require("../../(utils)/web-dev-stack.json");
const uiStack = require("../../(utils)/ui-ux-stack.json");
+15
View File
@@ -18,6 +18,21 @@ export default function RootLayout({ children }: { children: React.ReactNode })
return (
<html lang="en">
<head>
{/* <script
defer
src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"
></script>
<script
defer
src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/ScrollTrigger.min.js"
></script>
<script
defer
src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/TextPlugin.min.js"
></script> */}
<script src="https://cdn.jsdelivr.net/gh/studio-freight/lenis@1/bundled/lenis.min.js"></script>
<script
src="/scripts/main.js"
defer
+18 -9
View File
@@ -1,12 +1,21 @@
import React from "react";
import Hero from "./(components)/Hero";
import AboutSection from "./(components)/AboutSection";
import HomepageComponent from "./(components)/HomepageComponent";
export default function Homepage() {
return (
<React.Fragment>
<Hero />
<AboutSection />
</React.Fragment>
);
const datasquirel = require("datasquirel");
export const revalidate = 3600;
type DsqlResponse = {
success: boolean;
payload: any;
};
export default async function Homepage() {
const projects: DsqlResponse = await datasquirel.get({
db: process.env.DB_NAME,
key: process.env.DATASQUIREL_API_KEY,
query: "SELECT * FROM portfolio ORDER BY project_order ASC",
});
return <HomepageComponent projects={projects.payload} />;
}