This commit is contained in:
Benjamin Toby 2023-10-24 18:59:00 +01:00
parent d163438f7b
commit b9364a702c
68 changed files with 4701 additions and 4697 deletions

8
.gitignore vendored
View File

@ -1,5 +1,5 @@
node_modules
.next
node_shell
.env
node_modules
.next
node_shell
.env
/dump

View File

@ -1,20 +1,20 @@
# Set Node.js version
FROM node:16
# Set working directory
WORKDIR /usr/src/app
# Copy package.json and package-lock.json
COPY package*.json ./
# Install dependencies
RUN npm install
# Copy source code
COPY . .
# Expose port 3000
EXPOSE 3000
# Run the app
# Set Node.js version
FROM node:16
# Set working directory
WORKDIR /usr/src/app
# Copy package.json and package-lock.json
COPY package*.json ./
# Install dependencies
RUN npm install
# Copy source code
COPY . .
# Expose port 3000
EXPOSE 3000
# Run the app
CMD ["npm", "run", "build", "&&", "npm", "start"]

View File

@ -1,2 +1,2 @@
# personal_site
My personal site
# personal_site
My personal site

View File

@ -1,61 +1,61 @@
/** # MODULE TRACE
======================================================================
* Detected 1 files that call this module. The files are listed below:
======================================================================
* `import` Statement Found in [HomepageComponent.tsx] => file:///d:\GitHub\personal_site\app\(components)\HomepageComponent.tsx
==== MODULE TRACE END ==== */
"use client";
import React from "react";
export default function AboutSection() {
return (
<div
className="max-w-6xl w-full flex flex-col items-center pb-40 generic-scroll page-section"
id="about-section"
>
<div className="h-44"></div>
<div className="flex flex-col xl:flex-row w-full gap-8">
<div className="w-full xl:w-[40%]"></div>
<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"
>
99designs
</a>{" "}
profile.
</span>
<hr className="w-full my-8" />
<a
href="/about"
className="button outlined"
>
Learn More About Me
</a>
</section>
</div>
</div>
</div>
);
}
/** # MODULE TRACE
======================================================================
* Detected 1 files that call this module. The files are listed below:
======================================================================
* `import` Statement Found in [HomepageComponent.tsx] => file:///d:\GitHub\personal_site\app\(components)\HomepageComponent.tsx
==== MODULE TRACE END ==== */
"use client";
import React from "react";
export default function AboutSection() {
return (
<div
className="max-w-6xl w-full flex flex-col items-center pb-40 generic-scroll page-section"
id="about-section"
>
<div className="h-44"></div>
<div className="flex flex-col xl:flex-row w-full gap-8">
<div className="w-full xl:w-[40%]"></div>
<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"
>
99designs
</a>{" "}
profile.
</span>
<hr className="w-full my-8" />
<a
href="/about"
className="button outlined"
>
Learn More About Me
</a>
</section>
</div>
</div>
</div>
);
}

View File

@ -1,65 +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>
);
}
"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>
);
}

View File

@ -1,24 +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>
);
}
"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>
);
}

View File

@ -1,124 +1,124 @@
"use client";
import React from "react";
import Image from "next/image";
import { gsap } from "gsap";
import { TextPlugin } from "gsap/all";
gsap.registerPlugin(TextPlugin);
export default function 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
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"
>
<span
id="hero-sub-text"
className="uppercase bg-primary_light text-[black]/80 font-bold px-3 py-1 tracking-wide"
>
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"
>
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>
);
}
"use client";
import React from "react";
import Image from "next/image";
import { gsap } from "gsap";
import { TextPlugin } from "gsap/all";
gsap.registerPlugin(TextPlugin);
export default function 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
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"
>
<span
id="hero-sub-text"
className="uppercase bg-primary_light text-[black]/80 font-bold px-3 py-1 tracking-wide"
>
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"
>
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>
);
}

View File

@ -1,56 +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>
);
}
"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>
);
}

View File

@ -1,91 +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>
);
}
"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>
);
}

View File

@ -1,36 +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>
);
}
"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>
);
}

View File

@ -1,43 +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>
);
}
"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>
);
}

View File

@ -1,6 +1,6 @@
header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
}
header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 10px;
}

View File

@ -1,149 +1,149 @@
import { gsap } from "gsap";
import ScrollTrigger from "gsap/ScrollTrigger";
import TextPlugin from "gsap/TextPlugin";
/**
* Register plugins
*/
gsap.registerPlugin(ScrollTrigger);
gsap.registerPlugin(TextPlugin);
/**
* Animates the hero section
*/
export function hero() {
/**
* Animate hero section
*/
gsap.fromTo(
"#main-image",
{
z: -100,
scale: 1.2,
},
{
z: 0,
duration: 1.5,
scale: 1,
}
);
gsap.fromTo(
"#hero-text-section",
{
z: -100,
scale: 1.2,
},
{
z: 0,
duration: 2.5,
scale: 1,
}
);
gsap.fromTo(
"#main-image",
{
y: 0,
// scale: 1,
},
{
scrollTrigger: {
scrub: 1,
},
y: 150,
// scale: 0.8,
}
);
gsap.fromTo(
"#hero-text-section",
{
y: 0,
// scale: 1,
},
{
scrollTrigger: {
scrub: 2,
},
y: 100,
// scale: 0.8,
}
);
}
/**
* Animates the about section
*/
export function about() {
gsap.fromTo(
"#about-section",
{
y: 40,
scale: 0.8,
},
{
scrollTrigger: {
scrub: 4,
},
y: 0,
scale: 1.1,
}
);
gsap.to("#about-me-label", {
scrollTrigger: {
scrub: 2,
},
y: 300,
scale: 0.8,
});
}
/**
* Animates the about section
*/
export function genericScroll() {
gsap.fromTo(
".generic-scroll",
{
y: 0,
scale: 0.9,
},
{
scrollTrigger: {
scrub: 3,
},
y: 100,
scale: 1,
}
);
}
/**
* Animates the about section
*/
export function appear() {
gsap.fromTo(
".appear",
{
y: 40,
scale: 0.8,
opacity: 0,
},
{
y: 0,
scale: 1,
opacity: 1,
duration: 2,
delay: (i, target) => {
const datasetDelay = target?.dataset?.delay;
if (datasetDelay) {
return datasetDelay;
}
return 0;
},
}
);
}
import { gsap } from "gsap";
import ScrollTrigger from "gsap/ScrollTrigger";
import TextPlugin from "gsap/TextPlugin";
/**
* Register plugins
*/
gsap.registerPlugin(ScrollTrigger);
gsap.registerPlugin(TextPlugin);
/**
* Animates the hero section
*/
export function hero() {
/**
* Animate hero section
*/
gsap.fromTo(
"#main-image",
{
z: -100,
scale: 1.2,
},
{
z: 0,
duration: 1.5,
scale: 1,
}
);
gsap.fromTo(
"#hero-text-section",
{
z: -100,
scale: 1.2,
},
{
z: 0,
duration: 2.5,
scale: 1,
}
);
gsap.fromTo(
"#main-image",
{
y: 0,
// scale: 1,
},
{
scrollTrigger: {
scrub: 1,
},
y: 150,
// scale: 0.8,
}
);
gsap.fromTo(
"#hero-text-section",
{
y: 0,
// scale: 1,
},
{
scrollTrigger: {
scrub: 2,
},
y: 100,
// scale: 0.8,
}
);
}
/**
* Animates the about section
*/
export function about() {
gsap.fromTo(
"#about-section",
{
y: 40,
scale: 0.8,
},
{
scrollTrigger: {
scrub: 4,
},
y: 0,
scale: 1.1,
}
);
gsap.to("#about-me-label", {
scrollTrigger: {
scrub: 2,
},
y: 300,
scale: 0.8,
});
}
/**
* Animates the about section
*/
export function genericScroll() {
gsap.fromTo(
".generic-scroll",
{
y: 0,
scale: 0.9,
},
{
scrollTrigger: {
scrub: 3,
},
y: 100,
scale: 1,
}
);
}
/**
* Animates the about section
*/
export function appear() {
gsap.fromTo(
".appear",
{
y: 40,
scale: 0.8,
opacity: 0,
},
{
y: 0,
scale: 1,
opacity: 1,
duration: 2,
delay: (i, target) => {
const datasetDelay = target?.dataset?.delay;
if (datasetDelay) {
return datasetDelay;
}
return 0;
},
}
);
}

View File

@ -1,208 +1,208 @@
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 (!isMobile) {
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,
});
};
}
}
}
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 (!isMobile) {
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,
});
};
}
}
}

View File

@ -1,30 +1,30 @@
[
{
"title": "Adobe Photoshop",
"description": "Web design, image manipulation, image compositing, and more."
},
{
"title": "Adobe Illustrator",
"description": "Vector graphic of all types"
},
{
"title": "Figma",
"description": "Web, UI, UX design."
},
{
"title": "Affinity Designer",
"description": "Vector graphics."
},
{
"title": "After Effects",
"description": "Motion graphics and animation"
},
{
"title": "Adobe XD",
"description": "UI/UX design"
},
{
"title": "Webflow",
"description": "Visual Web coding"
}
]
[
{
"title": "Adobe Photoshop",
"description": "Web design, image manipulation, image compositing, and more."
},
{
"title": "Adobe Illustrator",
"description": "Vector graphic of all types"
},
{
"title": "Figma",
"description": "Web, UI, UX design."
},
{
"title": "Affinity Designer",
"description": "Vector graphics."
},
{
"title": "After Effects",
"description": "Motion graphics and animation"
},
{
"title": "Adobe XD",
"description": "UI/UX design"
},
{
"title": "Webflow",
"description": "Visual Web coding"
}
]

View File

@ -1,38 +1,38 @@
[
{
"title": "HTML, CSS, Javascript",
"description": "The basics, the bedrock of all websites."
},
{
"title": "React JS",
"description": "JavaScript library for high-performance web applications"
},
{
"title": "Next JS",
"description": "High performance React and Node js web framework for building blazing flast and performant web applications"
},
{
"title": "Tailwind CSS",
"description": "Lighting fast mobile first styling"
},
{
"title": "Node JS",
"description": "JavaScript runtime for the server. For creating backend architectures and APIs"
},
{
"title": "Ubuntu Linux",
"description": "Secure server management with ubuntu and Linux"
},
{
"title": "Nginx",
"description": "Super secure web server, reverse proxy and load balancer"
},
{
"title": "MySQL",
"description": "Tried and tested data storage, querying, and management."
},
{
"title": "Git and Github",
"description": "Version control"
}
]
[
{
"title": "HTML, CSS, Javascript",
"description": "The basics, the bedrock of all websites."
},
{
"title": "React JS",
"description": "JavaScript library for high-performance web applications"
},
{
"title": "Next JS",
"description": "High performance React and Node js web framework for building blazing flast and performant web applications"
},
{
"title": "Tailwind CSS",
"description": "Lighting fast mobile first styling"
},
{
"title": "Node JS",
"description": "JavaScript runtime for the server. For creating backend architectures and APIs"
},
{
"title": "Ubuntu Linux",
"description": "Secure server management with ubuntu and Linux"
},
{
"title": "Nginx",
"description": "Super secure web server, reverse proxy and load balancer"
},
{
"title": "MySQL",
"description": "Tried and tested data storage, querying, and management."
},
{
"title": "Git and Github",
"description": "Version control"
}
]

View File

@ -1,23 +1,27 @@
"use client";
import React from "react";
export default function Hero() {
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</span>
<h1
className="text-5xl leading-snug"
id="hero-text"
>
Ben of All Trades, 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.</span>
</div>
</div>
);
}
"use client";
import React from "react";
export default function Hero() {
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</span>
<h1 className="text-5xl leading-snug" id="hero-text">
I live on the bleeding edge of technology
</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.
</span>
</div>
</div>
);
}

View File

@ -1,98 +1,98 @@
"use client";
import React from "react";
export default function MoreAboutMe() {
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">
<div className="h-20"></div>
<div
className="flex flex-col w-full gap-8 appear"
data-delay={2}
>
<div className="w-full">
<h2>
More About Me
{/* <TextShuffler textInput="More About Me" /> */}
</h2>
<div className="flex flex-col items-start gap-4">
<div className="flex w-full gap-4">
<div
className={"p-4 cursor-pointer hover:opacity-60 grow" + (targetStack.match(/dev/i) ? " bg-[#343680]" : " border border-solid border-white/10")}
onClick={() => {
setTargetStack("dev");
}}
>
<div>Web Dev Stack</div>
</div>
<div
className={"p-4 cursor-pointer hover:opacity-60 grow" + (targetStack.match(/design/i) ? " bg-[#343680]" : " border border-solid border-white/10")}
onClick={() => {
setTargetStack("design");
}}
>
<div>UI/UX Stack</div>
</div>
</div>
</div>
</div>
<div className="w-full 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>
</div>
);
}
"use client";
import React from "react";
export default function MoreAboutMe() {
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">
<div className="h-20"></div>
<div
className="flex flex-col w-full gap-8 appear"
data-delay={2}
>
<div className="w-full">
<h2>
More About Me
{/* <TextShuffler textInput="More About Me" /> */}
</h2>
<div className="flex flex-col items-start gap-4">
<div className="flex w-full gap-4">
<div
className={"p-4 cursor-pointer hover:opacity-60 grow" + (targetStack.match(/dev/i) ? " bg-[#343680]" : " border border-solid border-white/10")}
onClick={() => {
setTargetStack("dev");
}}
>
<div>Web Dev Stack</div>
</div>
<div
className={"p-4 cursor-pointer hover:opacity-60 grow" + (targetStack.match(/design/i) ? " bg-[#343680]" : " border border-solid border-white/10")}
onClick={() => {
setTargetStack("design");
}}
>
<div>UI/UX Stack</div>
</div>
</div>
</div>
</div>
<div className="w-full 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>
</div>
);
}

View File

@ -1,12 +1,12 @@
import React from "react";
import MoreAboutMe from "./(components)/MoreAboutMe";
import Hero from "./(components)/Hero";
export default function AboutPage() {
return (
<React.Fragment>
<Hero />
<MoreAboutMe />
</React.Fragment>
);
}
import React from "react";
import MoreAboutMe from "./(components)/MoreAboutMe";
import Hero from "./(components)/Hero";
export default function AboutPage() {
return (
<React.Fragment>
<Hero />
<MoreAboutMe />
</React.Fragment>
);
}

View File

@ -1,17 +1,17 @@
import { NextResponse } from "next/server";
// export async function GET(request: Request, context?: {}) {
// console.log(request.headers);
// return NextResponse.json({ name: "Benjamin Toby" });
// }
export async function GET(request: Request) {
return new Response(JSON.stringify({ name: "Benjamin Toby" }), {
status: 200,
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Authorization",
},
});
}
import { NextResponse } from "next/server";
// export async function GET(request: Request, context?: {}) {
// console.log(request.headers);
// return NextResponse.json({ name: "Benjamin Toby" });
// }
export async function GET(request: Request) {
return new Response(JSON.stringify({ name: "Benjamin Toby" }), {
status: 200,
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Authorization",
},
});
}

View File

@ -1,178 +1,178 @@
/**
* ==============================================================================
* Imports
* ==============================================================================
*/
import React from "react";
import Head from "next/head";
import type { InferGetStaticPropsType, GetStaticProps, GetStaticPaths } from "next";
const datasquirel = require("datasquirel");
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
import GeneralLayout from "../../layouts/general_layout/GeneralLayout";
import TextShuffler from "../../components/actions/TextShuffler";
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/**
* ==============================================================================
* Main Component { Functional }
* ==============================================================================
* @param {Object} props - Server props
*/
export default function BlogIndex({ blogPost }: InferGetStaticPropsType<typeof getStaticProps>) {
// ## Get Contexts
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## Javascript Variables
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## React Hooks { useState, useEffect, useRef, etc ... }
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## Function Return
return (
<React.Fragment>
<Head>
<title>{blogPost.title} | Tben.me Blog</title>
<meta
name="description"
content={blogPost.excerpt}
/>
</Head>
<GeneralLayout>
<div className="flex flex-col items-start gap-2 mb-8 max-w-6xl w-full">
<button
className="bg-transparent text-white/50 border-2 border-solid border-white/20"
onClick={(e) => {
window.history.back();
}}
>
Back
</button>
<h1 className="m-0">
<TextShuffler textInput={blogPost.title} />
</h1>
<span className="text-lg">
<TextShuffler textInput={blogPost.excerpt} />
</span>
<span className="text-base opacity-50">
<TextShuffler textInput={blogPost.date_created.substring(0, 24)} />
</span>
</div>
<span
className="flex flex-col items-start max-w-6xl w-full gap-4 text-xl"
dangerouslySetInnerHTML={{ __html: blogPost.body }}
></span>
</GeneralLayout>
</React.Fragment>
);
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
}
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/**
* Server Side Props or Static Props
* ==============================================================================
*/
export const getStaticProps: GetStaticProps = async ({ params }) => {
// ## Environment processes
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## User Authentication
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## Page/Site Data Data Fetching
const postsResponse = await datasquirel.get({
key: process.env.DATASQUIREL_API_KEY,
db: "tbenme",
query: `select * from blog_posts WHERE slug='${params?.single}'`,
});
const post = postsResponse.payload[0];
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## Server Props Return
return {
props: {
blogPost: post,
},
revalidate: 3600,
};
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
};
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/**
* Server Side Props or Static Props
* ==============================================================================
*/
export const getStaticPaths: GetStaticPaths = async () => {
/**
* Data fetching
*
* @abstract fetch date from the server or externnal source
*/
const postsResponse = await datasquirel.get({
key: process.env.DATASQUIREL_API_KEY,
db: "tbenme",
query: `select slug from blog_posts`,
});
const posts: { slug: string }[] | null = postsResponse.payload;
const paths = posts?.map((entry) => {
return {
params: { single: entry.slug },
};
});
return {
paths: paths ? paths : [],
fallback: "blocking",
};
};
/**
* ==============================================================================
* Imports
* ==============================================================================
*/
import React from "react";
import Head from "next/head";
import type { InferGetStaticPropsType, GetStaticProps, GetStaticPaths } from "next";
const datasquirel = require("datasquirel");
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
import GeneralLayout from "../../layouts/general_layout/GeneralLayout";
import TextShuffler from "../../components/actions/TextShuffler";
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/**
* ==============================================================================
* Main Component { Functional }
* ==============================================================================
* @param {Object} props - Server props
*/
export default function BlogIndex({ blogPost }: InferGetStaticPropsType<typeof getStaticProps>) {
// ## Get Contexts
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## Javascript Variables
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## React Hooks { useState, useEffect, useRef, etc ... }
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## Function Return
return (
<React.Fragment>
<Head>
<title>{blogPost.title} | Tben.me Blog</title>
<meta
name="description"
content={blogPost.excerpt}
/>
</Head>
<GeneralLayout>
<div className="flex flex-col items-start gap-2 mb-8 max-w-6xl w-full">
<button
className="bg-transparent text-white/50 border-2 border-solid border-white/20"
onClick={(e) => {
window.history.back();
}}
>
Back
</button>
<h1 className="m-0">
<TextShuffler textInput={blogPost.title} />
</h1>
<span className="text-lg">
<TextShuffler textInput={blogPost.excerpt} />
</span>
<span className="text-base opacity-50">
<TextShuffler textInput={blogPost.date_created.substring(0, 24)} />
</span>
</div>
<span
className="flex flex-col items-start max-w-6xl w-full gap-4 text-xl"
dangerouslySetInnerHTML={{ __html: blogPost.body }}
></span>
</GeneralLayout>
</React.Fragment>
);
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
}
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/**
* Server Side Props or Static Props
* ==============================================================================
*/
export const getStaticProps: GetStaticProps = async ({ params }) => {
// ## Environment processes
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## User Authentication
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## Page/Site Data Data Fetching
const postsResponse = await datasquirel.get({
key: process.env.DATASQUIREL_API_KEY,
db: "tbenme",
query: `select * from blog_posts WHERE slug='${params?.single}'`,
});
const post = postsResponse.payload[0];
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## Server Props Return
return {
props: {
blogPost: post,
},
revalidate: 3600,
};
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
};
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/**
* Server Side Props or Static Props
* ==============================================================================
*/
export const getStaticPaths: GetStaticPaths = async () => {
/**
* Data fetching
*
* @abstract fetch date from the server or externnal source
*/
const postsResponse = await datasquirel.get({
key: process.env.DATASQUIREL_API_KEY,
db: "tbenme",
query: `select slug from blog_posts`,
});
const posts: { slug: string }[] | null = postsResponse.payload;
const paths = posts?.map((entry) => {
return {
params: { single: entry.slug },
};
});
return {
paths: paths ? paths : [],
fallback: "blocking",
};
};

View File

