Firist commit
This commit is contained in:
6
.env
Normal file
6
.env
Normal file
@@ -0,0 +1,6 @@
|
||||
NEXTAUTH_SECRET=v7cl92wK4Qdrdr1Jrr0JVl1qna4rIxFsY+T7X+8w0wM=
|
||||
NEXTAUTH_URL=https://3000.vscode.home.joemonk.co.uk
|
||||
|
||||
NEXT_COGNITO_CLIENT_ID=6lt3p9f2puu583pso84b9b1asu
|
||||
NEXT_COGNITO_CLIENT_SECRET=7mv13jembkcimd2qavlsqci5kgtihqhcvned5r54b46qpusl8o4
|
||||
NEXT_COGNITO_ISSUER=https://cognito-idp.eu-west-2.amazonaws.com/eu-west-2_qwrNOlWgg
|
||||
68
.eslintrc.json
Normal file
68
.eslintrc.json
Normal file
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"project": "./tsconfig.json"
|
||||
},
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"extends": [
|
||||
"plugin:import/recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:@typescript-eslint/recommended-requiring-type-checking",
|
||||
"next/core-web-vitals"
|
||||
],
|
||||
"rules": {
|
||||
"semi": [
|
||||
"error",
|
||||
"always"
|
||||
],
|
||||
"arrow-spacing": [
|
||||
"error",
|
||||
{
|
||||
"before": true,
|
||||
"after": true
|
||||
}
|
||||
],
|
||||
"block-spacing": [
|
||||
"error"
|
||||
],
|
||||
"semi-spacing": [
|
||||
"error"
|
||||
],
|
||||
"computed-property-spacing": [
|
||||
"error"
|
||||
],
|
||||
"comma-spacing": [
|
||||
"error"
|
||||
],
|
||||
"keyword-spacing": [
|
||||
"error"
|
||||
],
|
||||
"func-call-spacing": [
|
||||
"error"
|
||||
],
|
||||
"template-curly-spacing": [
|
||||
"error"
|
||||
],
|
||||
"array-bracket-spacing": [
|
||||
"error"
|
||||
],
|
||||
"@typescript-eslint/explicit-function-return-type": [
|
||||
"error"
|
||||
],
|
||||
"indent": [
|
||||
"error",
|
||||
2
|
||||
],
|
||||
"@typescript-eslint/no-inferrable-types": [
|
||||
"off"
|
||||
],
|
||||
"@typescript-eslint/no-empty-function": [
|
||||
"off"
|
||||
],
|
||||
"jsx-a11y/alt-text": [
|
||||
"off"
|
||||
]
|
||||
}
|
||||
}
|
||||
36
.gitignore
vendored
Normal file
36
.gitignore
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
.yarn/install-state.gz
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"editor.tabSize": 2,
|
||||
}
|
||||
63
Dockerfile
Normal file
63
Dockerfile
Normal file
@@ -0,0 +1,63 @@
|
||||
FROM node:18-alpine AS base
|
||||
|
||||
# Install dependencies only when needed
|
||||
FROM base AS deps
|
||||
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
|
||||
RUN apk add --no-cache libc6-compat
|
||||
WORKDIR /app
|
||||
|
||||
# Install dependencies based on the preferred package manager
|
||||
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
|
||||
RUN \
|
||||
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
|
||||
elif [ -f package-lock.json ]; then npm ci; \
|
||||
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \
|
||||
else echo "Lockfile not found." && exit 1; \
|
||||
fi
|
||||
|
||||
|
||||
# Rebuild the source code only when needed
|
||||
FROM base AS builder
|
||||
WORKDIR /app
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY . .
|
||||
|
||||
ENV NEXT_TELEMETRY_DISABLED 1
|
||||
|
||||
RUN \
|
||||
if [ -f yarn.lock ]; then yarn run build; \
|
||||
elif [ -f package-lock.json ]; then npm run build; \
|
||||
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \
|
||||
else echo "Lockfile not found." && exit 1; \
|
||||
fi
|
||||
|
||||
# Production image, copy all the files and run next
|
||||
FROM base AS runner
|
||||
WORKDIR /app
|
||||
|
||||
ENV NODE_ENV production
|
||||
ENV NEXT_TELEMETRY_DISABLED 1
|
||||
|
||||
RUN addgroup --system --gid 1001 nodejs
|
||||
RUN adduser --system --uid 1001 nextjs
|
||||
|
||||
COPY --from=builder /app/public ./public
|
||||
|
||||
# Set the correct permission for prerender cache
|
||||
RUN mkdir .next
|
||||
RUN chown nextjs:nodejs .next
|
||||
|
||||
# Automatically leverage output traces to reduce image size
|
||||
# https://nextjs.org/docs/advanced-features/output-file-tracing
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||
|
||||
USER nextjs
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
ENV PORT 3000
|
||||
|
||||
# server.js is created by next build from the standalone output
|
||||
# https://nextjs.org/docs/pages/api-reference/next-config-js/output
|
||||
CMD HOSTNAME="0.0.0.0" node server.js
|
||||
36
README.md
Normal file
36
README.md
Normal file
@@ -0,0 +1,36 @@
|
||||
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
yarn dev
|
||||
# or
|
||||
pnpm dev
|
||||
# or
|
||||
bun dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
|
||||
|
||||
This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
||||
8
next.config.mjs
Normal file
8
next.config.mjs
Normal file
@@ -0,0 +1,8 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
swcMinify: true,
|
||||
reactStrictMode: true,
|
||||
output: "standalone"
|
||||
};
|
||||
|
||||
export default nextConfig;
|
||||
5532
package-lock.json
generated
Normal file
5532
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
37
package.json
Normal file
37
package.json
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"name": "next-portfolio",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"build:analyse": "ANALYZE=true npm run build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"lint:fix": "next lint -- --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@headlessui/react": "^1.7.18",
|
||||
"@heroicons/react": "^2.1.3",
|
||||
"@next/bundle-analyzer": "^14.1.4",
|
||||
"@tailwindcss/typography": "^0.5.12",
|
||||
"@types/node": "^20.11.30",
|
||||
"@types/react": "^18.2.73",
|
||||
"@types/react-dom": "^18.2.23",
|
||||
"@typescript-eslint/eslint-plugin": "^7.4.0",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-next": "14.1.4",
|
||||
"framer-motion": "^11.0.23",
|
||||
"next": "14.1.4",
|
||||
"next-auth": "^4.24.7",
|
||||
"postcss": "^8.4.38",
|
||||
"react": "^18.2.0",
|
||||
"react-datocms": "^5.0.3",
|
||||
"react-dom": "^18.2.0",
|
||||
"server-only": "^0.0.1",
|
||||
"tailwind-scrollbar": "^3.1.0",
|
||||
"tailwindcss": "^3.4.3",
|
||||
"typescript": "^5.4.3"
|
||||
}
|
||||
}
|
||||
6
postcss.config.js
Normal file
6
postcss.config.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
1
public/next.svg
Normal file
1
public/next.svg
Normal file
@@ -0,0 +1 @@
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
1
public/vercel.svg
Normal file
1
public/vercel.svg
Normal file
@@ -0,0 +1 @@
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 629 B |
7
src/app/api/auth/[...nextauth]/route.ts
Normal file
7
src/app/api/auth/[...nextauth]/route.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import NextAuth from "next-auth";
|
||||
import { authConfig } from "@/lib/auth";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const handler = NextAuth(authConfig);
|
||||
|
||||
export { handler as GET, handler as POST };
|
||||
15
src/app/api/preview/route.ts
Normal file
15
src/app/api/preview/route.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { auth } from '@/lib/auth';
|
||||
import { revalidateTag } from 'next/cache';
|
||||
import { draftMode } from 'next/headers';
|
||||
import { redirect } from 'next/navigation';
|
||||
|
||||
export async function GET(): Promise<Response> {
|
||||
const session = await auth();
|
||||
if (session) {
|
||||
draftMode().enable();
|
||||
revalidateTag('datocms');
|
||||
} else if (draftMode().isEnabled) {
|
||||
draftMode().disable();
|
||||
}
|
||||
redirect('/');
|
||||
}
|
||||
5
src/app/api/status/route.ts
Normal file
5
src/app/api/status/route.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
export function GET(): Response {
|
||||
return NextResponse.json({ status: 200 }, { status: 200 });
|
||||
}
|
||||
BIN
src/app/favicon.ico
Normal file
BIN
src/app/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
10
src/app/globals.css
Normal file
10
src/app/globals.css
Normal file
@@ -0,0 +1,10 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer utilities {
|
||||
.text-balance {
|
||||
text-wrap: balance;
|
||||
}
|
||||
}
|
||||
|
||||
34
src/app/layout.tsx
Normal file
34
src/app/layout.tsx
Normal file
@@ -0,0 +1,34 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Inter } from "next/font/google";
|
||||
import "./globals.css";
|
||||
|
||||
import NavBar from '@/components/navbar';
|
||||
import Footer from '@/components/footer';
|
||||
|
||||
const inter = Inter({
|
||||
subsets: ['latin'],
|
||||
variable: '--font-inter',
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Create Next App",
|
||||
description: "Generated by create next app",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>): JSX.Element {
|
||||
return (
|
||||
<html className={`${inter.variable} font-sans`} lang="en">
|
||||
<body className="min-h-screen flex flex-col bg-dracula-bg">
|
||||
<NavBar/>
|
||||
<main className="px-6 py-4 w-full mx-auto flex-1 align-middle lg:max-w-5xl">
|
||||
{children}
|
||||
</main>
|
||||
<Footer/>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
31
src/app/page.tsx
Normal file
31
src/app/page.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
export default function Home(): JSX.Element {
|
||||
const len = Array.from({length: 0});
|
||||
return (
|
||||
<div>
|
||||
{len.map(() =>
|
||||
<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>
|
||||
);
|
||||
}
|
||||
19
src/components/auth/login.tsx
Normal file
19
src/components/auth/login.tsx
Normal file
@@ -0,0 +1,19 @@
|
||||
import { auth } from "@/lib/auth";
|
||||
import { LoginButton } from "./login_button";
|
||||
import { LogoutButton } from "./logout_button";
|
||||
|
||||
/**
|
||||
* This is a server component, then the buttons are client
|
||||
*/
|
||||
export default async function LogIn(): Promise<JSX.Element | undefined> {
|
||||
const session = await auth();
|
||||
if (session) {
|
||||
return (
|
||||
<LogoutButton/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<LoginButton/>
|
||||
);
|
||||
}
|
||||
}
|
||||
12
src/components/auth/login_button.tsx
Normal file
12
src/components/auth/login_button.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
"use client";
|
||||
import { UserCircleIcon } from '@heroicons/react/24/outline';
|
||||
import { signIn } from "next-auth/react";
|
||||
|
||||
export function LoginButton(): JSX.Element {
|
||||
return (
|
||||
<button className="p-1 hover:bg-dracula-bglight rounded-3xl transition-colors group" onClick={() => void signIn('cognito')}>
|
||||
<UserCircleIcon className='stroke-dracula-cyan h-8 w-auto group-hover:stroke-dracula-orange transition-colors'/>
|
||||
<span className="sr-only">Log in</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
12
src/components/auth/logout_button.tsx
Normal file
12
src/components/auth/logout_button.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
"use client";
|
||||
import { UserCircleIcon } from '@heroicons/react/24/outline';
|
||||
import { signOut } from "next-auth/react";
|
||||
|
||||
export function LogoutButton(): JSX.Element {
|
||||
return (
|
||||
<button className="p-1 hover:bg-dracula-bglight rounded-3xl transition-colors group" onClick={() => void signOut()}>
|
||||
<UserCircleIcon className='stroke-dracula-cyan h-8 w-auto group-hover:stroke-dracula-red transition-colors'/>
|
||||
<span className="sr-only">Log out</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
11
src/components/footer.tsx
Normal file
11
src/components/footer.tsx
Normal file
@@ -0,0 +1,11 @@
|
||||
export default function NavBar(): JSX.Element {
|
||||
return (
|
||||
<footer className="bg-dracula-bg-darker border-t-2 border-dracula-purple">
|
||||
<div className="mx-auto max-w-7xl px-4">
|
||||
<div className="relative flex flex-row-reverse h-12 items-center justify-between">
|
||||
<span className='text-white select-none'>© Joe Monk 2024</span>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
84
src/components/navbar.tsx
Normal file
84
src/components/navbar.tsx
Normal file
@@ -0,0 +1,84 @@
|
||||
'use client';
|
||||
import { useState } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { HomeModernIcon, Bars3Icon, XMarkIcon, UserCircleIcon } from '@heroicons/react/24/outline';
|
||||
import { AnimatePresence, m, LazyMotion, domAnimation } from "framer-motion";
|
||||
import LogIn from './auth/login';
|
||||
|
||||
const navigation = [
|
||||
{ name: 'Blog', href: '#', current: true },
|
||||
{ name: 'Projects', href: '#', current: false },
|
||||
{ name: 'Photos', href: '#', current: false },
|
||||
{ name: 'CV', href: '#', current: false },
|
||||
{ name: 'Contact', href: '#', current: false },
|
||||
];
|
||||
|
||||
export default function NavBar(): JSX.Element {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<nav className="bg-dracula-bg-darker border-b-2 border-dracula-purple">
|
||||
<LazyMotion features={domAnimation}>
|
||||
<div className="mx-auto max-w-7xl px-4">
|
||||
<div className="relative flex h-16 items-center justify-between">
|
||||
<div className="flex">
|
||||
<button className='sm:hidden hover:bg-dracula-bglight transition-colors duration-100 rounded-sm p-1' onClick={() => setOpen(!open)}>
|
||||
{open ? (
|
||||
<XMarkIcon className='rounded-sm stroke-dracula-cyan h-8 w-auto'/>
|
||||
) : (
|
||||
<Bars3Icon className='rounded-sm stroke-dracula-cyan h-8 w-auto'/>
|
||||
)}
|
||||
</button>
|
||||
<Link className='hidden sm:flex items-center p-1 hover:bg-dracula-bglight transition-colors' href='/'>
|
||||
<HomeModernIcon className='stroke-dracula-cyan rounded-sm h-8 w-auto'/>
|
||||
</Link>
|
||||
<div className='space-x-5 hidden sm:flex ml-10'>
|
||||
{navigation.map((item) => (
|
||||
<Link
|
||||
key={item.name}
|
||||
href={item.href}
|
||||
className={`hover:bg-dracula-bglight transition-colors duration-100 text-white rounded-sm px-3 pt-2 pb-1.5 font-normal border-b-2 border-transparent ${
|
||||
item.current ? 'border-b-dracula-pink' : ''
|
||||
}`}
|
||||
aria-current={item.current ? 'page' : undefined}
|
||||
>
|
||||
{item.name}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
</div>
|
||||
<LogIn/>
|
||||
</div>
|
||||
</div>
|
||||
<AnimatePresence>
|
||||
{ open ? (
|
||||
<m.div
|
||||
initial={{ height: 0 }}
|
||||
animate={{ height: "auto" }}
|
||||
transition={{ duration: 0.15, ease: 'linear' }}
|
||||
exit={{ height: 0 }}
|
||||
className='sm:hidden overflow-hidden'
|
||||
>
|
||||
<div className='flex flex-col space-y-1 py-1'>
|
||||
{navigation.map((item) => (
|
||||
<Link
|
||||
key={item.name}
|
||||
href={item.href}
|
||||
className={`hover:bg-dracula-bglight transition-colors duration-100 text-white px-2 py-2 font-normal border-l-4 border-transparent ${
|
||||
item.current ? 'border-l-dracula-pink' : ''
|
||||
}`}
|
||||
aria-current={item.current ? 'page' : undefined}
|
||||
>
|
||||
{item.name}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</m.div>
|
||||
) : null}
|
||||
</AnimatePresence>
|
||||
</LazyMotion>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
26
src/lib/auth.ts
Normal file
26
src/lib/auth.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import 'server-only';
|
||||
import { getServerSession } from "next-auth";
|
||||
import CognitoProvider from "next-auth/providers/cognito";
|
||||
import type { GetServerSidePropsContext, NextApiRequest, NextApiResponse } from "next";
|
||||
import type { NextAuthOptions, Session } from "next-auth";
|
||||
|
||||
export const authConfig = {
|
||||
secret: process.env.NEXT_AUTH_SECRET,
|
||||
providers: [
|
||||
CognitoProvider({
|
||||
clientId: process.env.NEXT_COGNITO_CLIENT_ID!,
|
||||
clientSecret: process.env.NEXT_COGNITO_CLIENT_SECRET!,
|
||||
issuer: process.env.NEXT_COGNITO_ISSUER,
|
||||
})
|
||||
],
|
||||
callbacks: {
|
||||
redirect(): string {
|
||||
return '/api/preview';
|
||||
}
|
||||
}
|
||||
} satisfies NextAuthOptions;
|
||||
|
||||
// Use it in server contexts
|
||||
export function auth(...args: [GetServerSidePropsContext["req"], GetServerSidePropsContext["res"]] | [NextApiRequest, NextApiResponse] | []): Promise<Session | null> {
|
||||
return getServerSession(...args, authConfig);
|
||||
}
|
||||
41
tailwind.config.ts
Normal file
41
tailwind.config.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import type { Config } from "tailwindcss";
|
||||
|
||||
const config: Config = {
|
||||
content: [
|
||||
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: {
|
||||
sans: ['var(--font-inter)'],
|
||||
mono: ['var(--font-roboto-mono)'],
|
||||
},
|
||||
colors: {
|
||||
// Nicked from the vs code version of the theme https://github.com/dracula/visual-studio-code/blob/master/src/dracula.yml
|
||||
dracula: {
|
||||
'bglighter': '#424450',
|
||||
'bglight': '#343746',
|
||||
'bg': '#282A36',
|
||||
'bg-dark': '#21222C',
|
||||
'bg-darker': '#191A21',
|
||||
'selection': '#44475A',
|
||||
'comment': '#6272A4',
|
||||
'cyan': '#8BE9FD',
|
||||
'green': '#50FA7B',
|
||||
'orange': '#FFB86C',
|
||||
'pink': '#FF79C6',
|
||||
'purple': '#BD93F9',
|
||||
'red': '#FF5555',
|
||||
'yellow': '#F1FA8C',
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
require('@tailwindcss/typography'),
|
||||
require('tailwind-scrollbar')({ nocompatible: true })
|
||||
],
|
||||
};
|
||||
export default config;
|
||||
39
tsconfig.json
Normal file
39
tsconfig.json
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user