Add log in, images page, start mum's sim

This commit is contained in:
2024-04-09 01:40:21 +01:00
parent 5293669f40
commit 1e02cb3827
14 changed files with 1396 additions and 309 deletions

View File

@@ -2,7 +2,7 @@
const nextConfig = { const nextConfig = {
swcMinify: true, swcMinify: true,
reactStrictMode: true, reactStrictMode: true,
output: "standalone" output: "standalone",
}; };
export default nextConfig; export default nextConfig;

1423
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -18,20 +18,25 @@
"@types/node": "^20.11.30", "@types/node": "^20.11.30",
"@types/react": "^18.2.73", "@types/react": "^18.2.73",
"@types/react-dom": "^18.2.23", "@types/react-dom": "^18.2.23",
"@typescript-eslint/eslint-plugin": "^7.4.0", "@typescript-eslint/eslint-plugin": "^6.21.0",
"autoprefixer": "^10.4.19", "autoprefixer": "^10.4.19",
"eslint": "^8.57.0", "eslint": "^8.57.0",
"eslint-config-next": "14.1.4", "eslint-config-next": "14.1.4",
"exif-reader": "^2.0.1",
"framer-motion": "^11.0.23", "framer-motion": "^11.0.23",
"glob": "^10.3.12",
"million": "^3.0.6",
"next": "14.1.4", "next": "14.1.4",
"next-auth": "^4.24.7", "next-auth": "^4.24.7",
"postcss": "^8.4.38", "postcss": "^8.4.38",
"radash": "^12.1.0",
"react": "^18.2.0", "react": "^18.2.0",
"react-datocms": "^5.0.3", "react-datocms": "^5.0.3",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"server-only": "^0.0.1", "server-only": "^0.0.1",
"sharp": "^0.33.3",
"tailwind-scrollbar": "^3.1.0", "tailwind-scrollbar": "^3.1.0",
"tailwindcss": "^3.4.3", "tailwindcss": "^3.4.3",
"typescript": "^5.4.3" "typescript": "^5.4.3"
} }
} }

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 283 64"><path fill="black" d="M141 16c-11 0-19 7-19 18s9 18 20 18c7 0 13-3 16-7l-7-5c-2 3-6 4-9 4-5 0-9-3-10-7h28v-3c0-11-8-18-19-18zm-9 15c1-4 4-7 9-7s8 3 9 7h-18zm117-15c-11 0-19 7-19 18s9 18 20 18c6 0 12-3 16-7l-8-5c-2 3-5 4-8 4-5 0-9-3-11-7h28l1-3c0-11-8-18-19-18zm-10 15c2-4 5-7 10-7s8 3 9 7h-19zm-39 3c0 6 4 10 10 10 4 0 7-2 9-5l8 5c-3 5-9 8-17 8-11 0-19-7-19-18s8-18 19-18c8 0 14 3 17 8l-8 5c-2-3-5-5-9-5-6 0-10 4-10 10zm83-29v46h-9V5h9zM37 0l37 64H0L37 0zm92 5-27 48L74 5h10l18 30 17-30h10zm59 12v10l-3-1c-6 0-10 4-10 10v15h-9V17h9v9c0-5 6-9 13-9z"/></svg>

Before

Width:  |  Height:  |  Size: 629 B

View File

