Oops probably should've committed, not even sure what's changed. Set up MDX, set up cv and print, set up dark mode, look at react query
This commit is contained in:
15
src/app/(root)/cv/page.tsx
Normal file
15
src/app/(root)/cv/page.tsx
Normal file
@@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
import Cv from '@/components/cv';
|
||||
|
||||
export default function CvPage(): React.JSX.Element {
|
||||
return (
|
||||
<div>
|
||||
<div className='flex flex-row justify-center pb-4'>
|
||||
<button className='py-2 px-4 border'>
|
||||
Download
|
||||
</button>
|
||||
</div>
|
||||
<Cv/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
21
src/app/(root)/layout.tsx
Normal file
21
src/app/(root)/layout.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import "../globals.css";
|
||||
|
||||
import NavBar from '@/components/navbar';
|
||||
import Footer from '@/components/footer';
|
||||
import LogIn from "@/components/auth/login";
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>): React.JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<NavBar LogIn={<LogIn/>}/>
|
||||
<main className="px-6 py-4 w-full mx-auto flex-1 align-middle lg:max-w-5xl">
|
||||
{children}
|
||||
</main>
|
||||
<Footer/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
import Sim from '@/components/sim';
|
||||
import HomeMdx from '@/markdown/page.mdx';
|
||||
|
||||
export default function Home(): React.JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<Sim/>
|
||||
<HomeMdx/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
32
src/app/(root)/photos/page.tsx
Normal file
32
src/app/(root)/photos/page.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import Image from "next/image";
|
||||
import Lightbox from "@/components/lightbox";
|
||||
import { type GetPhotos } from "@/app/api/photos/route";
|
||||
|
||||
async function getImageData(): Promise<GetPhotos> {
|
||||
const res = await fetch(`http://localhost:3000/api/photos`, { next: { revalidate: false, tags: ['photos'] } });
|
||||
console.log(res);
|
||||
return res.json() as Promise<GetPhotos>;
|
||||
}
|
||||
|
||||
export default async function Photos(): Promise<React.JSX.Element> {
|
||||
const {data: imageData} = await getImageData();
|
||||
|
||||
return (
|
||||
<Lightbox imageData={imageData.images}>
|
||||
{imageData.images.map((image) => (
|
||||
<Image
|
||||
key={image.src}
|
||||
alt={image.src}
|
||||
src={image.src}
|
||||
className="object-contain h-60 w-80"
|
||||
sizes="100vw"
|
||||
loading="lazy"
|
||||
width={image.width}
|
||||
height={image.height}
|
||||
blurDataURL={image.blur}
|
||||
placeholder="blur"
|
||||
/>
|
||||
))}
|
||||
</Lightbox>
|
||||
);
|
||||
}
|
||||
32
src/app/(root)/posts/[...slug]/page.tsx
Normal file
32
src/app/(root)/posts/[...slug]/page.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import { glob } from "glob";
|
||||
|
||||
// type postMdx = {
|
||||
// metadata: {
|
||||
// title: string,
|
||||
// date: string,
|
||||
// coverImage: string,
|
||||
// blurb: string,
|
||||
// shortBlurb: string,
|
||||
// tags: string[]
|
||||
// }
|
||||
// }
|
||||
|
||||
export async function generateStaticParams(): Promise<{slug: string[]}[]> {
|
||||
const posts = await glob(`src/markdown/posts/[...slug]/**/*.mdx`, {
|
||||
nodir: true,
|
||||
});
|
||||
|
||||
const slugs = posts.map((post) => ({
|
||||
slug: post.replace('src/markdown/posts/[...slug]/', '').replace(/\.mdx$/, '').split('/')
|
||||
}));
|
||||
|
||||
return slugs;
|
||||
}
|
||||
|
||||
export default function Post({params}: {params: { slug: string[] }}): React.JSX.Element {
|
||||
return (
|
||||
<>
|
||||
{params.slug}
|
||||
</>
|
||||
);
|
||||
}
|
||||
9
src/app/(root)/posts/layout.tsx
Normal file
9
src/app/(root)/posts/layout.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
|
||||
export default function Post({children}: {children: React.JSX.Element}): React.JSX.Element {
|
||||
return (
|
||||
<>
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
}
|
||||
7
src/app/(root)/posts/page.tsx
Normal file
7
src/app/(root)/posts/page.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
export default function Posts(): React.JSX.Element {
|
||||
return (
|
||||
<>
|
||||
Actually this should be custom
|
||||
</>
|
||||
);
|
||||
}
|
||||
9
src/app/(unique)/cv/print/page.tsx
Normal file
9
src/app/(unique)/cv/print/page.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
|
||||
import Cv from '@/components/cv';
|
||||
|
||||
export default function CvPrint(): React.JSX.Element {
|
||||
return (
|
||||
<Cv/>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
import Image from "next/image";
|
||||
import exifReader from "exif-reader";
|
||||
import { glob } from "glob";
|
||||
import sharp from 'sharp';
|
||||
import exifReader from 'exif-reader';
|
||||
import { pick } from 'radash';
|
||||
import Lightbox from "@/components/lightbox";
|
||||
import { NextResponse } from "next/server";
|
||||
import { pick } from "radash";
|
||||
import sharp from "sharp";
|
||||
|
||||
type ImageData = {
|
||||
export type ImageData = {
|
||||
width: number,
|
||||
height: number,
|
||||
blur: `data:image/${string}`,
|
||||
@@ -21,12 +20,19 @@ type ImageData = {
|
||||
}>
|
||||
}
|
||||
|
||||
export async function getImages(): Promise<{images: ImageData[]}> {
|
||||
export type GetPhotos = {
|
||||
status: number,
|
||||
data: {
|
||||
images: ImageData[]
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET(): Promise<Response> {
|
||||
const photosGlob = await glob(`public/photos/**/*.{png,jpeg,jpg}`, {
|
||||
nodir: true,
|
||||
});
|
||||
|
||||
const images = photosGlob.map(async (fileName) => {
|
||||
const imageData = photosGlob.map(async (fileName: string) => {
|
||||
const { width, height, exif } = await sharp(fileName).metadata();
|
||||
const blur = await sharp(fileName)
|
||||
.resize({ width: 12, height: 12, fit: 'inside' })
|
||||
@@ -42,30 +48,8 @@ export async function getImages(): Promise<{images: ImageData[]}> {
|
||||
exif: pick(exifData?.Photo ?? {}, ['ExposureBiasValue', 'FNumber', 'ISOSpeedRatings', 'FocalLength', 'DateTimeOriginal', 'LensModel'])
|
||||
};
|
||||
});
|
||||
|
||||
return { images: await Promise.all(images) };
|
||||
}
|
||||
|
||||
export default async function Home(): Promise<React.JSX.Element> {
|
||||
const { images } = await getImages();
|
||||
const images = await Promise.all(imageData);
|
||||
|
||||
return (
|
||||
<Lightbox imageData={images}>
|
||||
{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={image.blur}
|
||||
placeholder={image.blur}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</Lightbox>
|
||||
);
|
||||
}
|
||||
return NextResponse.json<GetPhotos>({ status: 200, data: { images } });
|
||||
}
|
||||
@@ -7,4 +7,3 @@
|
||||
text-wrap: balance;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,10 +2,6 @@ import type { Metadata } from "next";
|
||||
import { Inter } from "next/font/google";
|
||||
import "./globals.css";
|
||||
|
||||
import NavBar from '@/components/navbar';
|
||||
import Footer from '@/components/footer';
|
||||
import LogIn from "@/components/auth/login";
|
||||
|
||||
const inter = Inter({
|
||||
subsets: ['latin'],
|
||||
variable: '--font-inter',
|
||||
@@ -22,13 +18,22 @@ export default function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}>): React.JSX.Element {
|
||||
return (
|
||||
<html className={`${inter.variable} font-sans`} lang="en">
|
||||
<body className="min-h-screen flex flex-col bg-dracula-bg">
|
||||
<NavBar LogIn={<LogIn/>}/>
|
||||
<main className="px-6 py-4 w-full mx-auto flex-1 align-middle lg:max-w-5xl">
|
||||
{children}
|
||||
</main>
|
||||
<Footer/>
|
||||
// Use suppress hydration warnings to add the dark theme class on client
|
||||
<html className={`${inter.variable} font-sans`} lang="en" suppressHydrationWarning>
|
||||
<head>
|
||||
<script id="SetTheme"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
||||
document.documentElement.classList.add('dark');
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
}`,
|
||||
}}>
|
||||
</script>
|
||||
</head>
|
||||
<body className="min-h-screen flex flex-col bg-dracula-bg-lightest dark:bg-dracula-bg print:white">
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user