This commit is contained in:
Damien Ostler 2024-02-10 20:33:24 -05:00
commit 45ddebb984
28 changed files with 5678 additions and 0 deletions

5
.env.local.example Normal file
View File

@ -0,0 +1,5 @@
AUTH0_ISSUER_BASE_URL="https://"
AUTH0_CLIENT_ID=
AUTH0_CLIENT_SECRET=
AUTH0_BASE_URL="http://localhost:3000"
AUTH0_SECRET=

14
.github/workflows/on-pr.yml vendored Normal file
View File

@ -0,0 +1,14 @@
name: on-pr
on: [pull_request]
jobs:
unit-test:
runs-on: [Ubuntu-Latest]
steps:
- uses: actions/checkout@v3
- name: Install modules
run: npm install
- name: Run tests
run: npm run test

49
.github/workflows/on-push.yml vendored Normal file
View File

@ -0,0 +1,49 @@
name: on-pushed
on:
push:
branches: [main]
jobs:
build-image:
runs-on: [ubuntu-latest]
permissions: write-all
env:
docker_image_name: ghcr.io/data443/dim-search-web-ui
steps:
- uses: gittools/actions/gitversion/setup@v0.9.15
with:
versionSpec: '5.x'
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: gittools/actions/gitversion/execute@v0.9.15
with:
useConfigFile: true
configFilePath: GitVersion.yml
- name: login
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login https://ghcr.io -u $ --password-stdin
- name: build
run: docker build -t ${{ env.docker_image_name }}:${{ env.GitVersion_SemVer }} .
- name: tag latest
run: docker tag ${{ env.docker_image_name }}:${{ env.GitVersion_SemVer }} ${{ env.docker_image_name }}:latest
- name: push
run: docker push --all-tags ${{ env.docker_image_name }}
- name: tag branch
run: |
git tag ${{ env.GitVersion_SemVer }}
git push origin ${{ env.GitVersion_SemVer }}
- name: release
uses: softprops/action-gh-release@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
tag_name: ${{ env.GitVersion_SemVer }}
generate_release_notes: true

View File

@ -0,0 +1,20 @@
name: tag-stable-release
on:
release:
types: [published]
jobs:
build-image:
runs-on: [self-hosted, Linux, k8s]
permissions:
packages: write
steps:
- name: login into registry
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login https://ghcr.io -u $ --password-stdin
- name: Tag latest
env:
docker_image_name: ghcr.io/data443/dim-search-web-ui
run: |
docker tag ${{ env.docker_image_name }}:${{ github.ref_name }} ${{ env.docker_image_name }}:stable
docker push --all-tags ${{ env.docker_image_name }}

19
.gitignore vendored Normal file
View File

@ -0,0 +1,19 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# dependencies
/node_modules
# misc
.DS_Store
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Next.js
/.next
#local env files
.env*.local
/coverage

57
Dockerfile Normal file
View File

@ -0,0 +1,57 @@
# Install dependencies only when needed
FROM node:16-alpine 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 yarn global add pnpm && pnpm i; \
else echo "Lockfile not found." && exit 1; \
fi
# Rebuild the source code only when needed
FROM node:16-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Uncomment the following line in case you want to disable telemetry during the build.
ENV NEXT_TELEMETRY_DISABLED 1
RUN yarn build
# If using npm comment out above and use below instead
# RUN npm run build
# Production image, copy all the files and run next
FROM node:16-alpine AS runner
WORKDIR /app
ENV NODE_ENV production
# Uncomment the following line in case you want to disable telemetry during runtime.
ENV NEXT_TELEMETRY_DISABLED 1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
# 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
CMD ["node", "server.js"]

11
GitVersion.yml Normal file
View File

@ -0,0 +1,11 @@
assembly-versioning-scheme: MajorMinorPatch
mode: ContinuousDelivery
branches: {}
ignore:
sha: []
merge-message-formats: {}
commit-message-incrementing: Enabled
major-version-bump-message: "^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test)(\\([\\w\\s-]*\\))?(!:|:.*\\n\\n((.+\\n)+\\n)?BREAKING CHANGE:\\s.+)"
minor-version-bump-message: "^(feat)(\\([\\w\\s-]*\\))?:"
patch-version-bump-message: "^(build|chore|ci|docs|fix|perf|refactor|revert|style|test)(\\([\\w\\s-]*\\))?:"

