Skip to content

Commit

Permalink
Merge pull request #649 from hngprojects/Vxrcel-clean
Browse files Browse the repository at this point in the history
  • Loading branch information
Prudent Bird authored Jul 24, 2024
2 parents b02f2bd + f2850c4 commit acdc71e
Show file tree
Hide file tree
Showing 48 changed files with 403 additions and 2 deletions.
Binary file added public/404.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Sidebar from ".";
import { Box, House, Mail, Settings, Users } from "lucide-react";

import { render, screen } from "~/test/utils";
import Sidebar from "./";

const sideItems = [
{
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import HtmlTemplateViewer from "./_component.tsx";
import HtmlTemplateViewer from "./_component.tsx/index.jsx";

const page = () => {
return <HtmlTemplateViewer />;
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
1 change: 1 addition & 0 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
--default-foreground: 0 0% 100%;

--primary: 25 95% 53%;

--primary-foreground: 0 0% 100%;

--secondary: 210 40% 96.1%;
Expand Down
52 changes: 52 additions & 0 deletions src/app/not-found.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"use client";

import { ArrowLeft, Home } from "lucide-react";
import Image from "next/image";
import Link from "next/link";
import { useRouter } from "next/navigation";

import Particles404 from "~/components/error/Particles404";

const NotFoundPage = () => {
const router = useRouter();

return (
<section className="grid h-screen w-full place-items-center bg-white">
<div className="fixed left-0 top-0 min-h-[100dvh] w-screen bg-white" />
<Particles404 />
<div className="pointer-events-none relative z-30 flex flex-col gap-y-6">
<p className="text-center font-medium uppercase text-[#f97415] sm:text-2xl md:text-3xl lg:text-4xl lg:font-bold xl:font-bold">
Page Not Found
</p>
<Image
src="/404.gif"
alt="404"
width={480}
height={204}
unoptimized
loading="eager"
priority
/>
<div className="flex w-full items-center justify-center gap-x-4">
<button
onClick={() => router.back()}
className="hover:text-accent-color pointer-events-auto flex items-center gap-x-2 rounded-xl border border-gray-200 bg-white px-4 py-2 transition-colors duration-300"
>
<ArrowLeft className="size-5 xl:size-6" />
Back
</button>
<Link
href="/"
prefetch
className="hover:text-accent-color pointer-events-auto flex items-center gap-x-2 rounded-xl border border-gray-200 bg-white px-4 py-2 transition-colors duration-300"
>
<Home className="size-5 xl:size-6" />
Home
</Link>
</div>
</div>
</section>
);
};

export default NotFoundPage;
51 changes: 51 additions & 0 deletions src/components/error/Particles404.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React, { useEffect } from "react";

import useWindowWidth from "~/hooks/use-window-width";
import { handleMouseEnter } from "~/lib/utils";
import { particlesCanvas } from "./particles";

const Particles404 = () => {
const canvaReference = React.useRef<HTMLCanvasElement>(null);
const { winWidth } = useWindowWidth();

useEffect(() => {
setTimeout(() => {
if (!canvaReference.current) return;
particlesCanvas(canvaReference.current);
}, 500);
}, [winWidth]);
return (
<div className="absolute left-0 top-0 mx-auto h-full w-full max-w-[1440px] overflow-hidden">
<canvas
ref={canvaReference}
id="particles_404"
className="absolute left-0 top-0 h-[100dvh] w-full"
/>
<header className="absolute left-0 top-0 w-full bg-white px-10 py-8 font-medium uppercase text-[#f97415] md:text-3xl">
<h1
className=""
// @ts-expect-error Hacking the type
onMouseEnter={handleMouseEnter}
data-value="HNG-BOILERPLATE"
>
Hng-boilerplate
</h1>
</header>
<div className="absolute bottom-10 left-10 font-sans text-2xl uppercase">
<p
className="font-bold text-[#f97415] md:text-6xl lg:text-9xl xl:text-[10rem]"
// @ts-expect-error Hacking the type
onMouseEnter={handleMouseEnter}
data-value="404!!"
>
404
</p>
<p className="font-medium md:text-3xl lg:text-4xl">
This is not the page <br /> you are looking for
</p>
</div>
</div>
);
};

export default Particles404;
152 changes: 152 additions & 0 deletions src/components/error/particles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
interface Mouse {
radius: number;
x: number;
y: number;
}

export function particlesCanvas(canvasElement: HTMLCanvasElement) {
const context = canvasElement.getContext("2d");

const canvas = canvasElement;
canvas.width = window.innerWidth * window.devicePixelRatio;
canvas.height = window.innerHeight * window.devicePixelRatio;

canvas.style.width = `${window.innerWidth}px`;
canvas.style.height = `${window.innerHeight}px`;

// Particles Class
class Particle {
private originX: number;
private originY: number;
private x: number;
private y: number;
private effect: Effect;
private ctx: CanvasRenderingContext2D;
private vx: number;
private vy: number;
private ease: number;
private friction: number;
private dx: number;
private dy: number;
private distance: number;
private force: number;
private angle: number;
private size: number;
public spread: number;

constructor(x: number, y: number, effect: Effect) {
this.originX = x;
this.originY = y;
this.effect = effect;
this.x = Math.floor(x);
this.y = Math.floor(y);
this.ctx = effect.ctx;
this.ctx.fillStyle = "#f97415";
this.vx = 0;
this.vy = 0;
this.ease = 0.05;
this.friction = 0.9;
this.dx = 0;
this.dy = 0;
this.distance = 0;
this.force = 0;
this.angle = 0;
// this.size = Math.floor(Math.random() * 5 + 2);
this.size = 3;
this.spread = 20;
this.draw();
}
draw() {
this.ctx.beginPath();
this.ctx.fillRect(this.x, this.y, this.size, this.size);
}
update() {
this.dx = this.effect.mouse.x - this.x;
this.dy = this.effect.mouse.y - this.y;
this.distance = this.dx * this.dx + this.dy * this.dy;
this.force = (-this.effect.mouse.radius / this.distance) * this.spread;

if (this.distance < this.effect.mouse.radius) {
this.angle = Math.atan2(this.dy, this.dx);
this.vx += this.force * Math.cos(this.angle);
this.vy += this.force * Math.sin(this.angle);
}

this.x +=
(this.vx *= this.friction) + (this.originX - this.x) * this.ease;
this.y +=
(this.vy *= this.friction) + (this.originY - this.y) * this.ease;
this.draw();
}
}

// Effect class
class Effect {
private width: number;
private height: number;
private gap: number;
public ctx: CanvasRenderingContext2D;
private particlesArray: Particle[];
public readonly mouse: Mouse;
constructor(
width: number,
height: number,
context: CanvasRenderingContext2D,
) {
this.width = width;
this.height = height;
this.ctx = context;
this.particlesArray = [];
this.gap = 20;
this.mouse = {
radius: 3000,
x: 0,
y: 0,
};

window.addEventListener("mousemove", (event) => {
this.mouse.x = event.clientX * window.devicePixelRatio;
this.mouse.y = event.pageY * window.devicePixelRatio;
this.mouse.radius = 3000;
});
window.addEventListener("mouseout", () => {
this.mouse.x = 0;
this.mouse.y = 0;
this.mouse.radius = 0;
});
window.addEventListener("resize", () => {
canvas.width = window.innerWidth * window.devicePixelRatio;
canvas.height = window.innerHeight * window.devicePixelRatio;
this.width = canvas.width;
this.height = canvas.height;

canvas.style.width = `${window.innerWidth}px`;
canvas.style.height = `${window.innerHeight}px`;

this.particlesArray = [];
});

this.init();
}

init() {
for (let x = 0; x < this.width; x += this.gap) {
for (let y = 0; y < this.height; y += this.gap) {
this.particlesArray.push(new Particle(x, y, this));
}
}
}
update() {
this.ctx.clearRect(0, 0, this.width, this.height);
for (let index = 0; index < this.particlesArray.length; index++) {
this.particlesArray[index].update();
}
}
}
const effect = new Effect(canvas.width, canvas.height, context!);
function animate() {
effect.update();
requestAnimationFrame(animate);
}
animate();
}
47 changes: 47 additions & 0 deletions src/lib/routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* An array of routes that are accessible to the public
* These routes do not require authentication
* @type {string[]}
*/
export const publicRoutes = [
"/",
"/career",
"/faqs",
"privacy-policy",
"/terms",
"/about-us",
"/pricing",
"/contact-us",
"/waitlist",
"/blog",
];

export const authRoutes = [
"/sign-up",
"/login",
"/forgot-password",
"/reset-password",
"/verify-otp",
"/recovery",
];

export const apiAuthPrefix = "/api/";

/**
* The default redirect after login
* @type {string}
*/
export const DEFAULT_LOGIN_REDIRECT = "/dashboard";

/**
* An array of routes that are accessible to the client
* These routes require authentication
* @type {string[]}
*/
export const clientRoutes = [
"/dashboard",
"/hisory",
"messages",
"/notifications",
"/profile",
];
48 changes: 48 additions & 0 deletions src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,51 @@ export function formatPrice(
}).format(numericPrice);
return newPrice;
}

const letters = "!ABCDEFGHIJKLMNOPQRSTUVWXYZ#";

export const handleMouseEnter = (event: MouseEvent) => {
if (!event) return;
const element = event.target as HTMLDivElement;
let iteration: number = 0;
const speed: number = element.dataset.value!.length > 7 ? 30 : 60;

let lastTimestamp: number;
let animationFrameId: number | null;

const animate = (timestamp: number) => {
if (!lastTimestamp) {
lastTimestamp = timestamp;
}

// deltaTime is the time elapsed since the last animation frame
// I use am to reduce or increase speed
const deltaTime = timestamp - lastTimestamp;

if (deltaTime >= speed) {
// @ts-expect-error Hacking the type
element!.textContent! = [...element!.textContent!]
.map((_: string, index: number) => {
if (index < iteration) {
return element.dataset.value![index];
}

return letters[Math.floor(Math.random() * letters.length)];
})
.join("");

if (iteration >= element.dataset.value!.length) {
// Stop the animation if completed
return;
}

iteration += 1 / 3;
lastTimestamp = timestamp;
}

animationFrameId = requestAnimationFrame(animate);
};

cancelAnimationFrame(animationFrameId!);
animationFrameId = requestAnimationFrame(animate);
};
Loading

0 comments on commit acdc71e

Please sign in to comment.