@ -1,72 +1,72 @@
/////////////////////////////////////////////
//* IMPORTS
/////////////////////////////////////////////
import React from "react";
import { Metadata } from "next";
const datasquirel = require("datasquirel");
import { headers, cookies } from "next/headers";
/////////////////////////////////////////////
//* Metadata
/////////////////////////////////////////////
export const metadata: Metadata = {
title: "Blog posts | Tben.me",
description: "Tech talks and tutorials by Tben",
};
/////////////////////////////////////////////
//* Main Function
/////////////////////////////////////////////
/**
* Blog page index
* ==============================================================================
*/
export default async function BlogIndex() {
//* Data fetching
/////////////////////////////////////////////
const postsResponse = await datasquirel.get({
key: process.env.DATASQUIREL_API_KEY,
db: process.env.DB_NAME,
query: "select title,slug,excerpt,date_created from blog_posts limit 10",
});
const posts = postsResponse?.success ? postsResponse.payload : [];
try {
const test = await fetch("http://localhost:5000/api/test");
const result = await test.json();
console.log(result);
} catch (error: any) {
console.log(error.message);
}
console.log("Referer:", headers().get("referer"));
console.log("Host:", headers().get("host"));
// console.log(nextHeaders.cookies());
//* Main Function Return
/////////////////////////////////////////////
return (
<React.Fragment>
<div className="flex flex-col items-start max-w-6xl w-full">
<h1 className="mb-8">My Blog</h1>
<div className="flex flex-col items-start w-full gap-4">
{posts.map((post: { slug: string; title: string; excerpt: string; date_created: string }, index: number) => (
<a
key={index}
href={`/blog/${post.slug}`}
className="flex flex-col items-start gap-2 w-full hover:bg-blue-600 border border-solid border-white/20 p-8 transition-all bg-primary/10"
>
<h2 className="m-0">{post.title}</h2>
<span className="opacity-80">{post.excerpt}</span>
<span className="text-sm opacity-50">{post.date_created.substring(0, 24)}</span>
</a>
))}
</div>
</div>
</React.Fragment>
);
/** ********************************************** */
}
/////////////////////////////////////////////
//* IMPORTS
/////////////////////////////////////////////
import React from "react";
import { Metadata } from "next";
const datasquirel = require("datasquirel");
import { headers, cookies } from "next/headers";
/////////////////////////////////////////////
//* Metadata
/////////////////////////////////////////////
export const metadata: Metadata = {
title: "Blog posts | Tben.me",
description: "Tech talks and tutorials by Tben",
};
/////////////////////////////////////////////
//* Main Function
/////////////////////////////////////////////
/**
* Blog page index
* ==============================================================================
*/
export default async function BlogIndex() {
//* Data fetching
/////////////////////////////////////////////
const postsResponse = await datasquirel.get({
key: process.env.DATASQUIREL_API_KEY,
db: process.env.DB_NAME,
query: "select title,slug,excerpt,date_created from blog_posts limit 10",
});
const posts = postsResponse?.success ? postsResponse.payload : [];
try {
const test = await fetch("http://localhost:5000/api/test");
const result = await test.json();
console.log(result);
} catch (error: any) {
console.log(error.message);
}
console.log("Referer:", headers().get("referer"));
console.log("Host:", headers().get("host"));
// console.log(nextHeaders.cookies());
//* Main Function Return
/////////////////////////////////////////////
return (
<React.Fragment>
<div className="flex flex-col items-start max-w-6xl w-full">
<h1 className="mb-8">My Blog</h1>
<div className="flex flex-col items-start w-full gap-4">
{posts.map((post: { slug: string; title: string; excerpt: string; date_created: string }, index: number) => (
<a
key={index}
href={`/blog/${post.slug}`}
className="flex flex-col items-start gap-2 w-full hover:bg-blue-600 border border-solid border-white/20 p-8 transition-all bg-primary/10"
>
<h2 className="m-0">{post.title}</h2>
<span className="opacity-80">{post.excerpt}</span>
<span className="text-sm opacity-50">{post.date_created.substring(0, 24)}</span>
</a>
))}
</div>
</div>
</React.Fragment>
);
/** ********************************************** */
}

View File

@ -1,51 +1,51 @@
/** # MODULE TRACE
======================================================================
* No imports found for this Module
==== MODULE TRACE END ==== */
// General imports
import { Metadata } from "next";
import GeneralLayout from "../layouts/general_layout/GeneralLayout";
// Styles imports
import "../styles/main.css";
import "../styles/tw_main.css";
// Metadata
export const metadata: Metadata = {
title: "Homepage",
description: "Software engineer, UI/UX designer, Full Stack Web Developer, Web/graphic/motion designer, React Developer, Next JS developer, Node JS developer, Javascript Developer, Linux Ubuntu, DevOps, Nginx, MySQL developer, Freelancer",
keywords: "UI/UX designer, Full Stack Web Developer, Web/graphic/motion designer, React Developer, NextJS developer, Node JS developer, Javascript Developer, Linux Ubuntu, DevOps, Nginx, MySQL developer, Freelancer",
};
// Main Layout Component
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
></script>
</head>
<body>
<GeneralLayout>{children}</GeneralLayout>
</body>
</html>
);
}
/** # MODULE TRACE
======================================================================
* No imports found for this Module
==== MODULE TRACE END ==== */
// General imports
import { Metadata } from "next";
import GeneralLayout from "../layouts/general_layout/GeneralLayout";
// Styles imports
import "../styles/main.css";
import "../styles/tw_main.css";
// Metadata
export const metadata: Metadata = {
title: "Homepage",
description: "Software engineer, UI/UX designer, Full Stack Web Developer, Web/graphic/motion designer, React Developer, Next JS developer, Node JS developer, Javascript Developer, Linux Ubuntu, DevOps, Nginx, MySQL developer, Freelancer",
keywords: "UI/UX designer, Full Stack Web Developer, Web/graphic/motion designer, React Developer, NextJS developer, Node JS developer, Javascript Developer, Linux Ubuntu, DevOps, Nginx, MySQL developer, Freelancer",
};
// Main Layout Component
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
></script>
</head>
<body>
<GeneralLayout>{children}</GeneralLayout>
</body>
</html>
);
}

View File

@ -1,21 +1,21 @@
import React from "react";
import HomepageComponent from "./(components)/HomepageComponent";
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} />;
}
import React from "react";
import HomepageComponent from "./(components)/HomepageComponent";
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} />;
}

View File

@ -1,54 +1,54 @@
/**
*
*
* Imports
* ------------------------------------------------------------------------------
*
*/
/** ********************* React/Next Imports */
import React from "react";
import TextShuffler from "./actions/TextShuffler";
/** ~ End React/Next Imports *************** */
/** ********************* Functions and Other Page Imports */
/** ~ End Functions and Other Page Imports *************** */
/**
*
*
* Main Component { Functional }
* ------------------------------------------------------------------------------
* @param {Object} props - React component props including { children }
*
*/
/** ********************* Page Main Component */
export default function PortfolioEntry({ title, description, url, image }) {
// ## Get Contexts
// -----------------------
// ## Javascript Variables
// -----------------------
// ## React Hooks { useState, useEffect, useRef, etc ... }
// -----------------------
// ## Function Return
return (
<div className="portfolio-entry-block">
<h2 style={ { marginTop: "0" } }><TextShuffler textInput={ title } /></h2>
<div className="portfolio-image-wrapper">
<img src={ image } alt="" className="portfolio-image" />
</div>
<span>
<TextShuffler textInput={ description } />
</span>
<div className="hero-ctas-section">
<a href={ url } target="_blank">Visit Site</a>
</div>
</div>
);
// -----------------------
};
/** ~ End Page Main Component *************** */
// export default Header;
/**
*
*
* Imports
* ------------------------------------------------------------------------------
*
*/
/** ********************* React/Next Imports */
import React from "react";
import TextShuffler from "./actions/TextShuffler";
/** ~ End React/Next Imports *************** */
/** ********************* Functions and Other Page Imports */
/** ~ End Functions and Other Page Imports *************** */
/**
*
*
* Main Component { Functional }
* ------------------------------------------------------------------------------
* @param {Object} props - React component props including { children }
*
*/
/** ********************* Page Main Component */
export default function PortfolioEntry({ title, description, url, image }) {
// ## Get Contexts
// -----------------------
// ## Javascript Variables
// -----------------------
// ## React Hooks { useState, useEffect, useRef, etc ... }
// -----------------------
// ## Function Return
return (
<div className="portfolio-entry-block">
<h2 style={ { marginTop: "0" } }><TextShuffler textInput={ title } /></h2>
<div className="portfolio-image-wrapper">
<img src={ image } alt="" className="portfolio-image" />
</div>
<span>
<TextShuffler textInput={ description } />
</span>
<div className="hero-ctas-section">
<a href={ url } target="_blank">Visit Site</a>
</div>
</div>
);
// -----------------------
};
/** ~ End Page Main Component *************** */
// export default Header;

View File

@ -1,98 +1,98 @@
"use client";
import React, { FC, useEffect, useState, ReactElement } from "react";
import { gsap } from "gsap";
type ChildProps = {
textInput: string;
delay?: number;
};
const TextShuffler: FC<ChildProps> = ({ textInput, delay }): ReactElement => {
let [readyState, setReadyState] = useState(false);
const spanRef = React.useRef<HTMLElement>(null);
useEffect(() => {
const spanObserver = new IntersectionObserver(
(entries, observer) => {
if (entries[0].isIntersecting) {
if (delay) {
setTimeout(() => {
setReadyState(true);
}, delay);
} else {
setReadyState(true);
}
if (spanRef.current) observer.unobserve(spanRef.current);
}
},
{
rootMargin: "0px 0px 0px 0px",
}
);
if (spanRef.current) spanObserver.observe(spanRef.current);
}, []);
useEffect(() => {
if (!readyState) return;
let chars = textInput.split("");
let charsSpans = chars.map((char) => `<span style="opacity:0">${char}</span>`);
if (spanRef?.current?.innerHTML) spanRef.current.innerHTML = charsSpans.join("");
if (spanRef.current) {
gsap.to(spanRef.current, {
opacity: 1,
duration: 1,
});
}
let textSpans: NodeList | null = spanRef.current ? spanRef.current.querySelectorAll("span") : null;
requestAnimationFrame(() => {
if (textSpans) {
textSpans.forEach((span) => {
gsap.to(span, {
opacity: 1,
duration: Math.random() * 1.5,
});
});
textSpans.forEach((span) => {
gsap.killTweensOf(span, "opacity");
gsap.to(span, {
opacity: 0,
duration: Math.random() * 1.5,
delay: Math.random() * 0.5,
});
});
textSpans.forEach((span) => {
gsap.killTweensOf(span, "opacity");
gsap.to(span, {
opacity: 1,
duration: Math.random() * 1.5,
delay: Math.random(),
});
});
}
});
}, [readyState]);
return (
<span
className="shuffled-text-span"
ref={spanRef}
style={{ opacity: 0 }}
>
{textInput}
</span>
);
};
export default TextShuffler;
"use client";
import React, { FC, useEffect, useState, ReactElement } from "react";
import { gsap } from "gsap";
type ChildProps = {
textInput: string;
delay?: number;
};
const TextShuffler: FC<ChildProps> = ({ textInput, delay }): ReactElement => {
let [readyState, setReadyState] = useState(false);
const spanRef = React.useRef<HTMLElement>(null);
useEffect(() => {
const spanObserver = new IntersectionObserver(
(entries, observer) => {
if (entries[0].isIntersecting) {
if (delay) {
setTimeout(() => {
setReadyState(true);
}, delay);
} else {
setReadyState(true);
}
if (spanRef.current) observer.unobserve(spanRef.current);
}
},
{
rootMargin: "0px 0px 0px 0px",
}
);
if (spanRef.current) spanObserver.observe(spanRef.current);
}, []);
useEffect(() => {
if (!readyState) return;
let chars = textInput.split("");
let charsSpans = chars.map((char) => `<span style="opacity:0">${char}</span>`);
if (spanRef?.current?.innerHTML) spanRef.current.innerHTML = charsSpans.join("");
if (spanRef.current) {
gsap.to(spanRef.current, {
opacity: 1,
duration: 1,
});
}
let textSpans: NodeList | null = spanRef.current ? spanRef.current.querySelectorAll("span") : null;
requestAnimationFrame(() => {
if (textSpans) {
textSpans.forEach((span) => {
gsap.to(span, {
opacity: 1,
duration: Math.random() * 1.5,
});
});
textSpans.forEach((span) => {
gsap.killTweensOf(span, "opacity");
gsap.to(span, {
opacity: 0,
duration: Math.random() * 1.5,
delay: Math.random() * 0.5,
});
});
textSpans.forEach((span) => {
gsap.killTweensOf(span, "opacity");
gsap.to(span, {
opacity: 1,
duration: Math.random() * 1.5,
delay: Math.random(),
});
});
}
});
}, [readyState]);
return (
<span
className="shuffled-text-span"
ref={spanRef}
style={{ opacity: 0 }}
>
{textInput}
</span>
);
};
export default TextShuffler;

View File

@ -1,129 +1,129 @@
/**
* ==============================================================================
* Imports
* ==============================================================================
*/
import React from "react";
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/**
* ==============================================================================
* Main Component { Functional }
* ==============================================================================
* @param {Object} props - Server props
*/
export default function Homepage(props) {
// ## Get Contexts
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## Javascript Variables
/** ********************* Head Items */
let head = (
<React.Fragment>
<title>Showmerebates | Home</title>
<meta name="description" content="Find great property rebates" />
</React.Fragment>
);
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## React Hooks { useState, useEffect, useRef, etc ... }
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## Function Return
return (
<React.Fragment>
<GeneralLayout head={ head } user={ props.user }>
<main>
<Hero />
<HowItWorks />
<FeaturedProperties data={ props.data } user={ props.user } />
<AboutUsSection />
{ !props.user && <LoginPromptPopup user={ props.user } /> }
</main>
</GeneralLayout>
</React.Fragment>
);
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
};
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/**
* ==============================================================================
* Server Side Props or Static Props
* ==============================================================================
* @param {Object} req - http incoming request object
* @param {Object} res - http response object
* @param {Object} query - queries attached to the url
*/
export async function getServerSideProps({ req, res, query }) {
// ## Environment processes
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## User Authentication
const user = await userAuth(req, res);
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## Page/Site Data Data Fetching
let properties = await dbHandler(`
SELECT
ListingKeyNumeric,City,RoomsTotal,BathroomsFull,BathroomsTotalInteger,BedroomsTotal,UnparsedAddress,BuildingAreaTotal,ListPrice,PostalCode
FROM
utahapidata
WHERE
PhotosCount > 0 AND ListPrice > 0 AND BedroomsTotal > 0 LIMIT 3
`);
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## Server Props Return
return {
props: {
user: user,
data: properties,
},
};
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
}
/**
* ==============================================================================
* Imports
* ==============================================================================
*/
import React from "react";
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/**
* ==============================================================================
* Main Component { Functional }
* ==============================================================================
* @param {Object} props - Server props
*/
export default function Homepage(props) {
// ## Get Contexts
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## Javascript Variables
/** ********************* Head Items */
let head = (
<React.Fragment>
<title>Showmerebates | Home</title>
<meta name="description" content="Find great property rebates" />
</React.Fragment>
);
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## React Hooks { useState, useEffect, useRef, etc ... }
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## Function Return
return (
<React.Fragment>
<GeneralLayout head={ head } user={ props.user }>
<main>
<Hero />
<HowItWorks />
<FeaturedProperties data={ props.data } user={ props.user } />
<AboutUsSection />
{ !props.user && <LoginPromptPopup user={ props.user } /> }
</main>
</GeneralLayout>
</React.Fragment>
);
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
};
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/**
* ==============================================================================
* Server Side Props or Static Props
* ==============================================================================
* @param {Object} req - http incoming request object
* @param {Object} res - http response object
* @param {Object} query - queries attached to the url
*/
export async function getServerSideProps({ req, res, query }) {
// ## Environment processes
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## User Authentication
const user = await userAuth(req, res);
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## Page/Site Data Data Fetching
let properties = await dbHandler(`
SELECT
ListingKeyNumeric,City,RoomsTotal,BathroomsFull,BathroomsTotalInteger,BedroomsTotal,UnparsedAddress,BuildingAreaTotal,ListPrice,PostalCode
FROM
utahapidata
WHERE
PhotosCount > 0 AND ListPrice > 0 AND BedroomsTotal > 0 LIMIT 3
`);
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## Server Props Return
return {
props: {
user: user,
data: properties,
},
};
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
}

View File

@ -1,73 +1,73 @@
/**
* ==============================================================================
* Imports
* ==============================================================================
*/
import React from "react";
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/**
* ==============================================================================
* Main Component { Functional }
* ==============================================================================
* @param {Object} props - Server props
*/
export default function SingleBlogPostPreset(props) {
// ## Get Contexts
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## Javascript Variables
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## React Hooks { useState, useEffect, useRef, etc ... }
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## Function Return
return (
<React.Fragment>
<GeneralLayout head={ head } user={ props.user }>
<main>
<Hero />
<HowItWorks />
<FeaturedProperties data={ props.data } user={ props.user } />
<AboutUsSection />
{ !props.user && <LoginPromptPopup user={ props.user } /> }
</main>
</GeneralLayout>
</React.Fragment>
);
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
};
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/**
* ==============================================================================
* Imports
* ==============================================================================
*/
import React from "react";
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/**
* ==============================================================================
* Main Component { Functional }
* ==============================================================================
* @param {Object} props - Server props
*/
export default function SingleBlogPostPreset(props) {
// ## Get Contexts
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## Javascript Variables
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## React Hooks { useState, useEffect, useRef, etc ... }
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## Function Return
return (
<React.Fragment>
<GeneralLayout head={ head } user={ props.user }>
<main>
<Hero />
<HowItWorks />
<FeaturedProperties data={ props.data } user={ props.user } />
<AboutUsSection />
{ !props.user && <LoginPromptPopup user={ props.user } /> }
</main>
</GeneralLayout>
</React.Fragment>
);
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
};
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */

View File

@ -1,6 +1,6 @@
[
{
"title": "",
"id": 1
}
]
[
{
"title": "",
"id": 1
}
]

View File

@ -1,26 +1,26 @@
[
{
"title": "Showme Rebates",
"description": "Showmerebates curates agents' rebates/cashback on properties in Utah, Texas, Florida, and Kansas states",
"url": "https://showmerebates.com/",
"image": "/images/showmerebates.jpg"
},
{
"title": "Summit Lending",
"description": "Summit Lending is a mortgage broker offering competitive rates for home loans of all sorts",
"url": "https://summitlending.com",
"image": "/images/summit-lending.jpg"
},
{
"title": "Datasquirel",
"description": "Datasquirel is a fast and efficient cloud-based SQL data management system",
"url": "https://datasquirel.com",
"image": "/images/datasquirel-img.jpg"
},
{
"title": "Homeruntoken",
"description": "Real Estate Investing Made Easy",
"url": "https://homeruntoken.vercel.app/",
"image": "/images/Homeruntoken-graphic.jpg"
}
]
[
{
"title": "Showme Rebates",
"description": "Showmerebates curates agents' rebates/cashback on properties in Utah, Texas, Florida, and Kansas states",
"url": "https://showmerebates.com/",
"image": "/images/showmerebates.jpg"
},
{
"title": "Summit Lending",
"description": "Summit Lending is a mortgage broker offering competitive rates for home loans of all sorts",
"url": "https://summitlending.com",
"image": "/images/summit-lending.jpg"
},
{
"title": "Datasquirel",
"description": "Datasquirel is a fast and efficient cloud-based SQL data management system",
"url": "https://datasquirel.com",
"image": "/images/datasquirel-img.jpg"
},
{
"title": "Homeruntoken",
"description": "Real Estate Investing Made Easy",
"url": "https://homeruntoken.vercel.app/",
"image": "/images/Homeruntoken-graphic.jpg"
}
]

View File

@ -1,32 +1,32 @@
[
{
"title": "Showme Rebates",
"description": "Property rebates in Utah",
"url": "https://dev.showmerebates.com/",
"image": "/images/showmerebates.jpg"
},
{
"title": "Guaranteed software",
"description": "software development agency",
"url": "https://guaranteed.software/",
"image": "/images/guaranteed.jpg"
},
{
"title": "Renition",
"description": "Curated top quality Apparel",
"url": "https://renition.com",
"image": "/images/renition-graphic.jpg"
},
{
"title": "Castcord",
"description": "Social Media web app",
"url": "https://cast-cord.com",
"image": "/images/castcord-graphic.jpg"
},
{
"title": "Next 7 Web Engine",
"description": "Next 7 is an all-in-one web engine featuring a web page builder and a content management system",
"url": "https://next7.io",
"image": "/images/next-7-graphic-min.jpg"
}
]
[
{
"title": "Showme Rebates",
"description": "Property rebates in Utah",
"url": "https://dev.showmerebates.com/",
"image": "/images/showmerebates.jpg"
},
{
"title": "Guaranteed software",
"description": "software development agency",
"url": "https://guaranteed.software/",
"image": "/images/guaranteed.jpg"
},
{
"title": "Renition",
"description": "Curated top quality Apparel",
"url": "https://renition.com",
"image": "/images/renition-graphic.jpg"
},
{
"title": "Castcord",
"description": "Social Media web app",
"url": "https://cast-cord.com",
"image": "/images/castcord-graphic.jpg"
},
{
"title": "Next 7 Web Engine",
"description": "Next 7 is an all-in-one web engine featuring a web page builder and a content management system",
"url": "https://next7.io",
"image": "/images/next-7-graphic-min.jpg"
}
]