@@ -4,6 +4,7 @@ import "./globals.css";
import NavBar from '@/components/navbar'; import NavBar from '@/components/navbar';
import Footer from '@/components/footer'; import Footer from '@/components/footer';
import LogIn from "@/components/auth/login";
const inter = Inter({ const inter = Inter({
subsets: ['latin'], subsets: ['latin'],
@@ -23,7 +24,7 @@ export default function RootLayout({
return ( return (
<html className={`${inter.variable} font-sans`} lang="en"> <html className={`${inter.variable} font-sans`} lang="en">
<body className="min-h-screen flex flex-col bg-dracula-bg"> <body className="min-h-screen flex flex-col bg-dracula-bg">
<NavBar/> <NavBar LogIn={<LogIn/>}/>
<main className="px-6 py-4 w-full mx-auto flex-1 align-middle lg:max-w-5xl"> <main className="px-6 py-4 w-full mx-auto flex-1 align-middle lg:max-w-5xl">
{children} {children}
</main> </main>

View File

@@ -1,31 +1,9 @@
import Sim from '@/components/sim';
export default function Home(): JSX.Element { export default function Home(): JSX.Element {
const len = Array.from({length: 0});
return ( return (
<div> <>
{len.map(() => <Sim/>
<p className='prose prose-invert max-w-none'> </>
Excepteur aliquip voluptate minim quis velit id. Do quis incididunt tempor deserunt consectetur est velit reprehenderit pariatur aliquip mollit elit magna. Do occaecat ex labore tempor laboris ut. Sit aliquip ex consequat in adipisicing duis tempor. Esse adipisicing aliqua exercitation ullamco do. Do nostrud ullamco tempor amet cillum ex Lorem irure culpa proident.
Consequat dolor officia velit non pariatur ea quis duis tempor. Nisi adipisicing elit exercitation tempor aute deserunt consectetur. Labore proident excepteur enim minim ullamco labore. Ad ea minim in ut.
Minim laborum sit laborum commodo esse sint veniam deserunt. Nisi voluptate cupidatat consequat quis aute ex ut nisi enim aliquip consequat. Culpa dolore labore anim ut. Veniam pariatur velit nulla sit culpa.
Dolor incididunt ut consequat nostrud nostrud sit quis nostrud aliquip. Do officia voluptate qui qui officia quis in in laborum reprehenderit adipisicing. Exercitation ex proident cupidatat ut culpa. Ullamco officia deserunt esse commodo sint est ut labore tempor. Pariatur commodo proident voluptate excepteur ipsum velit consequat officia cillum eu nulla tempor duis. Consequat deserunt tempor nostrud quis officia aliqua mollit ea duis veniam.
Consectetur sint est ad voluptate quis nisi adipisicing cillum in. Non nostrud et proident nisi eu ullamco eiusmod et excepteur esse exercitation adipisicing exercitation ex. Dolore sit esse voluptate aliquip non eiusmod ex proident adipisicing voluptate aute sit culpa. Consequat non labore aute consectetur magna. Irure anim tempor aliquip mollit do nisi reprehenderit nulla cupidatat amet.
Ea sunt aliqua proident sit labore quis eiusmod velit adipisicing cillum aute magna. Incididunt ullamco labore cupidatat do veniam pariatur mollit duis dolor velit ut enim. Occaecat voluptate culpa cupidatat nulla veniam dolore quis consequat ea. Cupidatat non commodo occaecat incididunt nostrud eu aute nostrud pariatur velit ullamco aliqua et. Velit culpa qui ipsum ex. Eu irure veniam excepteur eu. Magna Lorem do elit non.
Ut fugiat eiusmod culpa ipsum enim do. In ipsum aute mollit nostrud incididunt laborum do voluptate amet tempor labore aute anim. Ea eiusmod consequat occaecat qui Lorem non esse.
Officia aute et aliqua laborum reprehenderit. Lorem excepteur deserunt dolor eu pariatur consequat veniam. Labore sint fugiat labore aliquip cupidatat cillum adipisicing ullamco eu amet consequat. Reprehenderit et est magna nostrud.
Amet mollit Lorem enim officia voluptate ipsum reprehenderit commodo aliqua adipisicing id culpa deserunt. Consequat nulla tempor tempor nisi deserunt nulla magna dolor duis id nostrud laborum dolor. Exercitation aute anim in aliqua sunt ea laborum anim dolore veniam pariatur. Voluptate enim tempor officia minim excepteur fugiat ullamco.
Do labore Lorem in officia incididunt velit id laborum ut magna pariatur officia cillum. Voluptate dolor ullamco commodo occaecat ex magna ex esse. Aliqua velit sint eu ipsum dolore in incididunt mollit eiusmod voluptate. Pariatur do voluptate adipisicing voluptate anim ad ipsum. Lorem qui nisi officia ullamco sunt duis enim amet ea cillum deserunt quis. Eu sunt sit enim ipsum.
</p>
)}
</div>
); );
} }

68
src/app/photos/page.tsx Normal file
View File

@@ -0,0 +1,68 @@
import Image from "next/image";
import { glob } from "glob";
import sharp from 'sharp';
import exifReader from 'exif-reader';
import { pick } from 'radash';
type ImageData = {
width: number,
height: number,
blur: string,
src: string,
exif: Partial<{
ExposureProgram: number,
ExposureBiasValue: number,
FNumber: number,
ISOSpeedRatings: number,
FocalLength: number,
DateTimeOriginal: Date,
LensModel: string
}>
}
export async function getImages(): Promise<{images: ImageData[]}> {
const photosGlob = await glob(`public/photos/**/*.{png,jpeg,jpg}`, {
nodir: true,
});
const images = photosGlob.map(async (fileName) => {
const { width, height, exif } = await sharp(fileName).metadata();
const blur = await sharp(fileName)
.resize({ width: 10, height: 10, fit: 'inside' })
.toBuffer();
const exifData = exif ? exifReader(exif) : undefined;
return {
width: width ?? 10,
height: height ?? 10,
blur: blur.toString('base64'),
src: fileName.slice(6),
exif: pick(exifData?.Photo ?? {}, ['ExposureProgram', 'ExposureBiasValue', 'FNumber', 'ISOSpeedRatings', 'FocalLength', 'DateTimeOriginal', 'LensModel'])
};
});
return { images: await Promise.all(images) };
}
export default async function Home(): Promise<JSX.Element> {
const { images } = await getImages();
return (
<div className="grid gap-2 grid-cols-3">
{images.map((image) => (
<div className="relative" key={image.src}>
<Image
alt={image.src}
src={image.src}
className="object-contain h-auto w-full"
sizes="(min-width: 808px) 50vw, 100vw"
loading="lazy"
width={image.width}
height={image.height}
blurDataURL={`data:image/jpeg;base64,${image.blur}`}
placeholder={`data:image/jpeg;base64,${image.blur}`}
/>
</div>
))}
</div>
);
}

View File

@@ -3,7 +3,7 @@ import { LoginButton } from "./login_button";
import { LogoutButton } from "./logout_button"; import { LogoutButton } from "./logout_button";
/** /**
* This is a server component, then the buttons are client * This is a server component, then the buttons are client side
*/ */
export default async function LogIn(): Promise<JSX.Element | undefined> { export default async function LogIn(): Promise<JSX.Element | undefined> {
const session = await auth(); const session = await auth();

View File

@@ -1,19 +1,18 @@
'use client'; 'use client';
import { useState } from 'react'; import { useState } from 'react';
import Link from 'next/link'; import Link from 'next/link';
import { HomeModernIcon, Bars3Icon, XMarkIcon, UserCircleIcon } from '@heroicons/react/24/outline'; import { HomeModernIcon, Bars3Icon, XMarkIcon } from '@heroicons/react/24/outline';
import { AnimatePresence, m, LazyMotion, domAnimation } from "framer-motion"; import { AnimatePresence, m, LazyMotion, domAnimation } from "framer-motion";
import LogIn from './auth/login';
const navigation = [ const navigation = [
{ name: 'Blog', href: '#', current: true }, { name: 'Blog', href: '#', current: true },
{ name: 'Projects', href: '#', current: false }, { name: 'Projects', href: '#', current: false },
{ name: 'Photos', href: '#', current: false }, { name: 'Photos', href: '/photos', current: false },
{ name: 'CV', href: '#', current: false }, { name: 'CV', href: '#', current: false },
{ name: 'Contact', href: '#', current: false }, { name: 'Contact', href: '#', current: false },
]; ];
export default function NavBar(): JSX.Element { export default function NavBar({LogIn}: {LogIn: JSX.Element}): JSX.Element {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
return ( return (
@@ -49,7 +48,7 @@ export default function NavBar(): JSX.Element {
</div> </div>
<div> <div>
</div> </div>
<LogIn/> {LogIn}
</div> </div>
</div> </div>
<AnimatePresence> <AnimatePresence>

149
src/components/sim.tsx Normal file
View File

@@ -0,0 +1,149 @@
"use client";
import { shuffle } from "radash";
import { FormEvent, useState, useRef, useCallback, useEffect } from "react";
export default function Sim(): JSX.Element {
const [outputs, setOutputs] = useState<
{ runs: number; tickets: number; totalWin: number }[]
>([]);
const workerRef = useRef<Worker>();
useEffect(() => {
workerRef.current = new Worker(
new URL("../workers/sim.ts", import.meta.url)
);
workerRef.current.onmessage = (event) =>
console.log(`WebWorker Response => ${JSON.stringify(event.data)}`);
return () => {
workerRef.current?.terminate();
};
}, []);
function runCalc(opts: {
prizes: number[];
fee: number;
ticketsAlreadySold: number;
ticketsTotal: number;
}): void {
const totalPrizePool = opts.prizes.reduce((total, prize) => {
return total + prize;
}, 0);
const ticketsToBuy = 1;
const ret =
(totalPrizePool - opts.fee * ticketsToBuy) / ticketsAlreadySold +
ticketsToBuy;
const worth = ret > opts.fee;
return worth;
}
const runSim = (
runs: number,
tickets: number
): Promise<{ runs: number; tickets: number; totalWin: number }> => {
return new Promise((resolve) => {
const allWinnings = [];
for (let i = 0; i < runs; i++) {
let winnings = 0;
const soldTickets = shuffle(
Array.from({ length: tickets }).map((_, index) => index)
);
const prizes = shuffle([500, 350, 150]);
while (prizes.length > 0) {
const drawnTicket = soldTickets.pop();
if (drawnTicket) {
const thisWin = prizes.pop() ?? 0;
if (drawnTicket < tickets) {
winnings += thisWin;
}
}
}
allWinnings.push(winnings);
}
const totalWin = allWinnings.reduce((total, win) => {
return total + win;
}, 0);
resolve({
runs,
tickets,
totalWin,
});
});
};
function onSubmit(event: FormEvent<HTMLFormElement>): void {
event.preventDefault();
const formData = new FormData(event.currentTarget);
const opts = {
prizes: formData.get("prizes"),
prizes: formData.get("fee"),
prizes: formData.get("ticketsAlreadySold"),
prizes: formData.get("ticketsTotal"),
};
const wrapper = async (): Promise<void> => {
if ((runs ?? 0) > 0 && (tickets ?? 0) > 0) {
workerRef.current?.postMessage(opts);
//const output = await runSim(runs, tickets);
//setOutputs([...outputs, output]);
}
};
void wrapper();
}
return (
<>
<form className="text-white" onSubmit={onSubmit}>
<label>Prizes</label>
<br />
<input className="text-black" type="text" name="prizes" />
<br />
<label>Fee</label>
<br />
<input className="text-black" type="text" name="fee" />
<br />
<label>Tickets sold</label>
<br />
<input className="text-black" type="text" name="ticketsAlreadySold" />
<br />
<label>Tickets total</label>
<br />
<input className="text-black" type="text" name="ticketsTotal" />
<br />
<button className="p-2 bg-dracula-bglighter rounded-sm" type="submit">
Run
</button>
<br />
</form>
<br />
{outputs.length ? (
<table className={"text-white"}>
<thead>
<tr>
<th>Runs</th>
<th>Tickets</th>
<th>Winnings</th>
</tr>
</thead>
<tbody>
{outputs.map((output, index) => {
return (
<tr key={index}>
<td>{output.runs}</td>
<td>{output.tickets}</td>
<td>{output.totalWin}</td>
</tr>
);
})}
</tbody>
</table>
) : null}
</>
);
}

View File

@@ -1,4 +1,4 @@
import 'server-only'; import "server-only";
import { getServerSession } from "next-auth"; import { getServerSession } from "next-auth";
import CognitoProvider from "next-auth/providers/cognito"; import CognitoProvider from "next-auth/providers/cognito";
import type { GetServerSidePropsContext, NextApiRequest, NextApiResponse } from "next"; import type { GetServerSidePropsContext, NextApiRequest, NextApiResponse } from "next";

3
src/workers/sim.ts Normal file
View File

@@ -0,0 +1,3 @@
addEventListener('message', (event) => {
postMessage({ received: true, ...event.data });
});

View File

@@ -1,5 +1,6 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ESNext",
"lib": [ "lib": [
"dom", "dom",
"dom.iterable", "dom.iterable",