68
README.md Normal file
View File

@ -0,0 +1,68 @@
# Next.js and Auth0 Example
This example shows how you can use `@auth0/nextjs-auth` to easily add authentication support to your Next.js application. It tries to cover a few topics:
- Signing in
- Signing out
- Loading the user on the server side and adding it as part of SSR ([`pages/advanced/ssr-profile.tsx`](pages/advanced/ssr-profile.tsx))
- Loading the user on the client side and using fast/cached SSR pages ([`pages/index.tsx`](pages/index.tsx))
- Loading the user on the client side and checking authentication CSR pages ([`pages/profile.tsx`](pages/profile.tsx))
- Loading the user on the client side by accessing API (Serverless function) CSR pages ([`pages/advanced/api-profile.tsx`](pages/advanced/api-profile.tsx))
- Creates route handlers under the hood that perform different parts of the authentication flow ([`pages/auth/[...auth0].tsx`](pages/auth/[...auth0].tsx))
Read more: [https://auth0.com/blog/ultimate-guide-nextjs-authentication-auth0/](https://auth0.com/blog/ultimate-guide-nextjs-authentication-auth0/)
## How to use
Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init), [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/), or [pnpm](https://pnpm.io) to bootstrap the example:
```bash
npx create-next-app --example auth0 auth0-app
```
```bash
yarn create next-app --example auth0 auth0-app
```
```bash
pnpm create next-app --example auth0 auth0-app
```
## Configuring Auth0
1. Go to the [Auth0 dashboard](https://manage.auth0.com/) and create a new application of type _Regular Web Applications_ and make sure to configure the following
2. Go to the settings page of the application
3. Configure the following settings:
- _Allowed Callback URLs_: Should be set to `http://localhost:3000/api/auth/callback` when testing locally or typically to `https://myapp.com/api/auth/callback` when deploying your application.
- _Allowed Logout URLs_: Should be set to `http://localhost:3000/` when testing locally or typically to `https://myapp.com/` when deploying your application.
4. Save the settings
### Set up environment variables
To connect the app with Auth0, you'll need to add the settings from your Auth0 application as environment variables
Copy the `.env.local.example` file in this directory to `.env.local` (which will be ignored by Git):
```bash
cp .env.local.example .env.local
```
Then, open `.env.local` and add the missing environment variables:
- `AUTH0_ISSUER_BASE_URL` - Can be found in the Auth0 dashboard under `settings`. (Should be prefixed with `https://`)
- `AUTH0_CLIENT_ID` - Can be found in the Auth0 dashboard under `settings`.
- `AUTH0_CLIENT_SECRET` - Can be found in the Auth0 dashboard under `settings`.
- `AUTH0_BASE_URL` - The base url of the application.
- `AUTH0_SECRET` - Has to be at least 32 characters. You can use [this generator](https://generate-secret.vercel.app/32) to generate a value.
## Deploy on Vercel
You can deploy this app to the cloud with [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).
### Deploy Your Local Project
To deploy your local project to Vercel, push it to GitHub/GitLab/Bitbucket and [import to Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example).
**Important**: When you import your project on Vercel, make sure to click on **Environment Variables** and set them to match your `.env.local` file.

7
additional.d.ts vendored Normal file
View File

@ -0,0 +1,7 @@
export {};
declare global {
interface Window {
__user: any;
}
}

31
components/artist.tsx Normal file
View File

@ -0,0 +1,31 @@
import * as React from 'react';
import ImageListItem from '@mui/material/ImageListItem';
import ImageListItemBar from '@mui/material/ImageListItemBar';
import IconButton from '@mui/material/IconButton';
import InfoIcon from '@mui/icons-material/Info';
import Image from 'next/image';
const Artist = ({artistId}) => {
return (
<ImageListItem key="https://images.unsplash.com/photo-1551963831-b3b1ca40c98e">
<img
srcSet={`https://images.unsplash.com/photo-1551963831-b3b1ca40c98e?w=248&fit=crop&auto=format&dpr=2 2x`}
src={`https://images.unsplash.com/photo-1551963831-b3b1ca40c98e?w=248&fit=crop&auto=format`}
alt={"Neroshi"}
loading="lazy"
/>
<ImageListItemBar
title={artistId}
subtitle={"0 Stars (0 Reviews)"}
actionIcon={
<IconButton
sx={{ color: 'rgba(255, 255, 255, 0.54)' }}
aria-label={`info`}
>
<InfoIcon />
</IconButton>
}
/>
</ImageListItem>)
}
export default Artist

140
components/header.tsx Normal file
View File

@ -0,0 +1,140 @@
import * as React from 'react';
import { useUser } from "@auth0/nextjs-auth0/client";
import AppBar from '@mui/material/AppBar';
import Box from '@mui/material/Box';
import Toolbar from '@mui/material/Toolbar';
import IconButton from '@mui/material/IconButton';
import Typography from '@mui/material/Typography';
import Menu from '@mui/material/Menu';
import MenuIcon from '@mui/icons-material/Menu';
import Container from '@mui/material/Container';
import Avatar from '@mui/material/Avatar';
import Button from '@mui/material/Button';
import Tooltip from '@mui/material/Tooltip';
import MenuItem from '@mui/material/MenuItem';
import Badge from "@mui/material/Badge";
import NotificationsIcon from "@mui/icons-material/Notifications";
import AdbIcon from '@mui/icons-material/Adb';
import { Chip, Icon } from '@mui/material';
import {
NovuProvider,
PopoverNotificationCenter,
NotificationBell,
} from '@novu/notification-center';
type HeaderProps = {
user?: any;
loading: boolean;
};
const settings = ['Profile', 'Account', 'Dashboard', 'Logout'];
function ResponsiveAppBar() {
const [anchorElNav, setAnchorElNav] = React.useState<null | HTMLElement>(null);
const [anchorElUser, setAnchorElUser] = React.useState<null | HTMLElement>(null);
const handleOpenNavMenu = (event: React.MouseEvent<HTMLElement>) => {
setAnchorElNav(event.currentTarget);
};
const handleOpenUserMenu = (event: React.MouseEvent<HTMLElement>) => {
setAnchorElUser(event.currentTarget);
};
const handleCloseNavMenu = () => {
setAnchorElNav(null);
};
const handleCloseUserMenu = () => {
setAnchorElUser(null);
};
const { user, isLoading } = useUser();
return (
<AppBar color="primary" position="static">
<Container maxWidth="xl">
<Toolbar disableGutters>
<Box sx={{ flex:1, textAlign:"center" }}>
<Typography
href="/"
variant="h6"
noWrap
color="secondary"
component="a"
sx={{
mr: 2,
paddingLeft: '1rem',
display: { xs: 'none', md: 'flex' },
fontFamily: 'monospace',
fontWeight: 700,
letterSpacing: '.3rem',
textDecoration: 'none',
}}
>
COMISSIONS.APP
</Typography>
</Box>
{
user ? (
<>
<Box>
<NovuProvider subscriberId={'on-boarding-subscriber-id-123'} applicationIdentifier={'9SKjzgN_odAF'}>
<PopoverNotificationCenter colorScheme={'light'}>
{({ unseenCount }) => <NotificationBell unseenCount={unseenCount} />}
</PopoverNotificationCenter>
</NovuProvider>
</Box>
<Box>
<Chip
onClick={handleOpenUserMenu}
label={user.name}
color="secondary"
variant={'outlined'}
style={{ marginLeft: '10px', minWidth: '5rem', maxWidth:'10rem' }}
/>
<Menu
sx={{ mt: '45px' }}
id="menu-appbar"
anchorEl={anchorElUser}
anchorOrigin={{
vertical: 'top',
horizontal: 'right',
}}
keepMounted
transformOrigin={{
vertical: 'top',
horizontal: 'right',
}}
open={Boolean(anchorElUser)}
onClose={handleCloseUserMenu}
>
<MenuItem key="logout" onClick={handleCloseUserMenu}>
<Button color="primary" href="profile">Settings</Button>
</MenuItem>
<MenuItem key="logout" onClick={handleCloseUserMenu}>
<Button color="error" href="/api/auth/logout">Logout</Button>
</MenuItem>
</Menu>
</Box>
</>
) : (
<>
<Button key="login" href='/api/auth/login' onClick={handleCloseNavMenu} sx={{ my: 2, display: 'block', color:"white" }}>
Login
</Button>
<Button key="signup" href='/api/auth/login' onClick={handleCloseNavMenu} sx={{ my: 2, display: 'block', color:"white" }}>
Signup
</Button>
</>
)}
</Toolbar>
</Container>
</AppBar>
);
}
export default ResponsiveAppBar;

41
components/layout.tsx Normal file
View File

@ -0,0 +1,41 @@
import Head from "next/head";
import Header from "./header";
type LayoutProps = {
user?: any;
loading?: boolean;
children: React.ReactNode;
};
const Layout = ({ user, loading = false, children }: LayoutProps) => {
return (
<>
<Head>
<title>Next.js with Auth0</title>
</Head>
<Header user={user} loading={loading} />
<main>
<div className="container">{children}</div>
</main>
<style jsx>{`
.container {
max-width: 42rem;
margin: 1.5rem auto;
}
`}</style>
<style jsx global>{`
body {
margin: 0;
color: #333;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
}
`}</style>
</>
);
};
export default Layout;

0
generate_ Normal file
View File

9
interfaces/index.ts Normal file
View File

@ -0,0 +1,9 @@
export type User = {
email: string;
email_verified: boolean;
name: string;
nickname: string;
picture: string;
sub: string;
updated_at: string;
};

5
next-env.d.ts vendored Normal file
View File

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

2220
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

27
package.json Normal file
View File

@ -0,0 +1,27 @@
{
"private": true,
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
},
"dependencies": {
"@auth0/nextjs-auth0": "^2.2.0",
"@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0",
"@fontsource/roboto": "^5.0.8",
"@mui/icons-material": "^5.15.6",
"@mui/material": "^5.15.6",
"@novu/notification-center": "^0.22.0",
"next": "latest",
"openapi-typescript-fetch": "^1.1.3",
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/node": "^18.0.0",
"@types/react": "^18.0.14",
"@types/react-dom": "^18.0.5",
"typescript": "^4.7.4"
}
}

38
pages/_app.tsx Normal file
View File

@ -0,0 +1,38 @@
import { UserProvider } from "@auth0/nextjs-auth0/client";
import '@fontsource/roboto/300.css';
import '@fontsource/roboto/400.css';
import '@fontsource/roboto/500.css';
import '@fontsource/roboto/700.css';
import { createTheme } from '@mui/material/styles';
import { green, purple } from '@mui/material/colors';
import { ThemeProvider } from "@emotion/react";
const theme = createTheme({
palette: {
primary: {
main: '#1D3555',
},
secondary: {
main: '#E5BEED'
},
background: {
default: '#3D3E46',
paper: '#FAFAFF'
}
},
})
export default function App({ Component, pageProps }) {
// optionally pass the 'user' prop from pages that require server-side
// rendering to prepopulate the 'useUser' hook.
const { user } = pageProps;
return (
<UserProvider user={user}>
<ThemeProvider theme={theme}>
<Component {...pageProps} />
</ThemeProvider>
</UserProvider>
);
}

24
pages/about.tsx Normal file
View File

@ -0,0 +1,24 @@
import { useUser } from "@auth0/nextjs-auth0/client";
import Layout from "../components/layout";
const About = () => {
const { user, isLoading } = useUser();
return (
<Layout user={user} loading={isLoading}>
<h1>About</h1>
<p>
This project shows different ways to display Profile info: using{" "}
<i>Client rendered</i>, <i>Server rendered</i>, and <i>API rendered</i>
</p>
<p>
Navigating between this page and <i>Home</i> is always pretty fast.
However, when you navigate to the <i>Server rendered profile</i> page it
takes more time because it uses SSR to fetch the user and then to
display it
</p>
</Layout>
);
};
export default About;

View File

@ -0,0 +1,37 @@
import { useEffect, useState } from "react";
import { useUser } from "@auth0/nextjs-auth0/client";
import Layout from "../../components/layout";
const ApiProfile = () => {
const { user, isLoading } = useUser();
const [data, setData] = useState(null);
useEffect(() => {
(async () => {
const res = await fetch("/api/protected-api");
const data = await res.json();
setData(data);
})();
}, []);
return (
<Layout user={user} loading={isLoading}>
<h1>Profile</h1>
<div>
<h3>Public page (client rendered)</h3>
<p>We are fetching data on the client-side :</p>
<p>By making request to '/api/protected-api' serverless function</p>
<p>so without a valid session cookie will fail</p>
<p>{JSON.stringify(data)}</p>
</div>
</Layout>
);
};
// Public route.(CSR) also accessing API from the client-side.
// data is not cached when redirecting between pages.
export default ApiProfile;

View File

@ -0,0 +1,26 @@
import { withPageAuthRequired } from "@auth0/nextjs-auth0";
import Layout from "../../components/layout";
import { User } from "../../interfaces";
type ProfileProps = {
user: User;
};
export default function Profile({ user }: ProfileProps) {
return (
<Layout user={user}>
<h1>Profile</h1>
<div>
<h3>Profile (server rendered)</h3>
<img src={user.picture} alt="user picture" />
<p>nickname: {user.nickname}</p>
<p>name: {user.name}</p>
</div>
</Layout>
);
}
// Protected route, checking authentication status before rendering the page.(SSR)
// It's slower than a static page with client side authentication
export const getServerSideProps = withPageAuthRequired();

View File

@ -0,0 +1,3 @@
import { handleAuth } from "@auth0/nextjs-auth0";
export default handleAuth();

View File

@ -0,0 +1,20 @@
import { withApiAuthRequired, getSession } from "@auth0/nextjs-auth0";
// Serverless function
// Protected API, requests to '/api/protected' without a valid session cookie will fail
async function handle(req, res) {
const { user } = await getSession(req, res);
try {
res.status(200).json({
session: "true",
id: user.sub,
nickname: user.nickname,
});
} catch (e) {
res.status(500).json({ error: "Unable to fetch", description: e });
}
}
export default withApiAuthRequired(handle);

199
pages/index.tsx Normal file
View File

@ -0,0 +1,199 @@
import { useUser } from "@auth0/nextjs-auth0/client";
import Layout from "../components/layout";
import TextField from '@mui/material/TextField';
import Button from '@mui/material/Button';
import { Typography } from "@mui/material";
import Box from '@mui/material/Box';
import ButtonGroup from '@mui/material/ButtonGroup';
import styled from "@emotion/styled";
import IconButton from '@mui/material/IconButton';
import ImageList from '@mui/material/ImageList';
import ImageListItem from '@mui/material/ImageListItem';
import ImageListItemBar from '@mui/material/ImageListItemBar';
import ListSubheader from '@mui/material/ListSubheader';
import InfoIcon from '@mui/icons-material/Info';
import Chip from "@mui/material/Chip";
import { Fetcher } from "openapi-typescript-fetch";
import { paths } from "../types";
import Artist from "../components/artist";
const StyledTextField = styled(TextField)({
"& .MuiInputLabel-root": {
right: 0,
textAlign: "center"
},
"& .MuiInputLabel-shrink": {
margin: "0 auto",
position: "absolute",
right: "0",
left: "0",
top: "-3px",
width: "150px", // Need to give it a width so the positioning will work
background: "white" // Add a white background as below we remove the legend that had the background so text is not meshing with the border
// display: "none" //if you want to hide it completly
},
"& .MuiOutlinedInput-root.Mui-focused": {
"& legend ": {
display: "none"
}
}
});
const Home = () => {
const { user, isLoading } = useUser();
const fetcher = Fetcher.for<paths>()
fetcher.configure({
baseUrl: 'https://localhost.7148',
init: {
headers: {
},
}
})
return (
<Layout user={user} loading={isLoading}>
<>
{ user ? (
<>
<Box sx={{ m: 1 }} />
<ButtonGroup fullWidth variant="contained" aria-label="outlined primary button group">
<Button color="primary" fullWidth>Your Orders</Button>
<Button color="secondary" fullWidth>Seller Dashboard</Button>
</ButtonGroup>
</>
): (
<Typography variant="h2" component="h2" textAlign="center">COMISSIONS.APP</Typography>
)}
<Box sx={{ m: 2 }} />
<StyledTextField inputRef={input => input && input.focus()} sx={{input: {textAlign: "center"}}} fullWidth color="secondary" label="SEARCH ARTISTS" variant="outlined" />
<Box sx={{ m: 4 }} />
<div style={{ display: 'flex', justifyContent: 'center', marginTop: '10px' }}>
{chips.map((chip, index) => (
<Chip
key={index}
label={chip.label}
color="primary"
variant={chip.outlined ? 'outlined' : 'default'}
onClick={() => console.log(`Clicked on ${chip.label}`)}
style={{ margin: '0 5px' }}
/>
))}
</div>
<ImageList sx={{ flex:1 }}>
{itemData.map((item) => (
<Artist artistId="1" />
))}
</ImageList>
</>
</Layout>
);
};
// fast/cached SSR page
export default Home;
const chips = [
{ label: 'Category', outlined: true },
{ label: 'Category', outlined: false },
{ label: 'Category', outlined: true },
{ label: 'Category', outlined: false },
{ label: 'Category', outlined: true },
{ label: 'Category', outlined: false },
{ label: 'Category', outlined: true },
{ label: 'Category', outlined: false },
];
const itemData = [
{
img: 'https://images.unsplash.com/photo-1551963831-b3b1ca40c98e',
title: 'Neroshi',
author: '0 Stars (0 Reviews)',
rows: 2,
cols: 2,
featured: true,
},
{
img: 'https://images.unsplash.com/photo-1551963831-b3b1ca40c98e',
title: 'Neroshi',
author: '0 Stars (0 Reviews)',
},
{
img: 'https://images.unsplash.com/photo-1551963831-b3b1ca40c98e',
title: 'Neroshi',
author: '0 Stars (0 Reviews)',
},
{
img: 'https://images.unsplash.com/photo-1551963831-b3b1ca40c98e',
title: 'Neroshi',
author: '0 Stars (0 Reviews)',
rows: 2,
cols: 2,
featured: true,
},
{
img: 'https://images.unsplash.com/photo-1551963831-b3b1ca40c98e',
title: 'Neroshi',
author: '0 Stars (0 Reviews)',
},
{
img: 'https://images.unsplash.com/photo-1551963831-b3b1ca40c98e',
title: 'Neroshi',
author: '0 Stars (0 Reviews)',
},
{
img: 'https://images.unsplash.com/photo-1551963831-b3b1ca40c98e',
title: 'Neroshi',
author: '0 Stars (0 Reviews)',
},
{
img: 'https://images.unsplash.com/photo-1551963831-b3b1ca40c98e',
title: 'Neroshi',
author: '0 Stars (0 Reviews)',
},
{
img: 'https://images.unsplash.com/photo-1551963831-b3b1ca40c98e',
title: 'Neroshi',
author: '0 Stars (0 Reviews)',
},
{
img: 'https://images.unsplash.com/photo-1551963831-b3b1ca40c98e',
title: 'Neroshi',
author: '0 Stars (0 Reviews)',
},
{
img: 'https://images.unsplash.com/photo-1551963831-b3b1ca40c98e',
title: 'Neroshi',
author: '0 Stars (0 Reviews)',
rows: 2,
cols: 2,
featured: true,
},
{
img: 'https://images.unsplash.com/photo-1551963831-b3b1ca40c98e',
title: 'Neroshi',
author: '0 Stars (0 Reviews)',
},
{
img: 'https://images.unsplash.com/photo-1551963831-b3b1ca40c98e',
title: 'Neroshi',
author: '0 Stars (0 Reviews)',
},
{
img: 'https://images.unsplash.com/photo-1551963831-b3b1ca40c98e',
title: 'Neroshi',
author: '0 Stars (0 Reviews)',
},
{
img: 'https://images.unsplash.com/photo-1551963831-b3b1ca40c98e',
title: 'Neroshi',
author: '0 Stars (0 Reviews)',
},
];

33
pages/profile.tsx Normal file
View File

@ -0,0 +1,33 @@
import { withPageAuthRequired } from "@auth0/nextjs-auth0/client";
import Layout from "../components/layout";
import { User } from "../interfaces";
type ProfileCardProps = {
user: User;
};
const ProfileCard = ({ user }: ProfileCardProps) => {
return (
<>
<h1>Profile</h1>
<div>
<h3>Profile (client rendered)</h3>
<img src={user.picture} alt="user picture" />
<p>nickname: {user.nickname}</p>
<p>name: {user.name}</p>
</div>
</>
);
};
const Profile = ({ user, isLoading }) => {
return (
<Layout user={user} loading={isLoading}>
{isLoading ? <>Loading...</> : <ProfileCard user={user} />}
</Layout>
);
};
// Protected route, checking user authentication client-side.(CSR)
export default withPageAuthRequired(Profile);

0
pages/theme.ts Normal file
View File

2555
swagger.json Normal file

File diff suppressed because it is too large Load Diff

20
tsconfig.json Normal file
View File

@ -0,0 +1,20 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true
},
"include": ["next-env.d.ts", "additional.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}