View File

@ -1,135 +1,135 @@
/**
* ==============================================================================
* Imports
* ==============================================================================
*/
const http = require("http");
const https = require("https");
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/**
* ==============================================================================
* Main Function
* ==============================================================================
* @param {Object} mailObject - foundUser if any
*/
/** ********************* Main API Handler */
module.exports = async function httpFetch({ protocol, options, paradigm }) {
if (!protocol) protocol = "http";
/**
* Http(s) Response function
*
* @description handle http and https protocols differently
* @param {object} response => response object
* @param {Function} resolve => promise resolve method
* @param {Function} reject => promise reject method
*/
function httpResponse(response, resolve, reject) {
var str = "";
/** * Append data chunks to "str" variable
*/
response.on("data", function (chunk) {
str += chunk;
});
/** * the whole response has been received, so we just print it out here
*/
response.on("end", function () {
resolve(JSON.parse(str));
});
/** * Handle errors
*/
response.on("error", (err) => {
console.log(err);
reject("Fetch Failed");
});
}
/**
* Switch protocols
*
* @description handle http and https protocols differently
*/
switch (protocol) {
case "http":
/**
* Handle http protocol
*
* @description handles fetch requests with http protocol
* @see => handles strapi api calls only for now
*/
return await new Promise((resolve, reject) => {
http.request(
/** * Make Request
* @abstract Make Request
*/
{
...options,
host: "localhost",
port: "1337",
// path: "/api/blog-posts",
// href: "http://localhost:1337/api/blog-posts",
headers: {
Authorization: `bearer ${process.env.STRAPI_API_KEY_DEV}`,
},
},
/** * Handle Response
* @abstract Handle response
*/
(response) => {
httpResponse(response, resolve, reject);
}
).end();
});
break;
case "https":
/**
* Handle http protocol
*
* @description handles fetch requests with http protocol
* @see => handles strapi api calls only for now
*/
return await new Promise((resolve, reject) => {
https.request(
/** * Make Request
* @abstract Make Request
*/
{
...options,
host: "cms.showmerebates.com",
headers: {
Authorization: `bearer ${process.env.STRAPI_API_KEY}`,
},
},
/** * Handle Response
* @abstract Handle response
*/
(response) => {
httpResponse(response, resolve, reject);
}
).end();
});
break;
default:
break;
}
};
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
/**
* ==============================================================================
* Imports
* ==============================================================================
*/
const http = require("http");
const https = require("https");
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/**
* ==============================================================================
* Main Function
* ==============================================================================
* @param {Object} mailObject - foundUser if any
*/
/** ********************* Main API Handler */
module.exports = async function httpFetch({ protocol, options, paradigm }) {
if (!protocol) protocol = "http";
/**
* Http(s) Response function
*
* @description handle http and https protocols differently
* @param {object} response => response object
* @param {Function} resolve => promise resolve method
* @param {Function} reject => promise reject method
*/
function httpResponse(response, resolve, reject) {
var str = "";
/** * Append data chunks to "str" variable
*/
response.on("data", function (chunk) {
str += chunk;
});
/** * the whole response has been received, so we just print it out here
*/
response.on("end", function () {
resolve(JSON.parse(str));
});
/** * Handle errors
*/
response.on("error", (err) => {
console.log(err);
reject("Fetch Failed");
});
}
/**
* Switch protocols
*
* @description handle http and https protocols differently
*/
switch (protocol) {
case "http":
/**
* Handle http protocol
*
* @description handles fetch requests with http protocol
* @see => handles strapi api calls only for now
*/
return await new Promise((resolve, reject) => {
http.request(
/** * Make Request
* @abstract Make Request
*/
{
...options,
host: "localhost",
port: "1337",
// path: "/api/blog-posts",
// href: "http://localhost:1337/api/blog-posts",
headers: {
Authorization: `bearer ${process.env.STRAPI_API_KEY_DEV}`,
},
},
/** * Handle Response
* @abstract Handle response
*/
(response) => {
httpResponse(response, resolve, reject);
}
).end();
});
break;
case "https":
/**
* Handle http protocol
*
* @description handles fetch requests with http protocol
* @see => handles strapi api calls only for now
*/
return await new Promise((resolve, reject) => {
https.request(
/** * Make Request
* @abstract Make Request
*/
{
...options,
host: "cms.showmerebates.com",
headers: {
Authorization: `bearer ${process.env.STRAPI_API_KEY}`,
},
},
/** * Handle Response
* @abstract Handle response
*/
(response) => {
httpResponse(response, resolve, reject);
}
).end();
});
break;
default:
break;
}
};
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */

View File

@ -1,10 +1,10 @@
const sanitizeHtmlOptions: object = {
allowedTags: ["b", "i", "em", "strong", "a", "p", "span", "ul", "ol", "li", "h1", "h2", "h3", "h4", "h5", "h6", "img", "div", "button", "pre", "code", "br"],
allowedAttributes: {
a: ["href"],
img: ["src", "alt", "width", "height", "class", "style"],
"*": ["style", "class"],
},
};
export default sanitizeHtmlOptions;
const sanitizeHtmlOptions: object = {
allowedTags: ["b", "i", "em", "strong", "a", "p", "span", "ul", "ol", "li", "h1", "h2", "h3", "h4", "h5", "h6", "img", "div", "button", "pre", "code", "br"],
allowedAttributes: {
a: ["href"],
img: ["src", "alt", "width", "height", "class", "style"],
"*": ["style", "class"],
},
};
export default sanitizeHtmlOptions;

View File

@ -1,40 +1,40 @@
import { Dispatch, SetStateAction } from "react";
export default async function submitContactForm(e: any, setSuccess: Dispatch<SetStateAction<string | null>>, setLoading: Dispatch<SetStateAction<boolean>>) {
e.preventDefault();
setLoading(true);
let name = e.target[0].value;
let email = e.target[1].value;
let message = e.target[2].value;
let body = {
name: name,
email: email,
message: message,
};
let res = await fetch("/api/contactForm", {
method: "post",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
let data = await res.json();
console.log(data);
if (data.msg === "Success") {
setSuccess("Success");
setTimeout(() => {
window.location.reload();
}, 1000);
} else {
setSuccess("Failed");
}
setLoading(false);
}
import { Dispatch, SetStateAction } from "react";
export default async function submitContactForm(e: any, setSuccess: Dispatch<SetStateAction<string | null>>, setLoading: Dispatch<SetStateAction<boolean>>) {
e.preventDefault();
setLoading(true);
let name = e.target[0].value;
let email = e.target[1].value;
let message = e.target[2].value;
let body = {
name: name,
email: email,
message: message,
};
let res = await fetch("/api/contactForm", {
method: "post",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
let data = await res.json();
console.log(data);
if (data.msg === "Success") {
setSuccess("Success");
setTimeout(() => {
window.location.reload();
}, 1000);
} else {
setSuccess("Failed");
}
setLoading(false);
}

View File

@ -1,13 +1,13 @@
export const textSHuffle = (text, dispatch) => {
let shuffleTimer = 0;
let startText = text;
let shuffledtext
setInterval(()=>{
shuffleTimer++
shuffledtext = startText + "whatever";
}, 200);
return shuffledtext
export const textSHuffle = (text, dispatch) => {
let shuffleTimer = 0;
let startText = text;
let shuffledtext
setInterval(()=>{
shuffleTimer++
shuffledtext = startText + "whatever";
}, 200);
return shuffledtext
}

View File

@ -1,132 +1,132 @@
import * as THREE from "three";
export default function threeJsAnimations() {
const animationWrapper = document.getElementById("homepage-animation-wrapper");
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, animationWrapper.clientWidth / animationWrapper.clientHeight, 0.1, 1000);
const light = new THREE.PointLight(0xff0000, 1, 200);
light.position.set(-1, -1, 5);
const ambientLight = new THREE.AmbientLight("#adb2d3");
const pointLight = new THREE.PointLight(0xffffff, 1, 100);
pointLight.position.set(10, 10, 10);
const sphereSize = 1;
const pointLightHelper = new THREE.PointLightHelper(pointLight, sphereSize);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(animationWrapper.clientWidth, animationWrapper.clientHeight);
animationWrapper.appendChild(renderer.domElement);
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshPhongMaterial({
// light
// specular: "rgb(30, 33, 68)",
// intermediate
color: "rgb(30, 33, 68)",
// dark
// emissive: "rgb(30, 33, 68)",
shininess: 50,
wireframe: true,
// map: THREE.ImageUtils.loadTexture("http://i.imgur.com/xCE2Br4.jpg?1"),
});
const cube = new THREE.Mesh(geometry, material);
const material2 = new THREE.MeshPhongMaterial({
// light
// specular: "rgb(30, 33, 68)",
// intermediate
color: "#1668e4",
// dark
// emissive: "rgb(30, 33, 68)",
shininess: 50,
wireframe: false,
// map: THREE.ImageUtils.loadTexture("http://i.imgur.com/xCE2Br4.jpg?1"),
});
const cube2 = new THREE.Mesh(geometry, material2);
cube2.position.x = window.innerWidth > 500 ? 1.5 : -0.5;
cube2.position.y = window.innerWidth > 500 ? 1.5 : 1.8;
cube2.position.z = -1;
cube2.scale.x = 0.3;
cube2.scale.y = 0.3;
cube2.scale.z = 0.3;
const material3 = new THREE.MeshPhongMaterial({
// light
// specular: "rgb(30, 33, 68)",
// intermediate
color: "rgb(53, 65, 194)",
// dark
// emissive: "rgb(30, 33, 68)",
shininess: 50,
wireframe: false,
// map: THREE.ImageUtils.loadTexture("http://i.imgur.com/xCE2Br4.jpg?1"),
});
const cube3 = new THREE.Mesh(geometry, material3);
scene.add(cube);
scene.add(cube2);
scene.add(cube3);
scene.add(ambientLight);
scene.add(light);
scene.add(pointLight);
scene.add(pointLightHelper);
cube3.position.x = -1.5;
cube3.position.y = -1.5;
cube3.position.z = -1;
cube3.scale.x = 0.2;
cube3.scale.y = 0.2;
cube3.scale.z = 0.2;
camera.position.z = 2;
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
cube.rotation.x += 0.002;
cube.rotation.y += 0.002;
cube2.rotation.x += 0.005;
cube2.rotation.y += 0.005;
cube3.rotation.x += 0.01;
cube3.rotation.y += 0.01;
scene.rotation.z += 0.0003;
}
animate();
window.addEventListener("mousemove", (e) => {
let relMouseX = e.x - window.innerWidth / 2;
let relMouseY = e.y - window.innerHeight / 2;
// console.log("X pos: ", relMouseX);
// console.log("Y pos: ", relMouseY);
// console.log(e.y);
relMouseX < 0 ? (cube.rotation.x += 0.003) : (cube.rotation.x += 0.003);
relMouseY > 0 ? (cube.rotation.y += 0.003) : (cube.rotation.y += 0.003);
relMouseX < 0 ? (cube2.position.x += 0.001) : (cube2.position.x -= 0.001);
relMouseY > 0 ? (cube2.position.y += 0.001) : (cube2.position.y -= 0.001);
relMouseX < 0 ? (cube3.position.x += 0.0007) : (cube3.position.x -= 0.0007);
relMouseY > 0 ? (cube3.position.y += 0.0007) : (cube3.position.y -= 0.0007);
});
window.addEventListener("resize", (e) => {
renderer.setSize(animationWrapper.clientWidth, window.innerHeight);
});
window.addEventListener("scroll", (e) => {
if (window.scrollY > 100) {
scene.remove(cube2);
scene.remove(cube3);
} else {
scene.add(cube2);
scene.add(cube3);
}
});
}
import * as THREE from "three";
export default function threeJsAnimations() {
const animationWrapper = document.getElementById("homepage-animation-wrapper");
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, animationWrapper.clientWidth / animationWrapper.clientHeight, 0.1, 1000);
const light = new THREE.PointLight(0xff0000, 1, 200);
light.position.set(-1, -1, 5);
const ambientLight = new THREE.AmbientLight("#adb2d3");
const pointLight = new THREE.PointLight(0xffffff, 1, 100);
pointLight.position.set(10, 10, 10);
const sphereSize = 1;
const pointLightHelper = new THREE.PointLightHelper(pointLight, sphereSize);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(animationWrapper.clientWidth, animationWrapper.clientHeight);
animationWrapper.appendChild(renderer.domElement);
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshPhongMaterial({
// light
// specular: "rgb(30, 33, 68)",
// intermediate
color: "rgb(30, 33, 68)",
// dark
// emissive: "rgb(30, 33, 68)",
shininess: 50,
wireframe: true,
// map: THREE.ImageUtils.loadTexture("http://i.imgur.com/xCE2Br4.jpg?1"),
});
const cube = new THREE.Mesh(geometry, material);
const material2 = new THREE.MeshPhongMaterial({
// light
// specular: "rgb(30, 33, 68)",
// intermediate
color: "#1668e4",
// dark
// emissive: "rgb(30, 33, 68)",
shininess: 50,
wireframe: false,
// map: THREE.ImageUtils.loadTexture("http://i.imgur.com/xCE2Br4.jpg?1"),
});
const cube2 = new THREE.Mesh(geometry, material2);
cube2.position.x = window.innerWidth > 500 ? 1.5 : -0.5;
cube2.position.y = window.innerWidth > 500 ? 1.5 : 1.8;
cube2.position.z = -1;
cube2.scale.x = 0.3;
cube2.scale.y = 0.3;
cube2.scale.z = 0.3;
const material3 = new THREE.MeshPhongMaterial({
// light
// specular: "rgb(30, 33, 68)",
// intermediate
color: "rgb(53, 65, 194)",
// dark
// emissive: "rgb(30, 33, 68)",
shininess: 50,
wireframe: false,
// map: THREE.ImageUtils.loadTexture("http://i.imgur.com/xCE2Br4.jpg?1"),
});
const cube3 = new THREE.Mesh(geometry, material3);
scene.add(cube);
scene.add(cube2);
scene.add(cube3);
scene.add(ambientLight);
scene.add(light);
scene.add(pointLight);
scene.add(pointLightHelper);
cube3.position.x = -1.5;
cube3.position.y = -1.5;
cube3.position.z = -1;
cube3.scale.x = 0.2;
cube3.scale.y = 0.2;
cube3.scale.z = 0.2;
camera.position.z = 2;
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
cube.rotation.x += 0.002;
cube.rotation.y += 0.002;
cube2.rotation.x += 0.005;
cube2.rotation.y += 0.005;
cube3.rotation.x += 0.01;
cube3.rotation.y += 0.01;
scene.rotation.z += 0.0003;
}
animate();
window.addEventListener("mousemove", (e) => {
let relMouseX = e.x - window.innerWidth / 2;
let relMouseY = e.y - window.innerHeight / 2;
// console.log("X pos: ", relMouseX);
// console.log("Y pos: ", relMouseY);
// console.log(e.y);
relMouseX < 0 ? (cube.rotation.x += 0.003) : (cube.rotation.x += 0.003);
relMouseY > 0 ? (cube.rotation.y += 0.003) : (cube.rotation.y += 0.003);
relMouseX < 0 ? (cube2.position.x += 0.001) : (cube2.position.x -= 0.001);
relMouseY > 0 ? (cube2.position.y += 0.001) : (cube2.position.y -= 0.001);
relMouseX < 0 ? (cube3.position.x += 0.0007) : (cube3.position.x -= 0.0007);
relMouseY > 0 ? (cube3.position.y += 0.0007) : (cube3.position.y -= 0.0007);
});
window.addEventListener("resize", (e) => {
renderer.setSize(animationWrapper.clientWidth, window.innerHeight);
});
window.addEventListener("scroll", (e) => {
if (window.scrollY > 100) {
scene.remove(cube2);
scene.remove(cube3);
} else {
scene.add(cube2);
scene.add(cube3);
}
});
}

View File

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1766.7 2003.6" style="enable-background:new 0 0 1766.7 2003.6;" xml:space="preserve">
<style type="text/css">
.st0{opacity:0.1;fill:#1730C6;enable-background:new ;}
.st1{fill:#FFFFFF;}
</style>
<g id="Layer_2_1_">
<g id="Layer_1-2">
<path class="st0" d="M1468,1688.2l137.5-1540.8H883.3v1702.9L1468,1688.2z"/>
<path class="st1" d="M0,0l160.8,1803.3l721.5,200.3l0,0l723.5-200.6l161-1803L0,0z M1396.7,816.3L1396.7,816.3l-5.4,59.4
l-52,582.3l-3.3,37.4l-452.6,125.4l0,0l-1,0.3l-453.1-125.7l-31-347.3h222l15.8,176.4l246.4,66.5h0.2l0,0l246.7-66.6l25.7-286.9
H388.4l-19.8-221.2h805.5l20.1-226.5H348.9l-20.1-221.2h1107.9L1396.7,816.3z"/>
</g>
</g>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 1766.7 2003.6" style="enable-background:new 0 0 1766.7 2003.6;" xml:space="preserve">
<style type="text/css">
.st0{opacity:0.1;fill:#1730C6;enable-background:new ;}
.st1{fill:#FFFFFF;}
</style>
<g id="Layer_2_1_">
<g id="Layer_1-2">
<path class="st0" d="M1468,1688.2l137.5-1540.8H883.3v1702.9L1468,1688.2z"/>
<path class="st1" d="M0,0l160.8,1803.3l721.5,200.3l0,0l723.5-200.6l161-1803L0,0z M1396.7,816.3L1396.7,816.3l-5.4,59.4
l-52,582.3l-3.3,37.4l-452.6,125.4l0,0l-1,0.3l-453.1-125.7l-31-347.3h222l15.8,176.4l246.4,66.5h0.2l0,0l246.7-66.6l25.7-286.9
H388.4l-19.8-221.2h805.5l20.1-226.5H348.9l-20.1-221.2h1107.9L1396.7,816.3z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 938 B

After

Width:  |  Height:  |  Size: 955 B

View File

@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<style type="text/css">
.st0{fill:#1C3766;}
</style>
<g>
<path class="st0" d="M28.6,13.4l-11,9.4c-0.7,0.6-1.7,0.9-2.6,0.9c-0.9,0-1.8-0.3-2.6-0.9l-11-9.4C-0.3,12-0.5,9.4,1,7.7
C2.4,6,5,5.8,6.6,7.2l8.4,7.2l8.4-7.2C25.1,5.8,27.6,6,29,7.7C30.5,9.4,30.3,12,28.6,13.4z"/>
</g>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<style type="text/css">
.st0{fill:#1C3766;}
</style>
<g>
<path class="st0" d="M28.6,13.4l-11,9.4c-0.7,0.6-1.7,0.9-2.6,0.9c-0.9,0-1.8-0.3-2.6-0.9l-11-9.4C-0.3,12-0.5,9.4,1,7.7
C2.4,6,5,5.8,6.6,7.2l8.4,7.2l8.4-7.2C25.1,5.8,27.6,6,29,7.7C30.5,9.4,30.3,12,28.6,13.4z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 626 B

After

Width:  |  Height:  |  Size: 638 B

View File

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 433.6 491.7" style="enable-background:new 0 0 433.6 491.7;" xml:space="preserve">
<style type="text/css">
.st0{opacity:0.1;fill:#2236C6;enable-background:new ;}
.st1{fill:#FFFFFF;}
</style>
<g id="Layer_2_1_">
<g id="Layer_1-2">
<polygon class="st0" points="216.8,454.1 360.3,414.3 394,36.2 216.8,36.2 "/>
<path class="st1" d="M0,0l39.5,442.6l177.1,49.2l177.6-49.2L433.6,0L0,0z M351.3,105l-2.5,27.5l-1.1,12.2H140l5,55.6h197.8
l-1.3,14.6l-12.8,142.9l-0.8,9.2l-111.1,30.8l0,0l-0.2,0.1L105.3,367l-7.6-85.2h54.5l3.9,43.3l60.4,16.3l0,0l0,0l60.5-16.3
l6.3-70.4H95.3L82,105l-1.3-14.6h271.9L351.3,105z"/>
</g>
</g>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 433.6 491.7" style="enable-background:new 0 0 433.6 491.7;" xml:space="preserve">
<style type="text/css">
.st0{opacity:0.1;fill:#2236C6;enable-background:new ;}
.st1{fill:#FFFFFF;}
</style>
<g id="Layer_2_1_">
<g id="Layer_1-2">
<polygon class="st0" points="216.8,454.1 360.3,414.3 394,36.2 216.8,36.2 "/>
<path class="st1" d="M0,0l39.5,442.6l177.1,49.2l177.6-49.2L433.6,0L0,0z M351.3,105l-2.5,27.5l-1.1,12.2H140l5,55.6h197.8
l-1.3,14.6l-12.8,142.9l-0.8,9.2l-111.1,30.8l0,0l-0.2,0.1L105.3,367l-7.6-85.2h54.5l3.9,43.3l60.4,16.3l0,0l0,0l60.5-16.3
l6.3-70.4H95.3L82,105l-1.3-14.6h271.9L351.3,105z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 908 B

After

Width:  |  Height:  |  Size: 925 B

View File

@ -1,17 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 310 310" style="enable-background:new 0 0 310 310;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g id="XMLID_801_">
<path id="XMLID_802_" class="st0" d="M72.2,99.7H9.9c-2.8,0-5,2.2-5,5v199.9c0,2.8,2.2,5,5,5h62.2c2.8,0,5-2.2,5-5V104.7
C77.2,102,74.9,99.7,72.2,99.7z"/>
<path id="XMLID_803_" class="st0" d="M41.1,0.3C18.4,0.3,0,18.7,0,41.4c0,22.6,18.4,41,41.1,41c22.6,0,41-18.4,41-41
C82.1,18.7,63.7,0.3,41.1,0.3z"/>
<path id="XMLID_804_" class="st0" d="M230.5,94.8c-25,0-43.5,10.7-54.7,23v-13c0-2.8-2.2-5-5-5h-59.6c-2.8,0-5,2.2-5,5v199.9
c0,2.8,2.2,5,5,5h62.1c2.8,0,5-2.2,5-5v-98.9c0-33.3,9.1-46.3,32.3-46.3c25.3,0,27.3,20.8,27.3,48v97.2c0,2.8,2.2,5,5,5H305
c2.8,0,5-2.2,5-5V195C310,145.4,300.5,94.8,230.5,94.8z"/>
</g>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 310 310" style="enable-background:new 0 0 310 310;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g id="XMLID_801_">
<path id="XMLID_802_" class="st0" d="M72.2,99.7H9.9c-2.8,0-5,2.2-5,5v199.9c0,2.8,2.2,5,5,5h62.2c2.8,0,5-2.2,5-5V104.7
C77.2,102,74.9,99.7,72.2,99.7z"/>
<path id="XMLID_803_" class="st0" d="M41.1,0.3C18.4,0.3,0,18.7,0,41.4c0,22.6,18.4,41,41.1,41c22.6,0,41-18.4,41-41
C82.1,18.7,63.7,0.3,41.1,0.3z"/>
<path id="XMLID_804_" class="st0" d="M230.5,94.8c-25,0-43.5,10.7-54.7,23v-13c0-2.8-2.2-5-5-5h-59.6c-2.8,0-5,2.2-5,5v199.9
c0,2.8,2.2,5,5,5h62.1c2.8,0,5-2.2,5-5v-98.9c0-33.3,9.1-46.3,32.3-46.3c25.3,0,27.3,20.8,27.3,48v97.2c0,2.8,2.2,5,5,5H305
c2.8,0,5-2.2,5-5V195C310,145.4,300.5,94.8,230.5,94.8z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g>
<path class="st0" d="M28.6,13.4l-11,9.4c-0.7,0.6-1.7,0.9-2.6,0.9c-0.9,0-1.8-0.3-2.6-0.9l-11-9.4C-0.3,12-0.5,9.4,1,7.7
C2.4,6,5,5.8,6.6,7.2l8.4,7.2l8.4-7.2C25.1,5.8,27.6,6,29,7.7C30.5,9.4,30.3,12,28.6,13.4z"/>
</g>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.3.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" style="enable-background:new 0 0 30 30;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g>
<path class="st0" d="M28.6,13.4l-11,9.4c-0.7,0.6-1.7,0.9-2.6,0.9c-0.9,0-1.8-0.3-2.6-0.9l-11-9.4C-0.3,12-0.5,9.4,1,7.7
C2.4,6,5,5.8,6.6,7.2l8.4,7.2l8.4-7.2C25.1,5.8,27.6,6,29,7.7C30.5,9.4,30.3,12,28.6,13.4z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 626 B

After

Width:  |  Height:  |  Size: 638 B

View File

@ -1,394 +1,394 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- ...................................................................................................................... Head -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Tben Web Designer</title>
<meta name="description" content="Web/Graphic/Motion Designer, UI UX Designer, Frontend Web Developer">
<link rel="shortcut icon" href="https://benjamintoby.github.io/personal_site/images/favicon4.ico"
type="image/x-icon">
<link href="https://fonts.googleapis.com/css?family=Titillium+Web:400,500,600,700,800|Material+Icons"
rel="stylesheet">
<link rel="stylesheet" href="main.css">
</head>
<body>
<!-- #################################################################################-- Preloader -->
<div class="preloader-init" id="preloader"></div>
<!-- #################################################################################-- Header -->
<header>
<a href="#">
<img src="images/logo-v3.svg" alt="">
<div>Tben.<br>Design</div>
</a>
<div>
<nav>
<a href="#my-work" id="my-work-link" class="nav-link">My Work</a>
<a href="#about-me" id="about-me-link" class="nav-link">About Me</a>
<a href="#my-specialties" id="resume-link" class="nav-link footer-special">Resume</a>
<a href="#contact-me" id="contact-me-link" class="nav-link footer-special">Contact Me</a>
</nav>
</div>
<div style="display: flex;align-items: center;">
<a href="mailto:benoti.san@gmail.com" class="social-media-links material-icons" id="mail">mail</a>
<a href="https://linkedin.com/in/benjamin-toby" class="social-media-links material-icons" id="linkedin"
target="_blank"><img src="images/linkedin.svg" alt=""></a>
</div>
<button class="hamburger-button">
<div></div>
<div></div>
<div></div>
</button>
</header>
<div id="header-controller"></div>
<!-- #################################################################################-- Preloader -->
<div id="preloader-container">
<img src="images/clouds.jpg" alt="">
<img src="images/logo-v3.svg" alt="">
</div>
<!-- #################################################################################-- Hero -->
<section class="hero-section">
<div></div>
<div>
<div class="main-text-block">
Hi. Im a
<h1 class="main-hero-text">
<a href="#" class="uiux-designer-link">UI UX Designer</a>,
<a href="#" class="web-designer-link">Web Designer</a>,
<a href="#" class="frontend-designer-link">Frontend Web Developer</a>,
<a href="#" class="graphic-motion-designer-link">Graphic and motion Designer</a>.
</h1> <br>
<button class="main-cta-button"><a href="#contact-me">Let's Talk</a></button>
</div>
<!-- <div class="hero-scroll-down-block">
<a href="#my-work">
<div>&lsaquo;</div>
</a>
<img src="images/rotating-text.svg" alt="" class="rotating-text-image">
</div> -->
<div class="hero-text-section-graphic">
<a href="#my-work">
<img src="images/down-arrow.svg" alt="">
</a>
<div></div>
<div></div>
<div></div>
</div>
</div>
<img src="images/clouds.jpg" alt="" id="clouds">
<img src="images/mountains.png" alt="" id="mountains">
<div class="benjamin-image-block-wrapper">
<div class="benjamin-image-block">
<div class="benjamin-image-block-overlay"></div>
</div>
<div class="service-display-block">
</div>
</div>
<div class="benjamin-big-text-block">Benjamin<br>Toby</div>
</section>
<!-- #################################################################################-- My Work -->
<section class="my-work-section" id="my-work">
<div>
<h2 class="scroll-into-view-content" data-delay="200">Some of My Work</h2>
<p class="scroll-into-view-content" data-delay="400">Here are a few of my works: you can find more of my
work on my <a href="https://99designs.com/profiles/2220588" target="_blank">99designs</a> profile.</p>
<div class="portfolio-buttons-container">
<button class="portfolio-left-button"><img src="images/white-arrow.svg" alt=""></button>
<button class="portfolio-right-button"><img src="images/white-arrow.svg" alt=""></button>
</div>
</div>
<div class="scroll-into-view">
<div class="portfolio-items-container scroll-into-view-content" data-delay="800">
<!-- Porfolio entry -->
<div class="portfolio-entry" id="transcend-barriers">
<!-- <img src="images/my-image-large.jpg" alt=""> -->
<div>
<h3>Transcend Barriers Animation</h3>
<p>This animation was made for web view. I used aftereffects and bodymoving to accomplish this:
the result? High definition svg anmimation that looks crisp on any resolution and maintains
a relatively light weight</p>
</div>
<div>
<div class="specialty-skill">
<div>Adobe Illustrator</div><img src="images/illustrator.svg" alt="">
</div>
<div class="specialty-skill">
<div>Adobe After Effects</div><img src="images/aftereffects.svg" alt="">
</div>
<div class="specialty-skill">
<div>javascript</div><img src="images/javascript.svg" alt="">
</div>
</div>
</div>
<!-- Porfolio entry -->
<div class="portfolio-entry" id="github-to-asana">
<!-- <img src="images/mountains.png" alt=""> -->
<div>
<h3>Github to Asana</h3>
<p>This project is an ongoing webapp that connects github to asana. In this project I'm focusing
on the UI/UX, together with frontend functionality.</p>
</div>
<div>
<div class="specialty-skill">
<div>Adobe Photoshop</div><img src="images/photoshop.svg" alt="">
</div>
<div class="specialty-skill">
<div>Adobe Illustrator</div><img src="images/illustrator.svg" alt="">
</div>
<div class="specialty-skill">
<div>Adobe After Effects</div><img src="images/aftereffects.svg" alt="">
</div>
<div class="specialty-skill">
<div>HTML5</div><img src="images/html5.svg" alt="">
</div>
<div class="specialty-skill">
<div>CSS3</div><img src="images/css3.svg" alt="">
</div>
<div class="specialty-skill">
<div>javascript</div><img src="images/javascript.svg" alt="">
</div>
<div class="specialty-skill">
<div>Git version control</div><img src="images/git-version-control.svg" alt="">
</div>
</div>
<img src="images/Project_GitToAsana_LandingPage_002.jpg" alt="">
<div id="github-to-asana-animation"></div>
</div>
</div>
</div>
</section>
<!-- #################################################################################-- About Me -->
<section class="about-me-section" id="about-me">
<div class="scroll-into-view">
<img src="images/my-image-large.jpg" alt="">
</div>
<div>
<h2 class="scroll-into-view-content">About Me</h2>
<p class="scroll-into-view-content">I've traversed multiple fields over the past decade: from engineering
studies, to solar power constructions, to web design, graphic design, 2d animations, and code. Over the
years I've learnt the best skill: which is learnability and adaptability: hence I aim to cut across many
different fields: the world changes fast, and I think this is the only skill that matters.</p>
</div>
</section>
<!-- #################################################################################-- My Specialties -->
<section class="my-specialties-section" id="my-specialties">
<h2 class="scroll-into-view-content">My Tools For Creating Awesomeness</h2>
<p class="scroll-into-view-content">Ever expanding and ever improving, I have proffesional grade understanding
of the tools below. But I don't plan to stop here.</p>
<div>
<div class="specialty-skill scroll-into-view-content" data-delay="0">
<div>Adobe Photoshop</div><img src="images/photoshop.svg" alt="">
</div>
<div class="specialty-skill scroll-into-view-content" data-delay="100">
<div>Adobe Illustrator</div><img src="images/illustrator.svg" alt="">
</div>
<div class="specialty-skill scroll-into-view-content" data-delay="200">
<div>Adobe After Effects</div><img src="images/aftereffects.svg" alt="">
</div>
<div class="specialty-skill scroll-into-view-content" data-delay="300">
<div>HTML 5</div><img src="images/html5.svg" alt="">
</div>
<div class="specialty-skill scroll-into-view-content" data-delay="400">
<div>CSS3</div><img src="images/css3.svg" alt="">
</div>
<div class="specialty-skill scroll-into-view-content" data-delay="500">
<div>javascript</div><img src="images/javascript.svg" alt="">
</div>
<div class="specialty-skill scroll-into-view-content" data-delay="600">
<div>Git Version Control</div><img src="images/git-version-control.svg" alt="">
</div>
<div class="specialty-skill scroll-into-view-content" data-delay="700">
<div>Figma</div><img src="images/figma.svg" alt="">
</div>
<div class="specialty-skill scroll-into-view-content" data-delay="800">
<div>Wordpress</div><img src="images/wordpress.svg" alt="">
</div>
<div class="specialty-skill scroll-into-view-content-2" data-delay="900">
<div>Webflow</div><img src="images/webflow.svg" alt="">
</div>
<div class="specialty-skill scroll-into-view-content-2" data-delay="1000">
<div>Adobe XD</div><img src="images/adobe-xd.svg" alt="">
</div>
<div class="specialty-skill scroll-into-view-content-2" data-delay="1100">
<div>Visual Studio Code</div><img src="images/vs-code.svg" alt="">
</div>
<div class="specialty-skill scroll-into-view-content-2" data-delay="1200">
<div>Dreamweaver</div><img src="images/dreamweaver.svg" alt="">
</div>
<div class="specialty-skill scroll-into-view-content-2" data-delay="1300">
<div>Adobe Animate</div><img src="images/adobe animate.svg" alt="">
</div>
<div class="specialty-skill scroll-into-view-content-2" data-delay="1400">
<div>Affinity Photo and Affinity Designer</div><img src="images/affinity.svg" alt="">
</div>
</div>
<button class="scroll-into-view-content-2" data-delay="500">Download My Resume</button>
</section>
<!-- #################################################################################-- Contact Me -->
<section class="contact-section" id="contact-me">
<div>
<h2 class="scroll-into-view-content">So. Let's talk</h2>
<p class="scroll-into-view-content">Your business is my business as soon as you hit that contact button. I'm
ready
to attend to your needs as soon as possible. Just drop a line.</p>
<a href="mailto:benoti.san@gmail.com" class="scroll-into-view-content-2" data-delay="500">
Email Address&nbsp;<span class="material-icons">mail</span>
</a>
<div class="scroll-into-view-content-2" data-delay="700">
<div>Copyright 2021 Tben.Design</div>
</div>
</div>
<div class="scroll-into-view" id="footer-right-section">
<div class="scroll-into-view-content" data-delay="800">
<form id="contact-form" class="main-cta-form" method="post" action="contact-form-process.php"
autocomplete="on">
<input type="text" required placeholder="Enter Full Name">
<input type="email" required placeholder="Enter Email Address">
<textarea name="Message" rows="3" maxlength="3000" required placeholder="Enter Message"></textarea>
<button type="submit">Send Message</button>
</form>
</div>
</div>
<div class="contact-section-controller"></div>
<div class="footer-bar">
</div>
</section>
<!-- #################################################################################-- Scripts -->
<script src="scripts/anime.min.js"></script>
<script src="scripts/lottie.min.js"></script>
<script src="scripts/main.js"></script>
</body>
<!DOCTYPE html>
<html lang="en">
<head>
<!-- ...................................................................................................................... Head -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Tben Web Designer</title>
<meta name="description" content="Web/Graphic/Motion Designer, UI UX Designer, Frontend Web Developer">
<link rel="shortcut icon" href="https://benjamintoby.github.io/personal_site/images/favicon4.ico"
type="image/x-icon">
<link href="https://fonts.googleapis.com/css?family=Titillium+Web:400,500,600,700,800|Material+Icons"
rel="stylesheet">
<link rel="stylesheet" href="main.css">
</head>
<body>
<!-- #################################################################################-- Preloader -->
<div class="preloader-init" id="preloader"></div>
<!-- #################################################################################-- Header -->
<header>
<a href="#">
<img src="images/logo-v3.svg" alt="">
<div>Tben.<br>Design</div>
</a>
<div>
<nav>
<a href="#my-work" id="my-work-link" class="nav-link">My Work</a>
<a href="#about-me" id="about-me-link" class="nav-link">About Me</a>
<a href="#my-specialties" id="resume-link" class="nav-link footer-special">Resume</a>
<a href="#contact-me" id="contact-me-link" class="nav-link footer-special">Contact Me</a>
</nav>
</div>
<div style="display: flex;align-items: center;">
<a href="mailto:benoti.san@gmail.com" class="social-media-links material-icons" id="mail">mail</a>
<a href="https://linkedin.com/in/benjamin-toby" class="social-media-links material-icons" id="linkedin"
target="_blank"><img src="images/linkedin.svg" alt=""></a>
</div>
<button class="hamburger-button">
<div></div>
<div></div>
<div></div>
</button>
</header>
<div id="header-controller"></div>
<!-- #################################################################################-- Preloader -->
<div id="preloader-container">
<img src="images/clouds.jpg" alt="">
<img src="images/logo-v3.svg" alt="">
</div>
<!-- #################################################################################-- Hero -->
<section class="hero-section">
<div></div>
<div>
<div class="main-text-block">
Hi. Im a
<h1 class="main-hero-text">
<a href="#" class="uiux-designer-link">UI UX Designer</a>,
<a href="#" class="web-designer-link">Web Designer</a>,
<a href="#" class="frontend-designer-link">Frontend Web Developer</a>,
<a href="#" class="graphic-motion-designer-link">Graphic and motion Designer</a>.
</h1> <br>
<button class="main-cta-button"><a href="#contact-me">Let's Talk</a></button>
</div>
<!-- <div class="hero-scroll-down-block">
<a href="#my-work">
<div>&lsaquo;</div>
</a>
<img src="images/rotating-text.svg" alt="" class="rotating-text-image">
</div> -->
<div class="hero-text-section-graphic">
<a href="#my-work">
<img src="images/down-arrow.svg" alt="">
</a>
<div></div>
<div></div>
<div></div>
</div>
</div>
<img src="images/clouds.jpg" alt="" id="clouds">
<img src="images/mountains.png" alt="" id="mountains">
<div class="benjamin-image-block-wrapper">
<div class="benjamin-image-block">
<div class="benjamin-image-block-overlay"></div>
</div>
<div class="service-display-block">
</div>
</div>
<div class="benjamin-big-text-block">Benjamin<br>Toby</div>
</section>
<!-- #################################################################################-- My Work -->
<section class="my-work-section" id="my-work">
<div>
<h2 class="scroll-into-view-content" data-delay="200">Some of My Work</h2>
<p class="scroll-into-view-content" data-delay="400">Here are a few of my works: you can find more of my
work on my <a href="https://99designs.com/profiles/2220588" target="_blank">99designs</a> profile.</p>
<div class="portfolio-buttons-container">
<button class="portfolio-left-button"><img src="images/white-arrow.svg" alt=""></button>
<button class="portfolio-right-button"><img src="images/white-arrow.svg" alt=""></button>
</div>
</div>
<div class="scroll-into-view">
<div class="portfolio-items-container scroll-into-view-content" data-delay="800">
<!-- Porfolio entry -->
<div class="portfolio-entry" id="transcend-barriers">
<!-- <img src="images/my-image-large.jpg" alt=""> -->
<div>
<h3>Transcend Barriers Animation</h3>
<p>This animation was made for web view. I used aftereffects and bodymoving to accomplish this:
the result? High definition svg anmimation that looks crisp on any resolution and maintains
a relatively light weight</p>
</div>
<div>
<div class="specialty-skill">
<div>Adobe Illustrator</div><img src="images/illustrator.svg" alt="">
</div>
<div class="specialty-skill">
<div>Adobe After Effects</div><img src="images/aftereffects.svg" alt="">
</div>
<div class="specialty-skill">
<div>javascript</div><img src="images/javascript.svg" alt="">
</div>
</div>
</div>
<!-- Porfolio entry -->
<div class="portfolio-entry" id="github-to-asana">
<!-- <img src="images/mountains.png" alt=""> -->
<div>
<h3>Github to Asana</h3>
<p>This project is an ongoing webapp that connects github to asana. In this project I'm focusing
on the UI/UX, together with frontend functionality.</p>
</div>
<div>
<div class="specialty-skill">
<div>Adobe Photoshop</div><img src="images/photoshop.svg" alt="">
</div>
<div class="specialty-skill">
<div>Adobe Illustrator</div><img src="images/illustrator.svg" alt="">
</div>
<div class="specialty-skill">
<div>Adobe After Effects</div><img src="images/aftereffects.svg" alt="">
</div>
<div class="specialty-skill">
<div>HTML5</div><img src="images/html5.svg" alt="">
</div>
<div class="specialty-skill">
<div>CSS3</div><img src="images/css3.svg" alt="">
</div>
<div class="specialty-skill">
<div>javascript</div><img src="images/javascript.svg" alt="">
</div>
<div class="specialty-skill">
<div>Git version control</div><img src="images/git-version-control.svg" alt="">
</div>
</div>
<img src="images/Project_GitToAsana_LandingPage_002.jpg" alt="">
<div id="github-to-asana-animation"></div>
</div>
</div>
</div>
</section>
<!-- #################################################################################-- About Me -->
<section class="about-me-section" id="about-me">
<div class="scroll-into-view">
<img src="images/my-image-large.jpg" alt="">
</div>
<div>
<h2 class="scroll-into-view-content">About Me</h2>
<p class="scroll-into-view-content">I've traversed multiple fields over the past decade: from engineering
studies, to solar power constructions, to web design, graphic design, 2d animations, and code. Over the
years I've learnt the best skill: which is learnability and adaptability: hence I aim to cut across many
different fields: the world changes fast, and I think this is the only skill that matters.</p>
</div>
</section>
<!-- #################################################################################-- My Specialties -->
<section class="my-specialties-section" id="my-specialties">
<h2 class="scroll-into-view-content">My Tools For Creating Awesomeness</h2>
<p class="scroll-into-view-content">Ever expanding and ever improving, I have proffesional grade understanding
of the tools below. But I don't plan to stop here.</p>
<div>
<div class="specialty-skill scroll-into-view-content" data-delay="0">
<div>Adobe Photoshop</div><img src="images/photoshop.svg" alt="">
</div>
<div class="specialty-skill scroll-into-view-content" data-delay="100">
<div>Adobe Illustrator</div><img src="images/illustrator.svg" alt="">
</div>
<div class="specialty-skill scroll-into-view-content" data-delay="200">
<div>Adobe After Effects</div><img src="images/aftereffects.svg" alt="">
</div>
<div class="specialty-skill scroll-into-view-content" data-delay="300">
<div>HTML 5</div><img src="images/html5.svg" alt="">
</div>
<div class="specialty-skill scroll-into-view-content" data-delay="400">
<div>CSS3</div><img src="images/css3.svg" alt="">
</div>
<div class="specialty-skill scroll-into-view-content" data-delay="500">
<div>javascript</div><img src="images/javascript.svg" alt="">
</div>
<div class="specialty-skill scroll-into-view-content" data-delay="600">
<div>Git Version Control</div><img src="images/git-version-control.svg" alt="">
</div>
<div class="specialty-skill scroll-into-view-content" data-delay="700">
<div>Figma</div><img src="images/figma.svg" alt="">
</div>
<div class="specialty-skill scroll-into-view-content" data-delay="800">
<div>Wordpress</div><img src="images/wordpress.svg" alt="">
</div>
<div class="specialty-skill scroll-into-view-content-2" data-delay="900">
<div>Webflow</div><img src="images/webflow.svg" alt="">
</div>
<div class="specialty-skill scroll-into-view-content-2" data-delay="1000">
<div>Adobe XD</div><img src="images/adobe-xd.svg" alt="">
</div>
<div class="specialty-skill scroll-into-view-content-2" data-delay="1100">
<div>Visual Studio Code</div><img src="images/vs-code.svg" alt="">
</div>
<div class="specialty-skill scroll-into-view-content-2" data-delay="1200">
<div>Dreamweaver</div><img src="images/dreamweaver.svg" alt="">
</div>
<div class="specialty-skill scroll-into-view-content-2" data-delay="1300">
<div>Adobe Animate</div><img src="images/adobe animate.svg" alt="">
</div>
<div class="specialty-skill scroll-into-view-content-2" data-delay="1400">
<div>Affinity Photo and Affinity Designer</div><img src="images/affinity.svg" alt="">
</div>
</div>
<button class="scroll-into-view-content-2" data-delay="500">Download My Resume</button>
</section>
<!-- #################################################################################-- Contact Me -->
<section class="contact-section" id="contact-me">
<div>
<h2 class="scroll-into-view-content">So. Let's talk</h2>
<p class="scroll-into-view-content">Your business is my business as soon as you hit that contact button. I'm
ready
to attend to your needs as soon as possible. Just drop a line.</p>
<a href="mailto:benoti.san@gmail.com" class="scroll-into-view-content-2" data-delay="500">
Email Address&nbsp;<span class="material-icons">mail</span>
</a>
<div class="scroll-into-view-content-2" data-delay="700">
<div>Copyright 2021 Tben.Design</div>
</div>
</div>
<div class="scroll-into-view" id="footer-right-section">
<div class="scroll-into-view-content" data-delay="800">
<form id="contact-form" class="main-cta-form" method="post" action="contact-form-process.php"
autocomplete="on">
<input type="text" required placeholder="Enter Full Name">
<input type="email" required placeholder="Enter Email Address">
<textarea name="Message" rows="3" maxlength="3000" required placeholder="Enter Message"></textarea>
<button type="submit">Send Message</button>
</form>
</div>
</div>
<div class="contact-section-controller"></div>
<div class="footer-bar">
</div>
</section>
<!-- #################################################################################-- Scripts -->
<script src="scripts/anime.min.js"></script>
<script src="scripts/lottie.min.js"></script>
<script src="scripts/main.js"></script>
</body>
</html>

View File

@ -1,79 +1,79 @@
[
{
"id": 1,
"title": "Choosing your stack",
"slug": "choosing-your-tech-stack",
"description": "So many technologies, so little time to vet all",
"date": "Fri Jun 10 2022 06:34:08 GMT+0100 (West Africa Standard Time)",
"body": [
{
"tag": "p",
"content": "The tech pool is an ever growing trojan horse. JavaScript libraries alone are getting out of hand: we seem to be getting a new one every 6 months. Hell even I have a javascript library of my own. Trying to pick from this pool can easily turn into mission impossible: understandably so: there's just too many of them"
},
{
"tag": "p",
"content": "The thing is, each of these libraries and frameworks end up doing essentially the same thing in thier respective categories: react creates reuseable javascript components: same with vue, same with angular, svelte. And for the most part, you won't be using every single feature provided by these libraries: just the ones that suit your project."
},
{
"tag": "p",
"content": "In truth, the tech stack you choose doesn't mean much: you can get 10 different options which achieve the same goal. Really, all that matters is the developer you pick: because a masterful developer can create great products using any stack of his/her choosing."
},
{
"tag": "p",
"children": [
{
"tag": "span",
"content": "If you're just starting off, perhaps the best step is to evaluate the stacks and get a recommendation from an expert in the field: you may be surprised to find out you don't need as many technologies as you think: you may even be surprised that using a traditional framework like ruby on rails is actually a lot easier than wordpress. "
},
{
"tag": "a",
"href": "/contact",
"class": "text-blue-300",
"content": "Reach out"
},
{
"tag": "span",
"content": " to find out more."
}
]
}
]
},
{
"id": 2,
"title": "Find your perfect framework",
"slug": "find-your-perfect-framework",
"description": "How much can a web framework affect your project?",
"date": "Fri Jun 10 2022 06:34:08 GMT+0100 (West Africa Standard Time)",
"body": [
{
"tag": "p",
"content": "Web frameworks are a great way to get the best out of multiple technologies while keeping your development time short and reliability high. But just like it is with every aspect of web development, there's a dilemma of \"Which framework should I go with\"."
},
{
"tag": "p",
"content": "Different frameworks come with different structures and different selections of languages and packages. In truth, you don't necessarily need a framework: nearly all major programming languages can handle applications on thier own: but as you build more projects, you keep encountering more repetitions, and you end up abstracting those repetitions into reuseable components: you now have a framework of your own. Now this isn't bad at all: infact, this is the end goal of a truly performant app: frameworks often come with a lot of packages you may not need: which end up bugging down your application: if you can develop a framework of your own, you can eliminate this downside: but, you have to be prepared to spend a lot of time on your project: if you have the time, then go for it: else, go for a framework."
},
{
"tag": "p",
"content": "Haven said this, there are lots of frameworks you can choose from: depending on your time and budget: here are some great picks you should consider:"
},
{
"tag": "h2",
"content": "1. Next JS"
},
{
"tag": "img",
"src": "https://miro.medium.com/max/1400/1*htbUdWgFQ3a94PMEvBr_hQ.png",
"class": "w-full",
"style": {
"border": "1px solid #ffffff40"
}
},
{
"tag": "p",
"content": "Next JS is slowly becoming the household name for web development frameworks: it features an immensely powerful and efficient structure, based on React JS: which enables server side rendering of pages: as opposed to the traditional SPA(single page application) model React was created for. Next JS handles the heavilifting of routing, apis, frontend and backend components, module bundling, and linting, leaving you with a relatively easy platform to integrate your project. Next JS is growing very quick and in no time, it will become the most used framework for enterprise applications"
}
]
}
]
[
{
"id": 1,
"title": "Choosing your stack",
"slug": "choosing-your-tech-stack",
"description": "So many technologies, so little time to vet all",
"date": "Fri Jun 10 2022 06:34:08 GMT+0100 (West Africa Standard Time)",
"body": [
{
"tag": "p",
"content": "The tech pool is an ever growing trojan horse. JavaScript libraries alone are getting out of hand: we seem to be getting a new one every 6 months. Hell even I have a javascript library of my own. Trying to pick from this pool can easily turn into mission impossible: understandably so: there's just too many of them"
},
{
"tag": "p",
"content": "The thing is, each of these libraries and frameworks end up doing essentially the same thing in thier respective categories: react creates reuseable javascript components: same with vue, same with angular, svelte. And for the most part, you won't be using every single feature provided by these libraries: just the ones that suit your project."
},
{
"tag": "p",
"content": "In truth, the tech stack you choose doesn't mean much: you can get 10 different options which achieve the same goal. Really, all that matters is the developer you pick: because a masterful developer can create great products using any stack of his/her choosing."
},
{
"tag": "p",
"children": [
{
"tag": "span",
"content": "If you're just starting off, perhaps the best step is to evaluate the stacks and get a recommendation from an expert in the field: you may be surprised to find out you don't need as many technologies as you think: you may even be surprised that using a traditional framework like ruby on rails is actually a lot easier than wordpress. "
},
{
"tag": "a",
"href": "/contact",
"class": "text-blue-300",
"content": "Reach out"
},
{
"tag": "span",
"content": " to find out more."
}
]
}
]
},
{
"id": 2,
"title": "Find your perfect framework",
"slug": "find-your-perfect-framework",
"description": "How much can a web framework affect your project?",
"date": "Fri Jun 10 2022 06:34:08 GMT+0100 (West Africa Standard Time)",
"body": [
{
"tag": "p",
"content": "Web frameworks are a great way to get the best out of multiple technologies while keeping your development time short and reliability high. But just like it is with every aspect of web development, there's a dilemma of \"Which framework should I go with\"."
},
{
"tag": "p",
"content": "Different frameworks come with different structures and different selections of languages and packages. In truth, you don't necessarily need a framework: nearly all major programming languages can handle applications on thier own: but as you build more projects, you keep encountering more repetitions, and you end up abstracting those repetitions into reuseable components: you now have a framework of your own. Now this isn't bad at all: infact, this is the end goal of a truly performant app: frameworks often come with a lot of packages you may not need: which end up bugging down your application: if you can develop a framework of your own, you can eliminate this downside: but, you have to be prepared to spend a lot of time on your project: if you have the time, then go for it: else, go for a framework."
},
{
"tag": "p",
"content": "Haven said this, there are lots of frameworks you can choose from: depending on your time and budget: here are some great picks you should consider:"
},
{
"tag": "h2",
"content": "1. Next JS"
},
{
"tag": "img",
"src": "https://miro.medium.com/max/1400/1*htbUdWgFQ3a94PMEvBr_hQ.png",
"class": "w-full",
"style": {
"border": "1px solid #ffffff40"
}
},
{
"tag": "p",
"content": "Next JS is slowly becoming the household name for web development frameworks: it features an immensely powerful and efficient structure, based on React JS: which enables server side rendering of pages: as opposed to the traditional SPA(single page application) model React was created for. Next JS handles the heavilifting of routing, apis, frontend and backend components, module bundling, and linting, leaving you with a relatively easy platform to integrate your project. Next JS is growing very quick and in no time, it will become the most used framework for enterprise applications"
}
]
}
]

View File

@ -1,46 +1,46 @@
"use client";
import React from "react";
import { gsap } from "gsap";
// import scrollTrigger from "gsap/ScrollTrigger";
const BG = () => {
React.useEffect(() => {
// gsap.registerPlugin(scrollTrigger);
gsap.to("#bg-blurred-image", {
scrollTrigger: {
scrub: 5,
},
y: 50,
scale: 0.8,
});
gsap.to("#bg-image", {
scrollTrigger: {
scrub: 2,
},
y: 50,
opacity: 0,
});
}, []);
return (
<React.Fragment>
<img
src="/images/rm378-07c-min.png"
alt="bg-image"
className="fixed top-0 left-0 w-full h-full object-cover z-[-2] p-[100px] opacity-20 blur-sm"
id="bg-blurred-image"
/>
<img
src="/images/rm378-07c-min.png"
alt="bg-image"
className="fixed top-0 left-0 w-full h-full object-cover z-[-1] p-[150px] opacity-5"
id="bg-image"
/>
</React.Fragment>
);
};
export default BG;
"use client";
import React from "react";
import { gsap } from "gsap";
// import scrollTrigger from "gsap/ScrollTrigger";
const BG = () => {
React.useEffect(() => {
// gsap.registerPlugin(scrollTrigger);
gsap.to("#bg-blurred-image", {
scrollTrigger: {
scrub: 5,
},
y: 50,
scale: 0.8,
});
gsap.to("#bg-image", {
scrollTrigger: {
scrub: 2,
},
y: 50,
opacity: 0,
});
}, []);
return (
<React.Fragment>
<img
src="/images/rm378-07c-min.png"
alt="bg-image"
className="fixed top-0 left-0 w-full h-full object-cover z-[-2] p-[100px] opacity-20 blur-sm"
id="bg-blurred-image"
/>
<img
src="/images/rm378-07c-min.png"
alt="bg-image"
className="fixed top-0 left-0 w-full h-full object-cover z-[-1] p-[150px] opacity-5"
id="bg-image"
/>
</React.Fragment>
);
};
export default BG;

View File

@ -1,53 +1,53 @@
import React from "react";
import Image from "next/image";
const GeneralFooter = () => {
const date = new Date();
return (
<footer
className="mt-10 w-full flex flex-col items-center py-20"
style={{
borderTop: "1px solid rgba(255,255,255,0.2)",
}}
>
<div className="max-w-6xl w-full flex flex-wrap items-stretch gap-10">
<a
href="/"
data-href="/"
className="logo-link-block -mb-4"
>
<Image
src="/images/logo-white.svg"
width={50}
height={100}
alt="Logo"
/>
</a>
<div className="flex flex-col items-start justify-between">
<div className="flex-col flex items-start gap-1">
<span className="opacity-50">Contact Me</span>
<div className="flex gap-4">
<a
href="https://www.linkedin.com/in/benjamin-toby/"
target="_blank"
>
LinkedIn
</a>
<a
href="https://github.com/BenjaminToby"
target="_blank"
>
Github
</a>
<a href="mailto:benoti.san@gmail.com">Mail</a>
<a href="tel:+2348123682346">Phone</a>
</div>
</div>
<span className="text-sm opacity-40">Copyright © {date.getFullYear()} Tben.me. All Rights Reserved.</span>
</div>
</div>
</footer>
);
};
export default GeneralFooter;
import React from "react";
import Image from "next/image";
const GeneralFooter = () => {
const date = new Date();
return (
<footer
className="mt-10 w-full flex flex-col items-center py-20"
style={{
borderTop: "1px solid rgba(255,255,255,0.2)",
}}
>
<div className="max-w-6xl w-full flex flex-wrap items-stretch gap-10">
<a
href="/"
data-href="/"
className="logo-link-block -mb-4"
>
<Image
src="/images/logo-white.svg"
width={50}
height={100}
alt="Logo"
/>
</a>
<div className="flex flex-col items-start justify-between">
<div className="flex-col flex items-start gap-1">
<span className="opacity-50">Contact Me</span>
<div className="flex gap-4">
<a
href="https://www.linkedin.com/in/benjamin-toby/"
target="_blank"
>
LinkedIn
</a>
<a
href="https://github.com/BenjaminToby"
target="_blank"
>
Github
</a>
<a href="mailto:benoti.san@gmail.com">Mail</a>
<a href="tel:+2348123682346">Phone</a>
</div>
</div>
<span className="text-sm opacity-40">Copyright © {date.getFullYear()} Tben.me. All Rights Reserved.</span>
</div>
</div>
</footer>
);
};
export default GeneralFooter;

View File

@ -1,101 +1,101 @@
"use client";
import React from "react";
import Image from "next/image";
import { gsap } from "gsap";
import HeaderNav from "./HeaderNav";
/**
* General Header for all pages
*/
const GeneralHeader = (): React.ReactElement => {
const links: { title: string; url: string }[] = require("./links.json");
const [mobileNavOpen, setMobileNavOpen] = React.useState<boolean>(false);
/**
* Animate the header on mount
*/
React.useEffect(() => {
gsap.fromTo(
"#main-header",
{
y: -20,
filter: "blur(100px)",
webkitFilter: "blur(100px)",
opacity: 0,
},
{
y: 0,
opacity: 1,
duration: 1,
filter: "none",
webkitFilter: "none",
delay: 0.5,
}
);
}, []);
React.useEffect(() => {
if (mobileNavOpen) {
gsap.to("#mobile-nave-drawer", {
opacity: 1,
pointerEvents: "all",
duration: 0.5,
});
} else {
gsap.to("#mobile-nave-drawer", {
opacity: 0,
pointerEvents: "none",
duration: 0.5,
});
}
}, [mobileNavOpen]);
return (
<header
id="main-header"
className="flex items-start justify-between gap-10 w-full py-4 md:py-10 z-10 fixed xl:sticky top-0"
>
<a
href="/"
data-href="/"
className="logo-link-block scale-90 sm:scale-100"
>
<Image
src="/images/logo-white.svg"
width={50}
height={100}
alt="Logo"
/>
</a>
<HeaderNav />
<button
className="p-2 w-14 h-14 rounded-full flex flex-col items-center justify-center gap-2 hover:bg-[white]/90 xl:hidden fixed top-[35px] right-10 z-10 -rotate-45"
onClick={(e) => {
e.preventDefault();
setMobileNavOpen(!mobileNavOpen);
}}
>
<div className="w-8 h-1 rounded-full bg-[black]"></div>
<div className="w-8 h-1 rounded-full bg-[black]"></div>
</button>
<div
id="mobile-nave-drawer"
className="flex xl:hidden flex-col fixed top-0 right-0 w-screen max-w-[400px] h-screen overflow-auto bg-[black] p-8"
style={{
opacity: 0,
pointerEvents: "none",
}}
>
<HeaderNav mobile={true} />
</div>
</header>
);
};
export default GeneralHeader;
"use client";
import React from "react";
import Image from "next/image";
import { gsap } from "gsap";
import HeaderNav from "./HeaderNav";
/**
* General Header for all pages
*/
const GeneralHeader = (): React.ReactElement => {
const links: { title: string; url: string }[] = require("./links.json");
const [mobileNavOpen, setMobileNavOpen] = React.useState<boolean>(false);
/**
* Animate the header on mount
*/
React.useEffect(() => {
gsap.fromTo(
"#main-header",
{
y: -20,
filter: "blur(100px)",
webkitFilter: "blur(100px)",
opacity: 0,
},
{
y: 0,
opacity: 1,
duration: 1,
filter: "none",
webkitFilter: "none",
delay: 0.5,
}
);
}, []);
React.useEffect(() => {
if (mobileNavOpen) {
gsap.to("#mobile-nave-drawer", {
opacity: 1,
pointerEvents: "all",
duration: 0.5,
});
} else {
gsap.to("#mobile-nave-drawer", {
opacity: 0,
pointerEvents: "none",
duration: 0.5,
});
}
}, [mobileNavOpen]);
return (
<header
id="main-header"
className="flex items-start justify-between gap-10 w-full py-4 md:py-10 z-10 fixed xl:sticky top-0"
>
<a
href="/"
data-href="/"
className="logo-link-block scale-90 sm:scale-100"
>
<Image
src="/images/logo-white.svg"
width={50}
height={100}
alt="Logo"
/>
</a>
<HeaderNav />
<button
className="p-2 w-14 h-14 rounded-full flex flex-col items-center justify-center gap-2 hover:bg-[white]/90 xl:hidden fixed top-[35px] right-10 z-10 -rotate-45"
onClick={(e) => {
e.preventDefault();
setMobileNavOpen(!mobileNavOpen);
}}
>
<div className="w-8 h-1 rounded-full bg-[black]"></div>
<div className="w-8 h-1 rounded-full bg-[black]"></div>
</button>
<div
id="mobile-nave-drawer"
className="flex xl:hidden flex-col fixed top-0 right-0 w-screen max-w-[400px] h-screen overflow-auto bg-[black] p-8"
style={{
opacity: 0,
pointerEvents: "none",
}}
>
<HeaderNav mobile={true} />
</div>
</header>
);
};
export default GeneralHeader;

View File

@ -1,62 +1,62 @@
"use client";
import React, { ReactElement } from "react";
import GeneralHeader from "./GeneralHeader";
import GeneralFooter from "./GeneralFooter";
import { gsap } from "gsap";
import BG from "./BG";
export const SiteContext: any = React.createContext({});
type GeneralLayoutProps = {
children: React.ReactNode;
};
const GeneralLayout = ({ children }: GeneralLayoutProps): ReactElement => {
const [readyState, setReadyState] = React.useState(false);
React.useEffect(() => {
const links: NodeListOf<HTMLAnchorElement> | null = document.querySelectorAll("nav a");
links?.forEach((link) => {
if (link.dataset.href === window.location.pathname) {
link.classList.add("active-page");
}
if (window.location.pathname.match(new RegExp(`${link.dataset.href}\\/.*`))) {
link.classList.add("active-page");
}
});
gsap.to("#main-content-wrapper", {
opacity: 1,
duration: 2,
});
}, []);
return (
<SiteContext.Provider value={{ readyState, setReadyState }}>
<div
id="main-content-wrapper"
style={{
opacity: 0,
}}
className="px-4 md:px-10"
>
<GeneralHeader />
<main
className="flex items-center flex-col w-full"
// style={{
// perspective: "800px",
// }}
>
{children}
</main>
<GeneralFooter />
</div>
<BG />
</SiteContext.Provider>
);
};
export default GeneralLayout;
"use client";
import React, { ReactElement } from "react";
import GeneralHeader from "./GeneralHeader";
import GeneralFooter from "./GeneralFooter";
import { gsap } from "gsap";
import BG from "./BG";
export const SiteContext: any = React.createContext({});
type GeneralLayoutProps = {
children: React.ReactNode;
};
const GeneralLayout = ({ children }: GeneralLayoutProps): ReactElement => {
const [readyState, setReadyState] = React.useState(false);
React.useEffect(() => {
const links: NodeListOf<HTMLAnchorElement> | null = document.querySelectorAll("nav a");
links?.forEach((link) => {
if (link.dataset.href === window.location.pathname) {
link.classList.add("active-page");
}
if (window.location.pathname.match(new RegExp(`${link.dataset.href}\\/.*`))) {
link.classList.add("active-page");
}
});
gsap.to("#main-content-wrapper", {
opacity: 1,
duration: 2,
});
}, []);
return (
<SiteContext.Provider value={{ readyState, setReadyState }}>
<div
id="main-content-wrapper"
style={{
opacity: 0,
}}
className="px-4 md:px-10"
>
<GeneralHeader />
<main
className="flex items-center flex-col w-full"
// style={{
// perspective: "800px",
// }}
>
{children}
</main>
<GeneralFooter />
</div>
<BG />
</SiteContext.Provider>
);
};
export default GeneralLayout;

View File

@ -1,60 +1,60 @@
"use client";
import React from "react";
import { gsap } from "gsap";
/**
* General Header for all pages
*/
const HeaderNav = ({ mobile }: { mobile?: boolean }): React.ReactElement => {
const links: { title: string; url: string }[] = require("./links.json");
return (
<nav className={"items-start xl:items-center gap-x-6 gap-y-2 xl:flex-row" + (mobile ? " flex-col" : " hidden xl:flex")}>
{links.map((link: { title: string; url: string; download?: boolean }, index) => {
return (
<a
key={index}
href={link.url}
data-href={link.url}
className="text-lg"
download={link.download}
>
{link.title}
</a>
);
})}
<span className="opacity-20">|</span>
<div className="flex items-start xl:items-center gap-4 flex-col xl:flex-row">
<a
href="https://github.com/BenjaminToby"
target="_blank"
>
<img
src="/images/github-white.png"
alt="Benjamin Toby Github"
width={20}
height={20}
className="flex items-center"
/>
</a>
<a
href="https://www.linkedin.com/in/benjamin-toby/"
target="_blank"
>
<img
src="/images/linkedin-white.png"
alt="Benjamin Toby Github"
width={20}
height={20}
className="flex items-center"
/>
</a>
</div>
</nav>
);
};
export default HeaderNav;
"use client";
import React from "react";
import { gsap } from "gsap";
/**
* General Header for all pages
*/
const HeaderNav = ({ mobile }: { mobile?: boolean }): React.ReactElement => {
const links: { title: string; url: string }[] = require("./links.json");
return (
<nav className={"items-start xl:items-center gap-x-6 gap-y-2 xl:flex-row" + (mobile ? " flex-col" : " hidden xl:flex")}>
{links.map((link: { title: string; url: string; download?: boolean }, index) => {
return (
<a
key={index}
href={link.url}
data-href={link.url}
className="text-lg"
download={link.download}
>
{link.title}
</a>
);
})}
<span className="opacity-20">|</span>
<div className="flex items-start xl:items-center gap-4 flex-col xl:flex-row">
<a
href="https://github.com/BenjaminToby"
target="_blank"
>
<img
src="/images/github-white.png"
alt="Benjamin Toby Github"
width={20}
height={20}
className="flex items-center"
/>
</a>
<a
href="https://www.linkedin.com/in/benjamin-toby/"
target="_blank"
>
<img
src="/images/linkedin-white.png"
alt="Benjamin Toby Github"
width={20}
height={20}
className="flex items-center"
/>
</a>
</div>
</nav>
);
};
export default HeaderNav;

View File

@ -1,27 +1,27 @@
[
{
"title": "Home",
"url": "/"
},
{
"title": "About Me",
"url": "/about"
},
{
"title": "My Work",
"url": "/work"
},
{
"title": "My Resume",
"url": "/documents/Resume-Benjamin-Toby-Linkedin.pdf",
"download": true
},
{
"title": "Blog",
"url": "/blog"
},
{
"title": " Contact Me",
"url": "/contact"
}
]
[
{
"title": "Home",
"url": "/"
},
{
"title": "About Me",
"url": "/about"
},
{
"title": "My Work",
"url": "/work"
},
{
"title": "My Resume",
"url": "/documents/Resume-Benjamin-Toby-Linkedin.pdf",
"download": true
},
{
"title": "Blog",
"url": "/blog"
},
{
"title": " Contact Me",
"url": "/contact"
}
]

12
next-env.d.ts vendored
View File

@ -1,6 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference types="next/navigation-types/compat/navigation" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference types="next/navigation-types/compat/navigation" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

View File

@ -1,15 +1,15 @@
import "../styles/main.css";
import "../styles/tw_main.css";
import { Fragment } from "react";
import type { AppProps } from "next/app";
function MyApp({ Component, pageProps }: AppProps) {
return (
<Fragment>
<Component {...pageProps} />
</Fragment>
);
}
export default MyApp;
import "../styles/main.css";
import "../styles/tw_main.css";
import { Fragment } from "react";
import type { AppProps } from "next/app";
function MyApp({ Component, pageProps }: AppProps) {
return (
<Fragment>
<Component {...pageProps} />
</Fragment>
);
}
export default MyApp;

View File

@ -1,19 +1,19 @@
import React from "react";
import { Html, Head, Main, NextScript } from "next/document";
export default function Document() {
return (
<Html>
<Head>
<script
src="/scripts/main.js"
defer
></script>
</Head>
<body className="w-full">
<Main />
<NextScript />
</body>
</Html>
);
}
import React from "react";
import { Html, Head, Main, NextScript } from "next/document";
export default function Document() {
return (
<Html>
<Head>
<script
src="/scripts/main.js"
defer
></script>
</Head>
<body className="w-full">
<Main />
<NextScript />
</body>
</Html>
);
}

View File

@ -1,49 +1,49 @@
/**
* Imports
*/
const fs = require("fs");
const sanitizeHtml = require("sanitize-html");
const nodemailer = require("nodemailer");
import sanitizeHtmlOptions from "../../functions/backend/sanitizeHtmlOptions";
import { NextApiRequest, NextApiResponse } from "next";
let transporter = nodemailer.createTransport({
host: process.env.TBENMAIL_HOST,
port: 587,
auth: {
user: process.env.TBENMAIL_EMAIL,
pass: process.env.TBENMAIL_EMAIL_PASSWORD,
},
});
/**
* API handler
*
*/
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method === "POST") {
let name = req.body.name;
let email = req.body.email;
let message = req.body.message;
const html = `<h1>Message from ${name} | ${email}</h1><h4>Name:</h4><p>${name}</p><h4>Email:</h4><p>${email}</p><h4>Message:</h4><p>${message}</p>`;
const sanitizedHtml = sanitizeHtml(html, sanitizeHtmlOptions);
try {
let info = await transporter.sendMail({
from: `"Tben.me" <${process.env.TBENMAIL_EMAIL}>`,
to: "benoti.san@gmail.com, benoti.sanchez@gmail.com",
subject: "Tben.me | Client Message",
text: "Hello from tben",
html: sanitizedHtml,
});
console.log("Message sent: %s", info.messageId);
res.json({ msg: "Success", info: info });
} catch (error) {
console.log(error);
res.json({ msg: "Failed" });
}
}
}
/**
* Imports
*/
const fs = require("fs");
const sanitizeHtml = require("sanitize-html");
const nodemailer = require("nodemailer");
import sanitizeHtmlOptions from "../../functions/backend/sanitizeHtmlOptions";
import { NextApiRequest, NextApiResponse } from "next";
let transporter = nodemailer.createTransport({
host: process.env.TBENMAIL_HOST,
port: 587,
auth: {
user: process.env.TBENMAIL_EMAIL,
pass: process.env.TBENMAIL_EMAIL_PASSWORD,
},
});
/**
* API handler
*
*/
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method === "POST") {
let name = req.body.name;
let email = req.body.email;
let message = req.body.message;
const html = `<h1>Message from ${name} | ${email}</h1><h4>Name:</h4><p>${name}</p><h4>Email:</h4><p>${email}</p><h4>Message:</h4><p>${message}</p>`;
const sanitizedHtml = sanitizeHtml(html, sanitizeHtmlOptions);
try {
let info = await transporter.sendMail({
from: `"Tben.me" <${process.env.TBENMAIL_EMAIL}>`,
to: "benoti.san@gmail.com, benoti.sanchez@gmail.com",
subject: "Tben.me | Client Message",
text: "Hello from tben",
html: sanitizedHtml,
});
console.log("Message sent: %s", info.messageId);
res.json({ msg: "Success", info: info });
} catch (error) {
console.log(error);
res.json({ msg: "Failed" });
}
}
}

View File

@ -1,45 +1,45 @@
import { NextApiHandler, NextApiRequest, NextApiResponse } from "next";
import Cors from "cors";
const cors = Cors({
methods: ["GET"],
origin: "*",
});
// Helper method to wait for a middleware to execute before continuing
// And to throw an error when an error happens in a middleware
function runMiddleware(req: NextApiRequest, res: NextApiResponse, fn: Function) {
return new Promise((resolve, reject) => {
fn(req, res, (result: any) => {
if (result instanceof Error) {
return reject(result);
}
return resolve(result);
});
});
}
/**
* @type {NextApiHandler}
*/
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
// res.setHeader("Access-Control-Allow-Credentials", "true");
// res.setHeader("Access-Control-Allow-Origin", "*");
// res.setHeader("Access-Control-Allow-Methods", "*");
// res.setHeader("Access-Control-Allow-Headers", "*");
res.setHeader("Access-Control-Allow-Credentials", "true");
res.setHeader("Access-Control-Allow-Origin", "*");
// another common pattern
// res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
res.setHeader("Access-Control-Allow-Methods", "GET,OPTIONS,PATCH,DELETE,POST,PUT");
res.setHeader("Access-Control-Allow-Headers", "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version");
// await runMiddleware(req, res, cors);
res.json({
title: "Hello There",
message: "General Kenobi",
});
}
import { NextApiHandler, NextApiRequest, NextApiResponse } from "next";
import Cors from "cors";
const cors = Cors({
methods: ["GET"],
origin: "*",
});
// Helper method to wait for a middleware to execute before continuing
// And to throw an error when an error happens in a middleware
function runMiddleware(req: NextApiRequest, res: NextApiResponse, fn: Function) {
return new Promise((resolve, reject) => {
fn(req, res, (result: any) => {
if (result instanceof Error) {
return reject(result);
}
return resolve(result);
});
});
}
/**
* @type {NextApiHandler}
*/
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
// res.setHeader("Access-Control-Allow-Credentials", "true");
// res.setHeader("Access-Control-Allow-Origin", "*");
// res.setHeader("Access-Control-Allow-Methods", "*");
// res.setHeader("Access-Control-Allow-Headers", "*");
res.setHeader("Access-Control-Allow-Credentials", "true");
res.setHeader("Access-Control-Allow-Origin", "*");
// another common pattern
// res.setHeader('Access-Control-Allow-Origin', req.headers.origin);
res.setHeader("Access-Control-Allow-Methods", "GET,OPTIONS,PATCH,DELETE,POST,PUT");
res.setHeader("Access-Control-Allow-Headers", "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version");
// await runMiddleware(req, res, cors);
res.json({
title: "Hello There",
message: "General Kenobi",
});
}

View File

@ -1,178 +1,178 @@
/**
* ==============================================================================
* Imports
* ==============================================================================
*/
import React from "react";
import Head from "next/head";
import type { InferGetStaticPropsType, GetStaticProps, GetStaticPaths } from "next";
const datasquirel = require("datasquirel");
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
import GeneralLayout from "../../layouts/general_layout/GeneralLayout";
import TextShuffler from "../../components/actions/TextShuffler";
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/**
* ==============================================================================
* Main Component { Functional }
* ==============================================================================
* @param {Object} props - Server props
*/
export default function BlogIndex({ blogPost }: InferGetStaticPropsType<typeof getStaticProps>) {
// ## Get Contexts
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## Javascript Variables
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## React Hooks { useState, useEffect, useRef, etc ... }
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## Function Return
return (
<React.Fragment>
<Head>
<title>{blogPost.title} | Tben.me Blog</title>
<meta
name="description"
content={blogPost.excerpt}
/>
</Head>
<GeneralLayout>
<div className="flex flex-col items-start gap-2 mb-8 max-w-6xl w-full">
<button
className="bg-transparent text-white/50 border-2 border-solid border-white/20"
onClick={(e) => {
window.history.back();
}}
>
Back
</button>
<h1 className="m-0">
<TextShuffler textInput={blogPost.title} />
</h1>
<span className="text-lg">
<TextShuffler textInput={blogPost.excerpt} />
</span>
<span className="text-base opacity-50">
<TextShuffler textInput={blogPost.date_created.substring(0, 24)} />
</span>
</div>
<span
className="flex flex-col items-start max-w-6xl w-full gap-4 text-xl"
dangerouslySetInnerHTML={{ __html: blogPost.body }}
></span>
</GeneralLayout>
</React.Fragment>
);
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
}
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/**
* Server Side Props or Static Props
* ==============================================================================
*/
export const getStaticProps: GetStaticProps = async ({ params }) => {
// ## Environment processes
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## User Authentication
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## Page/Site Data Data Fetching
const postsResponse = await datasquirel.get({
key: process.env.DATASQUIREL_API_KEY,
db: "tbenme",
query: `select * from blog_posts WHERE slug='${params?.single}'`,
});
const post = postsResponse.payload[0];
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## Server Props Return
return {
props: {
blogPost: post,
},
revalidate: 3600,
};
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
};
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/**
* Server Side Props or Static Props
* ==============================================================================
*/
export const getStaticPaths: GetStaticPaths = async () => {
/**
* Data fetching
*
* @abstract fetch date from the server or externnal source
*/
const postsResponse = await datasquirel.get({
key: process.env.DATASQUIREL_API_KEY,
db: "tbenme",
query: `select slug from blog_posts`,
});
const posts: { slug: string }[] | null = postsResponse.payload;
const paths = posts?.map((entry) => {
return {
params: { single: entry.slug },
};
});
return {
paths: paths ? paths : [],
fallback: "blocking",
};
};
/**
* ==============================================================================
* Imports
* ==============================================================================
*/
import React from "react";
import Head from "next/head";
import type { InferGetStaticPropsType, GetStaticProps, GetStaticPaths } from "next";
const datasquirel = require("datasquirel");
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
import GeneralLayout from "../../layouts/general_layout/GeneralLayout";
import TextShuffler from "../../components/actions/TextShuffler";
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/**
* ==============================================================================
* Main Component { Functional }
* ==============================================================================
* @param {Object} props - Server props
*/
export default function BlogIndex({ blogPost }: InferGetStaticPropsType<typeof getStaticProps>) {
// ## Get Contexts
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## Javascript Variables
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## React Hooks { useState, useEffect, useRef, etc ... }
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## Function Return
return (
<React.Fragment>
<Head>
<title>{blogPost.title} | Tben.me Blog</title>
<meta
name="description"
content={blogPost.excerpt}
/>
</Head>
<GeneralLayout>
<div className="flex flex-col items-start gap-2 mb-8 max-w-6xl w-full">
<button
className="bg-transparent text-white/50 border-2 border-solid border-white/20"
onClick={(e) => {
window.history.back();
}}
>
Back
</button>
<h1 className="m-0">
<TextShuffler textInput={blogPost.title} />
</h1>
<span className="text-lg">
<TextShuffler textInput={blogPost.excerpt} />
</span>
<span className="text-base opacity-50">
<TextShuffler textInput={blogPost.date_created.substring(0, 24)} />
</span>
</div>
<span
className="flex flex-col items-start max-w-6xl w-full gap-4 text-xl"
dangerouslySetInnerHTML={{ __html: blogPost.body }}
></span>
</GeneralLayout>
</React.Fragment>
);
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
}
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/**
* Server Side Props or Static Props
* ==============================================================================
*/
export const getStaticProps: GetStaticProps = async ({ params }) => {
// ## Environment processes
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## User Authentication
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## Page/Site Data Data Fetching
const postsResponse = await datasquirel.get({
key: process.env.DATASQUIREL_API_KEY,
db: "tbenme",
query: `select * from blog_posts WHERE slug='${params?.single}'`,
});
const post = postsResponse.payload[0];
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
// ## Server Props Return
return {
props: {
blogPost: post,
},
revalidate: 3600,
};
/** ********************************************** */
/** ********************************************** */
/** ********************************************** */
};
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/** ****************************************************************************** */
/**
* Server Side Props or Static Props
* ==============================================================================
*/
export const getStaticPaths: GetStaticPaths = async () => {
/**
* Data fetching
*
* @abstract fetch date from the server or externnal source
*/
const postsResponse = await datasquirel.get({
key: process.env.DATASQUIREL_API_KEY,
db: "tbenme",
query: `select slug from blog_posts`,
});
const posts: { slug: string }[] | null = postsResponse.payload;
const paths = posts?.map((entry) => {
return {
params: { single: entry.slug },
};
});
return {
paths: paths ? paths : [],
fallback: "blocking",
};
};

View File

@ -1,39 +1,39 @@
import React from "react";
import Head from "next/head";
import TextShuffler from "../components/actions/TextShuffler";
import submitContactForm from "../functions/frontend/submitContactForm";
import GeneralLayout from "../layouts/general_layout/GeneralLayout";
import threeJsAnimations from "../functions/frontend/threeJsAnimations";
import ContactForm from "../app/(components)/ContactForm";
const contact = () => {
let [success, setSuccess] = React.useState(false);
return (
<GeneralLayout>
<Head>
<title>Contact me</title>
<meta
name="description"
content="Get in touch"
/>
</Head>
<div className="flex flex-col items-start max-w-6xl w-full">
<h1>
<TextShuffler textInput="Get in touch" />
</h1>
<span className="hero-sub-text">
<TextShuffler
textInput="Have a question? Want to work together? Send me a message!"
delay={500}
/>
</span>
<ContactForm />
</div>
</GeneralLayout>
);
};
export default contact;
import React from "react";
import Head from "next/head";
import TextShuffler from "../components/actions/TextShuffler";
import submitContactForm from "../functions/frontend/submitContactForm";
import GeneralLayout from "../layouts/general_layout/GeneralLayout";
import threeJsAnimations from "../functions/frontend/threeJsAnimations";
import ContactForm from "../app/(components)/ContactForm";
const contact = () => {
let [success, setSuccess] = React.useState(false);
return (
<GeneralLayout>
<Head>
<title>Contact me</title>
<meta
name="description"
content="Get in touch"
/>
</Head>
<div className="flex flex-col items-start max-w-6xl w-full">
<h1>
<TextShuffler textInput="Get in touch" />
</h1>
<span className="hero-sub-text">
<TextShuffler
textInput="Have a question? Want to work together? Send me a message!"
delay={500}
/>
</span>
<ContactForm />
</div>
</GeneralLayout>
);
};
export default contact;

View File

@ -1,54 +1,54 @@
import React from "react";
import Head from "next/head";
import TextShuffler from "../components/actions/TextShuffler";
import GeneralLayout from "../layouts/general_layout/GeneralLayout";
import PortfolioEntry from "../components/PortfolioEntry";
const myWork = () => {
const portfolioEntries = require("../components/portfolioEntries.json");
return (
<GeneralLayout>
<Head>
<title>My Work | Tben</title>
<meta
name="description"
content="Some of my Work"
/>
</Head>
<div className="flex flex-col items-start w-full max-w-6xl">
<h1>
<TextShuffler textInput="My Work" />
</h1>
<span className="hero-sub-text mb-2">
<TextShuffler
textInput="Some of the projects I've worked on"
delay={500}
/>
</span>
<div className="portfolio-entries-block mt-4">
{portfolioEntries.map((entry) => (
<PortfolioEntry
key={entry.title}
title={entry.title}
description={entry.description}
url={entry.url}
image={entry.image}
/>
))}
</div>
</div>
</GeneralLayout>
);
};
export default myWork;
// {
// "title": "Stirrmedia Social webapp",
// "description": "A new social media experience without censorship",
// "url": "https://stirrmedia.com",
// "image": "/images/stirrmediascreenshot.png"
// },
import React from "react";
import Head from "next/head";
import TextShuffler from "../components/actions/TextShuffler";
import GeneralLayout from "../layouts/general_layout/GeneralLayout";
import PortfolioEntry from "../components/PortfolioEntry";
const myWork = () => {
const portfolioEntries = require("../components/portfolioEntries.json");
return (
<GeneralLayout>
<Head>
<title>My Work | Tben</title>
<meta
name="description"
content="Some of my Work"
/>
</Head>
<div className="flex flex-col items-start w-full max-w-6xl">
<h1>
<TextShuffler textInput="My Work" />
</h1>
<span className="hero-sub-text mb-2">
<TextShuffler
textInput="Some of the projects I've worked on"
delay={500}
/>
</span>
<div className="portfolio-entries-block mt-4">
{portfolioEntries.map((entry) => (
<PortfolioEntry
key={entry.title}
title={entry.title}
description={entry.description}
url={entry.url}
image={entry.image}
/>
))}
</div>
</div>
</GeneralLayout>
);
};
export default myWork;
// {
// "title": "Stirrmedia Social webapp",
// "description": "A new social media experience without censorship",
// "url": "https://stirrmedia.com",
// "image": "/images/stirrmediascreenshot.png"
// },

View File

@ -1,3 +1,3 @@
User-agent: *
User-agent: *
Sitemap: https://tben.me/sitemap.xml

View File

@ -1,169 +1,169 @@
/**
* Register plugins
*/
gsap.registerPlugin(ScrollTrigger);
const mm = gsap.matchMedia();
mm.add("(min-width: 1200px)", () => {
ScrollTrigger.create({
trigger: "#homepage-content-wrapper",
// toggleActions: "play none none none",
start: "top 200px",
end: "bottom 90%",
pin: "#main-image",
// markers: true,
scrub: 1,
onUpdate: (self) => {
/**
* Origin
*/
if (self.progress < 0.4) {
gsap.to("#main-image", {
backgroundColor: "#3e3f9c",
x: 0,
duration: 1,
});
gsap.to("body", {
backgroundColor: "#181515",
});
}
/**
* Step 2
*/
if (self.progress > 0.4) {
gsap.to("#main-image", {
backgroundColor: "#FE6847",
x: -50,
duration: 1,
});
gsap.to("body", {
backgroundColor: "#292a6b",
});
}
// if (self.progress > 0 && self.progress < 1) {
// gsap.to("#main-image", {
// position: "fixed",
// top: 20,
// left: 20,
// });
// }
},
onLeave: (self) => {},
});
// gsap.to("#main-image", {
// scale: 1,
// duration: 1,
// });
/**
* Entry animation timeline
*/
const entryTimeline = gsap.timeline();
entryTimeline.to("#main-image", {
scale: 1,
duration: 1,
});
/**
* Scroll animation timeline
*/
// const scrollTimeline = gsap.timeline({
// scrollTrigger: {
// trigger: "#homepage-content-wrapper",
// start: "top 200px",
// end: "bottom 90%",
// scrub: 1,
// },
// });
// scrollTimeline
// // .to("#main-image", {
// // scale: 1,
// // duration: 1,
// // })
// .to("#main-image", {
// rotate: -10,
// });
});
/**
* Animates the hero section
*/
// function homepageTimeline() {
// /**
// * Animate hero section
// */
// tl.to("#main-image", {
// y: 100,
// }).to("#main-image", {
// y: -100,
// });
// tl.to("#main-image", {
// x: 300,
// scrollTrigger: {
// trigger: "#main-image",
// // toggleActions: "play none none none",
// // start: "top center",
// // end: "bottom 500px",
// // pin: "#main-image",
// markers: true,
// scrub: 1,
// },
// });
// }
// .to({
// scrollTrigger: {
// trigger: "#homepage-content-wrapper",
// // toggleActions: "play none none none",
// start: "top top",
// end: "bottom bottom",
// pin: "#main-image",
// markers: true,
// scrub: 1,
// },
// });
// .to("#main-image", {
// y: 300,
// duration: 1,
// scrollTrigger: {
// // trigger: "#homepage-content-wrapper",
// // toggleActions: "play none none none",
// start: "top 100%",
// end: "bottom 0%",
// pin: "#main-image",
// markers: true,
// scrub: 1,
// },
// });
// requestAnimationFrame(async () => {
// await tl.fromTo(
// "#main-image",
// {
// scale: 1.2,
// },
// {
// scale: 1,
// duration: 1,
// }
// );
// await tl.to("#main-image", {
// y: 300,
// duration: 1,
// scrollTrigger: {
// // trigger: "#main-image",
// // toggleActions: "play none none none",
// // start: "top center",
// // end: "bottom 500px",
// pin: true,
// markers: true,
// scrub: 1,
// },
// });
// });
/**
* Register plugins
*/
gsap.registerPlugin(ScrollTrigger);
const mm = gsap.matchMedia();
mm.add("(min-width: 1200px)", () => {
ScrollTrigger.create({
trigger: "#homepage-content-wrapper",
// toggleActions: "play none none none",
start: "top 200px",
end: "bottom 90%",
pin: "#main-image",
// markers: true,
scrub: 1,
onUpdate: (self) => {
/**
* Origin
*/
if (self.progress < 0.4) {
gsap.to("#main-image", {
backgroundColor: "#3e3f9c",
x: 0,
duration: 1,
});
gsap.to("body", {
backgroundColor: "#181515",
});
}
/**
* Step 2
*/
if (self.progress > 0.4) {
gsap.to("#main-image", {
backgroundColor: "#FE6847",
x: -50,
duration: 1,
});
gsap.to("body", {
backgroundColor: "#292a6b",
});
}
// if (self.progress > 0 && self.progress < 1) {
// gsap.to("#main-image", {
// position: "fixed",
// top: 20,
// left: 20,
// });
// }
},
onLeave: (self) => {},
});
// gsap.to("#main-image", {
// scale: 1,
// duration: 1,
// });
/**
* Entry animation timeline
*/
const entryTimeline = gsap.timeline();
entryTimeline.to("#main-image", {
scale: 1,
duration: 1,
});
/**
* Scroll animation timeline
*/
// const scrollTimeline = gsap.timeline({
// scrollTrigger: {
// trigger: "#homepage-content-wrapper",
// start: "top 200px",
// end: "bottom 90%",
// scrub: 1,
// },
// });
// scrollTimeline
// // .to("#main-image", {
// // scale: 1,
// // duration: 1,
// // })
// .to("#main-image", {
// rotate: -10,
// });
});
/**
* Animates the hero section
*/
// function homepageTimeline() {
// /**
// * Animate hero section
// */
// tl.to("#main-image", {
// y: 100,
// }).to("#main-image", {
// y: -100,
// });
// tl.to("#main-image", {
// x: 300,
// scrollTrigger: {
// trigger: "#main-image",
// // toggleActions: "play none none none",
// // start: "top center",
// // end: "bottom 500px",
// // pin: "#main-image",
// markers: true,
// scrub: 1,
// },
// });
// }
// .to({
// scrollTrigger: {
// trigger: "#homepage-content-wrapper",
// // toggleActions: "play none none none",
// start: "top top",
// end: "bottom bottom",
// pin: "#main-image",
// markers: true,
// scrub: 1,
// },
// });
// .to("#main-image", {
// y: 300,
// duration: 1,
// scrollTrigger: {
// // trigger: "#homepage-content-wrapper",
// // toggleActions: "play none none none",
// start: "top 100%",
// end: "bottom 0%",
// pin: "#main-image",
// markers: true,
// scrub: 1,
// },
// });
// requestAnimationFrame(async () => {
// await tl.fromTo(
// "#main-image",
// {
// scale: 1.2,
// },
// {
// scale: 1,
// duration: 1,
// }
// );
// await tl.to("#main-image", {
// y: 300,
// duration: 1,
// scrollTrigger: {
// // trigger: "#main-image",
// // toggleActions: "play none none none",
// // start: "top center",
// // end: "bottom 500px",
// pin: true,
// markers: true,
// scrub: 1,
// },
// });
// });

View File

@ -1,22 +1,22 @@
console.log("main js");
const lenis = new Lenis();
lenis.on("scroll", (e) => {
// console.log(e);
});
function raf(time) {
lenis.raf(time);
requestAnimationFrame(raf);
}
requestAnimationFrame(raf);
/**
* GSAP globals
*/
// gsap.to(".gsap-text", {
// text: "Welcome",
// });
console.log("main js");
const lenis = new Lenis();
lenis.on("scroll", (e) => {
// console.log(e);
});
function raf(time) {
lenis.raf(time);
requestAnimationFrame(raf);
}
requestAnimationFrame(raf);
/**
* GSAP globals
*/
// gsap.to(".gsap-text", {
// text: "Welcome",
// });

View File

@ -1,42 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://tben.me/</loc>
<lastmod>2022-07-16T17:30:45.214Z</lastmod>
</url>
<url>
<loc>https://tben.me/about</loc>
<lastmod>2022-07-16T17:30:45.214Z</lastmod>
</url>
<url>
<loc>https://tben.me/work</loc>
<lastmod>2022-07-16T17:30:45.214Z</lastmod>
</url>
<url>
<loc>https://tben.me/blog</loc>
<lastmod>2022-07-16T17:30:45.214Z</lastmod>
</url>
<url>
<loc>https://tben.me/blog/choosing-your-tech-stack</loc>
<lastmod>2022-07-16T17:30:45.214Z</lastmod>
</url>
<url>
<loc>https://tben.me/blog/find-your-perfect-framework</loc>
<lastmod>2022-07-16T17:30:45.214Z</lastmod>
</url>
<url>
<loc>https://tben.me/contact</loc>
<lastmod>2022-07-16T17:30:45.214Z</lastmod>
</url>
<url>
<loc>https://tben.me/documents/Benjamin-Toby-CV-7-27-2022.pdf</loc>
<lastmod>2022-07-16T17:30:45.214Z</lastmod>
</url>
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://tben.me/</loc>
<lastmod>2022-07-16T17:30:45.214Z</lastmod>
</url>
<url>
<loc>https://tben.me/about</loc>
<lastmod>2022-07-16T17:30:45.214Z</lastmod>
</url>
<url>
<loc>https://tben.me/work</loc>
<lastmod>2022-07-16T17:30:45.214Z</lastmod>
</url>
<url>
<loc>https://tben.me/blog</loc>
<lastmod>2022-07-16T17:30:45.214Z</lastmod>
</url>
<url>
<loc>https://tben.me/blog/choosing-your-tech-stack</loc>
<lastmod>2022-07-16T17:30:45.214Z</lastmod>
</url>
<url>
<loc>https://tben.me/blog/find-your-perfect-framework</loc>
<lastmod>2022-07-16T17:30:45.214Z</lastmod>
</url>
<url>
<loc>https://tben.me/contact</loc>
<lastmod>2022-07-16T17:30:45.214Z</lastmod>
</url>
<url>
<loc>https://tben.me/documents/Benjamin-Toby-CV-7-27-2022.pdf</loc>
<lastmod>2022-07-16T17:30:45.214Z</lastmod>
</url>
</urlset>

View File

@ -1,441 +1,441 @@
@import url("https://fonts.googleapis.com/css?family=Source+Code+Pro&display=swap");
html {
width: 100%;
/* overflow-x: hidden; */
scroll-behavior: smooth;
font-family: "Source Code Pro", Helvetica;
font-size: 16px;
line-height: 1.5;
color: #222;
letter-spacing: -0.8px;
}
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
:root {
--main-color: #1668e4;
--main-color-lighter: #5698fc;
--dark-color: #201e1e;
--sec-color-3: #688e26;
--sec-color-4: #adb2d3;
--sec-color-5: #c2a878;
--light-color-1: rgb(64, 37, 216);
--test-color: rgb(113, 116, 255);
--transparent-white: rgba(255, 255, 255, 0.2);
}
body {
width: 100%;
margin: 0px;
padding: 0px;
top: 0;
justify-content: center;
background-color: var(--dark-color);
color: white;
}
a {
text-decoration: none;
color: white;
border: 1px solid transparent;
}
a:hover {
color: var(--sec-color-2);
}
button {
padding: 10px 25px;
border: none;
cursor: pointer;
font-size: inherit;
border: 1px solid transparent;
}
hr {
opacity: 0.3;
}
button:hover {
background-color: var(--dark-color);
/* background-color: #c52532; */
color: white;
border-color: var(--transparent-white);
}
form * {
font-family: inherit;
font-size: inherit;
}
header {
z-index: 1000000;
margin-bottom: 40px;
}
h1 {
font-size: 52px;
margin-top: 0;
margin-bottom: 10px;
}
/* * */
/* * */
/* * */
/* * */
/* * */
/* * */
/* * */
p a,
span a {
color: var(--main-color-lighter);
/* border-bottom: 1px solid var(--main-color-lighter); */
}
p a:hover,
span a:hover {
color: var(--main-color);
}
/* ################################################# -- Sliders */
aside,
.side-nav-block {
scrollbar-width: none;
}
/* width */
aside::-webkit-scrollbar,
.side-nav-block::-webkit-scrollbar {
width: 5px;
}
/* Track */
aside::-webkit-scrollbar-track,
.side-nav-block::-webkit-scrollbar-track {
background: #f1f1f1;
}
/* Handle */
aside::-webkit-scrollbar-thumb,
.side-nav-block::-webkit-scrollbar-thumb {
background: #dbe1eb;
border-radius: 10px;
}
/* Handle on hover */
aside::-webkit-scrollbar-thumb:hover,
.side-nav-block::-webkit-scrollbar-thumb:hover {
background: #555;
}
/*############################################# -- Common Actions */
.visible {
display: flex;
}
.hidden {
display: none;
}
.fixed {
position: fixed;
}
.absolute {
position: absolute;
}
.relative {
position: relative;
}
.no-pointer-events {
pointer-events: none;
}
.pointer-events {
pointer-events: visible;
}
.spacer {
display: block;
width: 100%;
height: 80px;
}
/*############################################# -- Header */
header {
color: white;
}
.logo-link-block h1 {
font-size: 28px;
margin: 0;
margin-bottom: 5px;
}
nav {
display: flex;
align-items: center;
gap: 20px;
}
nav a {
opacity: 0.5;
cursor: pointer;
}
nav a:hover {
opacity: 1;
}
.active-page {
opacity: 1;
border-bottom: 2px solid white;
}
/*############################################# -- Shuffled Text */
#__next {
max-width: 1200px;
width: 100%;
height: 100%;
opacity: 0;
animation-name: shuffle;
animation-timing-function: ease-out;
animation-duration: 1s;
animation-fill-mode: forwards;
}
/* .shuffled-text-span span {
animation-name: shuffle;
animation-timing-function: ease-out;
animation-delay: 0.5s;
} */
@keyframes shuffle {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
/*############################################# -- Hero Section */
.hero-sub-text {
font-size: 24px;
color: rgba(255, 255, 255, 0.7);
}
.hero-ctas-section {
display: flex;
align-items: center;
margin-top: 40px;
gap: 20px;
}
.hero-ctas-section a {
padding: 10px 25px;
border: none;
cursor: pointer;
font-size: inherit;
color: var(--dark-color);
background-color: white;
font-size: 18px;
}
.hero-ctas-section a:hover {
background-color: var(--dark-color);
color: white;
border-color: var(--transparent-white);
}
/*############################################# -- 404 page */
.not-found-page-wrapper {
display: flex;
align-items: flex-start;
justify-content: center;
flex-direction: column;
width: 100%;
height: 100%;
}
/*############################################# -- Portfolio page */
.portfolio-entries-block {
display: grid;
gap: 40px;
grid-template-columns: 1fr 1fr;
width: 100%;
}
.portfolio-entry-block {
max-width: 700px;
border: 1px solid rgba(255, 255, 255, 0.3);
padding: 20px;
}
/*############################################# -- Contact Forms */
form {
margin-top: 40px;
max-width: 1000px;
width: 100%;
display: flex;
flex-direction: column;
gap: 20px;
position: relative;
}
input,
textarea {
padding: 15px 20px;
background-color: transparent;
color: white;
border: 1px solid rgba(255, 255, 255, 0.2);
width: 100%;
resize: none;
}
.message-response {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
background-color: var(--dark-color);
border: 2px solid #688e26;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 24px;
gap: 10px;
}
.message-response.failed {
border: 2px solid #d42222;
}
#homepage-animation-wrapper {
width: 150%;
height: 100%;
background-color: var(--dark-color);
}
.tech-stack-header {
color: rgb(113, 116, 255);
margin-bottom: 3px;
}
.portfolio-image-wrapper {
width: 100%;
height: 300px;
margin-bottom: 15px;
overflow: hidden;
}
.portfolio-image {
width: 100%;
height: 100%;
object-fit: cover;
width: 100%;
height: 300px;
object-position: left top;
margin-bottom: 20px;
}
/* * */
/* * */
/* * */
/* * */
/* * */
/* * */
/* * */
/* * */
span img {
width: 100%;
height: auto;
object-fit: contain;
}
/* * */
/* * */
/* * */
/* * */
/* * */
/* * */
/* * */
/* * */
/* .transition-fade {
transition: 0.4s;
opacity: 1;
}
html.is-animating .transition-fade {
opacity: 0;
} */
/* ###############################################################################################
##################################################################################################
##################################################################################################
##################################################################################################
##################################### -- Mobile Styles -- ########################################
##################################################################################################
##################################################################################################
##################################################################################################
############################################################################################### */
@media (max-width: 1200px) {
}
@media (max-width: 990px) {
.portfolio-entries-block {
grid-template-columns: 1fr;
}
}
@media (max-width: 800px) {
h1 {
font-size: 28px;
}
nav {
flex-wrap: wrap;
gap: 15px;
}
body {
padding: 20px;
padding-bottom: 80px;
}
.hero-ctas-section {
flex-wrap: wrap;
}
}
@media (max-width: 600px) {
.portfolio-image-wrapper {
height: auto;
}
.portfolio-image {
object-fit: contain;
height: auto;
margin: 0;
}
}
@media (max-width: 450px) {
}
@media (max-width: 350px) {
}
@import url("https://fonts.googleapis.com/css?family=Source+Code+Pro&display=swap");
html {
width: 100%;
/* overflow-x: hidden; */
scroll-behavior: smooth;
font-family: "Source Code Pro", Helvetica;
font-size: 16px;
line-height: 1.5;
color: #222;
letter-spacing: -0.8px;
}
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
:root {
--main-color: #1668e4;
--main-color-lighter: #5698fc;
--dark-color: #201e1e;
--sec-color-3: #688e26;
--sec-color-4: #adb2d3;
--sec-color-5: #c2a878;
--light-color-1: rgb(64, 37, 216);
--test-color: rgb(113, 116, 255);
--transparent-white: rgba(255, 255, 255, 0.2);
}
body {
width: 100%;
margin: 0px;
padding: 0px;
top: 0;
justify-content: center;
background-color: var(--dark-color);
color: white;
}
a {
text-decoration: none;
color: white;
border: 1px solid transparent;
}
a:hover {
color: var(--sec-color-2);
}
button {
padding: 10px 25px;
border: none;
cursor: pointer;
font-size: inherit;
border: 1px solid transparent;
}
hr {
opacity: 0.3;
}
button:hover {
background-color: var(--dark-color);
/* background-color: #c52532; */
color: white;
border-color: var(--transparent-white);
}
form * {
font-family: inherit;
font-size: inherit;
}
header {
z-index: 1000000;
margin-bottom: 40px;
}
h1 {
font-size: 52px;
margin-top: 0;
margin-bottom: 10px;
}
/* * */
/* * */
/* * */
/* * */
/* * */
/* * */
/* * */
p a,
span a {
color: var(--main-color-lighter);
/* border-bottom: 1px solid var(--main-color-lighter); */
}
p a:hover,
span a:hover {
color: var(--main-color);
}
/* ################################################# -- Sliders */
aside,
.side-nav-block {
scrollbar-width: none;
}
/* width */
aside::-webkit-scrollbar,
.side-nav-block::-webkit-scrollbar {
width: 5px;
}
/* Track */
aside::-webkit-scrollbar-track,
.side-nav-block::-webkit-scrollbar-track {
background: #f1f1f1;
}
/* Handle */
aside::-webkit-scrollbar-thumb,
.side-nav-block::-webkit-scrollbar-thumb {
background: #dbe1eb;
border-radius: 10px;
}
/* Handle on hover */
aside::-webkit-scrollbar-thumb:hover,
.side-nav-block::-webkit-scrollbar-thumb:hover {
background: #555;
}
/*############################################# -- Common Actions */
.visible {
display: flex;
}
.hidden {
display: none;
}
.fixed {
position: fixed;
}
.absolute {
position: absolute;
}
.relative {
position: relative;
}
.no-pointer-events {
pointer-events: none;
}
.pointer-events {
pointer-events: visible;
}
.spacer {
display: block;
width: 100%;
height: 80px;
}
/*############################################# -- Header */
header {
color: white;
}
.logo-link-block h1 {
font-size: 28px;
margin: 0;
margin-bottom: 5px;
}
nav {
display: flex;
align-items: center;
gap: 20px;
}
nav a {
opacity: 0.5;
cursor: pointer;
}
nav a:hover {
opacity: 1;
}
.active-page {
opacity: 1;
border-bottom: 2px solid white;
}
/*############################################# -- Shuffled Text */
#__next {
max-width: 1200px;
width: 100%;
height: 100%;
opacity: 0;
animation-name: shuffle;
animation-timing-function: ease-out;
animation-duration: 1s;
animation-fill-mode: forwards;
}
/* .shuffled-text-span span {
animation-name: shuffle;
animation-timing-function: ease-out;
animation-delay: 0.5s;
} */
@keyframes shuffle {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
/*############################################# -- Hero Section */
.hero-sub-text {
font-size: 24px;
color: rgba(255, 255, 255, 0.7);
}
.hero-ctas-section {
display: flex;
align-items: center;
margin-top: 40px;
gap: 20px;
}
.hero-ctas-section a {
padding: 10px 25px;
border: none;
cursor: pointer;
font-size: inherit;
color: var(--dark-color);
background-color: white;
font-size: 18px;
}
.hero-ctas-section a:hover {
background-color: var(--dark-color);
color: white;
border-color: var(--transparent-white);
}
/*############################################# -- 404 page */
.not-found-page-wrapper {
display: flex;
align-items: flex-start;
justify-content: center;
flex-direction: column;
width: 100%;
height: 100%;
}
/*############################################# -- Portfolio page */
.portfolio-entries-block {
display: grid;
gap: 40px;
grid-template-columns: 1fr 1fr;
width: 100%;
}
.portfolio-entry-block {
max-width: 700px;
border: 1px solid rgba(255, 255, 255, 0.3);
padding: 20px;
}
/*############################################# -- Contact Forms */
form {
margin-top: 40px;
max-width: 1000px;
width: 100%;
display: flex;
flex-direction: column;
gap: 20px;
position: relative;
}
input,
textarea {
padding: 15px 20px;
background-color: transparent;
color: white;
border: 1px solid rgba(255, 255, 255, 0.2);
width: 100%;
resize: none;
}
.message-response {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
background-color: var(--dark-color);
border: 2px solid #688e26;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 24px;
gap: 10px;
}
.message-response.failed {
border: 2px solid #d42222;
}
#homepage-animation-wrapper {
width: 150%;
height: 100%;
background-color: var(--dark-color);
}
.tech-stack-header {
color: rgb(113, 116, 255);
margin-bottom: 3px;
}
.portfolio-image-wrapper {
width: 100%;
height: 300px;
margin-bottom: 15px;
overflow: hidden;
}
.portfolio-image {
width: 100%;
height: 100%;
object-fit: cover;
width: 100%;
height: 300px;
object-position: left top;
margin-bottom: 20px;
}
/* * */
/* * */
/* * */
/* * */
/* * */
/* * */
/* * */
/* * */
span img {
width: 100%;
height: auto;
object-fit: contain;
}
/* * */
/* * */
/* * */
/* * */
/* * */
/* * */
/* * */
/* * */
/* .transition-fade {
transition: 0.4s;
opacity: 1;
}
html.is-animating .transition-fade {
opacity: 0;
} */
/* ###############################################################################################
##################################################################################################
##################################################################################################
##################################################################################################
##################################### -- Mobile Styles -- ########################################
##################################################################################################
##################################################################################################
##################################################################################################
############################################################################################### */
@media (max-width: 1200px) {
}
@media (max-width: 990px) {
.portfolio-entries-block {
grid-template-columns: 1fr;
}
}
@media (max-width: 800px) {
h1 {
font-size: 28px;
}
nav {
flex-wrap: wrap;
gap: 15px;
}
body {
padding: 20px;
padding-bottom: 80px;
}
.hero-ctas-section {
flex-wrap: wrap;
}
}
@media (max-width: 600px) {
.portfolio-image-wrapper {
height: auto;
}
.portfolio-image {
object-fit: contain;
height: auto;
margin: 0;
}
}
@media (max-width: 450px) {
}
@media (max-width: 350px) {
}

28
scripts/lottie.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -1,479 +1,479 @@
@import url("https://fonts.googleapis.com/css?family=Cutive+Mono&display=swap");
html {
width: 100%;
/* scroll-behavior: smooth; */
font-family: "Cutive Mono", Helvetica;
font-size: 16px;
line-height: 1.5;
color: #222;
letter-spacing: -0.8px;
}
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
:root {
--main-color: #7174ff;
--main-color-lighter: #9d9eff;
--main-color-darker: #7072c9;
--main-color-dark: #292a6b;
--dark-color: #181515;
--sec-color-3: #688e26;
--sec-color-4: #adb2d3;
--sec-color-5: #c2a878;
--light-color-1: rgb(64, 37, 216);
--test-color: #7174ff;
--transparent-white: rgba(255, 255, 255, 0.2);
}
body {
width: 100%;
margin: 0px;
top: 0;
justify-content: center;
background-color: var(--dark-color);
color: white;
}
@media (max-width: 1200px) {
main {
padding-top: 200px;
}
}
@media (max-width: 600px) {
main {
padding-top: 150px;
}
}
a {
text-decoration: none;
color: white;
border: 1px solid transparent;
}
a:hover {
color: var(--sec-color-2);
}
button,
.button {
padding: 10px 25px;
border: none;
cursor: pointer;
font-size: inherit;
border: 1px solid transparent;
color: var(--dark-color);
background-color: white;
font-size: 18px;
font-weight: 600;
}
button.outlined,
.button.outlined {
border: 1px solid white;
background-color: transparent;
color: white;
}
hr {
opacity: 0.3;
}
button:hover,
.button:hover {
background-color: var(--dark-color);
/* background-color: #c52532; */
color: white;
border-color: var(--transparent-white);
}
form * {
font-family: inherit;
font-size: inherit;
}
header {
z-index: 1000000;
}
h1 {
font-size: 52px;
margin-top: 0;
margin-bottom: 10px;
}
h2 {
font-size: 44px;
}
h3 {
font-size: 28px;
}
h4 {
font-size: 22px;
}
/* * */
/* * */
/* * */
/* * */
/* * */
/* * */
/* * */
p a,
span a {
color: var(--main-color-lighter);
/* border-bottom: 1px solid var(--main-color-lighter); */
}
p a:hover,
span a:hover {
color: var(--main-color);
}
/* ################################################# -- Sliders */
aside,
.side-nav-block {
scrollbar-width: none;
}
/* width */
aside::-webkit-scrollbar,
.side-nav-block::-webkit-scrollbar {
width: 5px;
}
/* Track */
aside::-webkit-scrollbar-track,
.side-nav-block::-webkit-scrollbar-track {
background: #f1f1f1;
}
/* Handle */
aside::-webkit-scrollbar-thumb,
.side-nav-block::-webkit-scrollbar-thumb {
background: #dbe1eb;
border-radius: 10px;
}
/* Handle on hover */
aside::-webkit-scrollbar-thumb:hover,
.side-nav-block::-webkit-scrollbar-thumb:hover {
background: #555;
}
/*############################################# -- Common Actions */
.visible {
display: flex;
}
.hidden {
display: none;
}
.fixed {
position: fixed;
}
.absolute {
position: absolute;
}
.relative {
position: relative;
}
.no-pointer-events {
pointer-events: none;
}
.pointer-events {
pointer-events: visible;
}
.spacer {
display: block;
width: 100%;
height: 80px;
}
/*############################################# -- Header */
header {
color: white;
}
.logo-link-block h1 {
font-size: 28px;
margin: 0;
margin-bottom: 5px;
}
nav {
display: flex;
align-items: center;
gap: 20px;
}
nav a {
opacity: 0.5;
cursor: pointer;
}
nav a:hover {
opacity: 1;
}
.active-page {
opacity: 1;
border-bottom: 2px solid white;
}
/*############################################# -- Shuffled Text */
#__next {
width: 100%;
opacity: 0;
animation-name: shuffle;
animation-timing-function: ease-out;
animation-duration: 1s;
animation-fill-mode: forwards;
}
/* .shuffled-text-span span {
animation-name: shuffle;
animation-timing-function: ease-out;
animation-delay: 0.5s;
} */
@keyframes shuffle {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
/*############################################# -- Hero Section */
.hero-sub-text {
font-size: 24px;
color: rgba(255, 255, 255, 0.7);
}
.hero-ctas-section {
display: flex;
align-items: center;
margin-top: 40px;
gap: 20px;
}
.hero-ctas-section a {
padding: 10px 25px;
border: none;
cursor: pointer;
font-size: inherit;
color: var(--dark-color);
background-color: white;
font-size: 18px;
}
.hero-ctas-section a:hover {
background-color: var(--dark-color);
color: white;
border-color: var(--transparent-white);
}
/*############################################# -- 404 page */
.not-found-page-wrapper {
display: flex;
align-items: flex-start;
justify-content: center;
flex-direction: column;
width: 100%;
height: 100%;
}
/*############################################# -- Portfolio page */
.portfolio-entries-block {
display: grid;
gap: 40px;
grid-template-columns: 1fr 1fr;
width: 100%;
}
.portfolio-entry-block {
max-width: 700px;
border: 1px solid rgba(255, 255, 255, 0.3);
padding: 20px;
}
/*############################################# -- Contact Forms */
form {
margin-top: 40px;
max-width: 1000px;
width: 100%;
display: flex;
flex-direction: column;
gap: 20px;
position: relative;
}
input,
textarea {
padding: 15px 20px;
background-color: transparent;
color: white;
border: 1px solid rgba(255, 255, 255, 0.5);
width: 100%;
resize: none;
font-size: 20px;
}
input::placeholder,
textarea::placeholder {
color: rgba(255, 255, 255, 0.5);
}
.message-response {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
background-color: var(--dark-color);
border: 2px solid #688e26;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 24px;
gap: 10px;
}
.message-response.failed {
border: 2px solid #d42222;
}
#homepage-animation-wrapper {
width: 150%;
height: 100%;
background-color: var(--dark-color);
}
.tech-stack-header {
color: #7174ff;
margin-bottom: 3px;
}
.portfolio-image-wrapper {
width: 100%;
height: 300px;
margin-bottom: 15px;
overflow: hidden;
}
.portfolio-image {
width: 100%;
height: 100%;
object-fit: cover;
width: 100%;
height: 300px;
object-position: left top;
margin-bottom: 20px;
}
/* * */
/* * */
/* * */
/* * */
/* * */
/* * */
/* * */
/* * */
span img {
width: 100%;
height: auto;
object-fit: contain;
}
/* * */
/* * */
/* * */
/* * */
/* * */
/* * */
/* * */
/* * */
/* .transition-fade {
transition: 0.4s;
opacity: 1;
}
html.is-animating .transition-fade {
opacity: 0;
} */
/* ###############################################################################################
##################################################################################################
##################################################################################################
##################################################################################################
##################################### -- Mobile Styles -- ########################################
##################################################################################################
##################################################################################################
##################################################################################################
############################################################################################### */
@media (max-width: 1200px) {
#main-image {
position: fixed;
top: 40px;
z-index: -1;
}
}
@media (max-width: 990px) {
.portfolio-entries-block {
grid-template-columns: 1fr;
}
}
@media (max-width: 800px) {
h1 {
font-size: 28px;
}
nav {
flex-wrap: wrap;
gap: 15px;
}
.hero-ctas-section {
flex-wrap: wrap;
}
}
@media (max-width: 600px) {
.portfolio-image-wrapper {
height: auto;
}
.portfolio-image {
object-fit: contain;
height: auto;
margin: 0;
}
}
@media (max-width: 450px) {
}
@media (max-width: 350px) {
}
@import url("https://fonts.googleapis.com/css?family=Cutive+Mono&display=swap");
html {
width: 100%;
/* scroll-behavior: smooth; */
font-family: "Cutive Mono", Helvetica;
font-size: 16px;
line-height: 1.5;
color: #222;
letter-spacing: -0.8px;
}
* {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
:root {
--main-color: #7174ff;
--main-color-lighter: #9d9eff;
--main-color-darker: #7072c9;
--main-color-dark: #292a6b;
--dark-color: #181515;
--sec-color-3: #688e26;
--sec-color-4: #adb2d3;
--sec-color-5: #c2a878;
--light-color-1: rgb(64, 37, 216);
--test-color: #7174ff;
--transparent-white: rgba(255, 255, 255, 0.2);
}
body {
width: 100%;
margin: 0px;
top: 0;
justify-content: center;
background-color: var(--dark-color);
color: white;
}
@media (max-width: 1200px) {
main {
padding-top: 200px;
}
}
@media (max-width: 600px) {
main {
padding-top: 150px;
}
}
a {
text-decoration: none;
color: white;
border: 1px solid transparent;
}
a:hover {
color: var(--sec-color-2);
}
button,
.button {
padding: 10px 25px;
border: none;
cursor: pointer;
font-size: inherit;
border: 1px solid transparent;
color: var(--dark-color);
background-color: white;
font-size: 18px;
font-weight: 600;
}
button.outlined,
.button.outlined {
border: 1px solid white;
background-color: transparent;
color: white;
}
hr {
opacity: 0.3;
}
button:hover,
.button:hover {
background-color: var(--dark-color);
/* background-color: #c52532; */
color: white;
border-color: var(--transparent-white);
}
form * {
font-family: inherit;
font-size: inherit;
}
header {
z-index: 1000000;
}
h1 {
font-size: 52px;
margin-top: 0;
margin-bottom: 10px;
}
h2 {
font-size: 44px;
}
h3 {
font-size: 28px;
}
h4 {
font-size: 22px;
}
/* * */
/* * */
/* * */
/* * */
/* * */
/* * */
/* * */
p a,
span a {
color: var(--main-color-lighter);
/* border-bottom: 1px solid var(--main-color-lighter); */
}
p a:hover,
span a:hover {
color: var(--main-color);
}
/* ################################################# -- Sliders */
aside,
.side-nav-block {
scrollbar-width: none;
}
/* width */
aside::-webkit-scrollbar,
.side-nav-block::-webkit-scrollbar {
width: 5px;
}
/* Track */
aside::-webkit-scrollbar-track,
.side-nav-block::-webkit-scrollbar-track {
background: #f1f1f1;
}
/* Handle */
aside::-webkit-scrollbar-thumb,
.side-nav-block::-webkit-scrollbar-thumb {
background: #dbe1eb;
border-radius: 10px;
}
/* Handle on hover */
aside::-webkit-scrollbar-thumb:hover,
.side-nav-block::-webkit-scrollbar-thumb:hover {
background: #555;
}
/*############################################# -- Common Actions */
.visible {
display: flex;
}
.hidden {
display: none;
}
.fixed {
position: fixed;
}
.absolute {
position: absolute;
}
.relative {
position: relative;
}
.no-pointer-events {
pointer-events: none;
}
.pointer-events {
pointer-events: visible;
}
.spacer {
display: block;
width: 100%;
height: 80px;
}
/*############################################# -- Header */
header {
color: white;
}
.logo-link-block h1 {
font-size: 28px;
margin: 0;
margin-bottom: 5px;
}
nav {
display: flex;
align-items: center;
gap: 20px;
}
nav a {
opacity: 0.5;
cursor: pointer;
}
nav a:hover {
opacity: 1;
}
.active-page {
opacity: 1;
border-bottom: 2px solid white;
}
/*############################################# -- Shuffled Text */
#__next {
width: 100%;
opacity: 0;
animation-name: shuffle;
animation-timing-function: ease-out;
animation-duration: 1s;
animation-fill-mode: forwards;
}
/* .shuffled-text-span span {
animation-name: shuffle;
animation-timing-function: ease-out;
animation-delay: 0.5s;
} */
@keyframes shuffle {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
/*############################################# -- Hero Section */
.hero-sub-text {
font-size: 24px;
color: rgba(255, 255, 255, 0.7);
}
.hero-ctas-section {
display: flex;
align-items: center;
margin-top: 40px;
gap: 20px;
}
.hero-ctas-section a {
padding: 10px 25px;
border: none;
cursor: pointer;
font-size: inherit;
color: var(--dark-color);
background-color: white;
font-size: 18px;
}
.hero-ctas-section a:hover {
background-color: var(--dark-color);
color: white;
border-color: var(--transparent-white);
}
/*############################################# -- 404 page */
.not-found-page-wrapper {
display: flex;
align-items: flex-start;
justify-content: center;
flex-direction: column;
width: 100%;
height: 100%;
}
/*############################################# -- Portfolio page */
.portfolio-entries-block {
display: grid;
gap: 40px;
grid-template-columns: 1fr 1fr;
width: 100%;
}
.portfolio-entry-block {
max-width: 700px;
border: 1px solid rgba(255, 255, 255, 0.3);
padding: 20px;
}
/*############################################# -- Contact Forms */
form {
margin-top: 40px;
max-width: 1000px;
width: 100%;
display: flex;
flex-direction: column;
gap: 20px;
position: relative;
}
input,
textarea {
padding: 15px 20px;
background-color: transparent;
color: white;
border: 1px solid rgba(255, 255, 255, 0.5);
width: 100%;
resize: none;
font-size: 20px;
}
input::placeholder,
textarea::placeholder {
color: rgba(255, 255, 255, 0.5);
}
.message-response {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
background-color: var(--dark-color);
border: 2px solid #688e26;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 24px;
gap: 10px;
}
.message-response.failed {
border: 2px solid #d42222;
}
#homepage-animation-wrapper {
width: 150%;
height: 100%;
background-color: var(--dark-color);
}
.tech-stack-header {
color: #7174ff;
margin-bottom: 3px;
}
.portfolio-image-wrapper {
width: 100%;
height: 300px;
margin-bottom: 15px;
overflow: hidden;
}
.portfolio-image {
width: 100%;
height: 100%;
object-fit: cover;
width: 100%;
height: 300px;
object-position: left top;
margin-bottom: 20px;
}
/* * */
/* * */
/* * */
/* * */
/* * */
/* * */
/* * */
/* * */
span img {
width: 100%;
height: auto;
object-fit: contain;
}
/* * */
/* * */
/* * */
/* * */
/* * */
/* * */
/* * */
/* * */
/* .transition-fade {
transition: 0.4s;
opacity: 1;
}
html.is-animating .transition-fade {
opacity: 0;
} */
/* ###############################################################################################
##################################################################################################
##################################################################################################
##################################################################################################
##################################### -- Mobile Styles -- ########################################
##################################################################################################
##################################################################################################
##################################################################################################
############################################################################################### */
@media (max-width: 1200px) {
#main-image {
position: fixed;
top: 40px;
z-index: -1;
}
}
@media (max-width: 990px) {
.portfolio-entries-block {
grid-template-columns: 1fr;
}
}
@media (max-width: 800px) {
h1 {
font-size: 28px;
}
nav {
flex-wrap: wrap;
gap: 15px;
}
.hero-ctas-section {
flex-wrap: wrap;
}
}
@media (max-width: 600px) {
.portfolio-image-wrapper {
height: auto;
}
.portfolio-image {
object-fit: contain;
height: auto;
margin: 0;
}
}
@media (max-width: 450px) {
}
@media (max-width: 350px) {
}

View File

@ -762,4 +762,4 @@
.xl\:pt-0 {
padding-top: 0px
}
}
}

View File

@ -1,35 +1,35 @@
module.exports = {
content: ["./components/**/*.{html,js,jsx}", "./pages/**/*.{jsx,tsx}", "./app/**/*.{html,js,jsx,tsx}", "./layouts/**/*.{jsx,tsx}"],
theme: {
screens: {
xs: "350px",
sm: "450px",
md: "600px",
sl: "800px",
lg: "976px",
xl: "1200px",
},
colors: {
primary: "#7174ff",
primary_light: "#9d9eff",
primary_dark: "#7072c9",
},
fontFamily: {
sans: ["Graphik", "sans-serif"],
serif: ["Merriweather", "serif"],
},
// extend: {
// spacing: {
// 128: "32rem",
// 144: "36rem",
// },
// borderRadius: {
// "4xl": "2rem",
// },
// },
},
plugins: [],
corePlugins: {
preflight: false,
},
};
module.exports = {
content: ["./components/**/*.{html,js,jsx}", "./pages/**/*.{jsx,tsx}", "./app/**/*.{html,js,jsx,tsx}", "./layouts/**/*.{jsx,tsx}"],
theme: {
screens: {
xs: "350px",
sm: "450px",
md: "600px",
sl: "800px",
lg: "976px",
xl: "1200px",
},
colors: {
primary: "#7174ff",
primary_light: "#9d9eff",
primary_dark: "#7072c9",
},
fontFamily: {
sans: ["Graphik", "sans-serif"],
serif: ["Merriweather", "serif"],
},
// extend: {
// spacing: {
// 128: "32rem",
// 144: "36rem",
// },
// borderRadius: {
// "4xl": "2rem",
// },
// },
},
plugins: [],
corePlugins: {
preflight: false,
},
};

View File

@ -1,3 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@tailwind base;
@tailwind components;
@tailwind utilities;

View File

@ -1,14 +1,14 @@
{
"rewrites": [
{
"source": "/api/:path*",
"destination": "/api/:path*",
"headers": [
{ "key": "Access-Control-Allow-Credentials", "value": "true" },
{ "key": "Access-Control-Allow-Origin", "value": "*" },
{ "key": "Access-Control-Allow-Methods", "value": "GET,OPTIONS,PATCH,DELETE,POST,PUT" },
{ "key": "Access-Control-Allow-Headers", "value": "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version" }
]
}
]
}
{
"rewrites": [
{
"source": "/api/:path*",
"destination": "/api/:path*",
"headers": [
{ "key": "Access-Control-Allow-Credentials", "value": "true" },
{ "key": "Access-Control-Allow-Origin", "value": "*" },
{ "key": "Access-Control-Allow-Methods", "value": "GET,OPTIONS,PATCH,DELETE,POST,PUT" },
{ "key": "Access-Control-Allow-Headers", "value": "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version" }
]
}
]
}