diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1437c53 --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# 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 +.env.development.local +.env.test.local +.env.production.local + +# vercel +.vercel diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..709e0a2 --- /dev/null +++ b/Procfile @@ -0,0 +1 @@ +web: yarn start \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..0f980ed --- /dev/null +++ b/README.md @@ -0,0 +1,39 @@ +--- +title: NextJS Prisma +description: A NextJS app using Prisma with a PostgreSQL database +tags: + - next + - prisma + - postgresql + - typescript +--- + +# NextJS Prisma Example + +This example is a [NextJS](https://nextjs.org/) todo app that uses +[Prisma](https://www.prisma.io/) to store todos in Postgres. + +[![Deploy on Railway](https://railway.app/button.svg)](https://railway.app/new?template=https%3A%2F%2Fgithub.com%2Frailwayapp%2Fexamples%2Ftree%2Fmaster%2Fexamples%2Fnextjs-prisma&plugins=postgresql) + +## ✨ Features + +- Prisma +- NextJS +- Postgres +- TypeScript + +## 💁‍♀️ How to use + +- [Provision a Postgres container on Railway](https://dev.new) +- Connect to your Railway project with `railway link` +- Migrate the database `railway run yarn migrate:dev` +- Run the NextJS app `railway run yarn dev` + +## 📝 Notes + +This app is a simple todo list where the data is persisted to Postgres. [Prisma +migrations](https://www.prisma.io/docs/concepts/components/prisma-migrate#prisma-migrate) +can be created with `railway run yarn migrate:dev` and deployed with `railway run yarn migrate:deploy`. The Prisma client can be regenerated with +`yarn generate`. + +[swr](https://swr.vercel.app/) is used to fetch data on the client and perform optimistic updates. diff --git a/next-env.d.ts b/next-env.d.ts new file mode 100644 index 0000000..7b7aa2c --- /dev/null +++ b/next-env.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/package.json b/package.json new file mode 100644 index 0000000..21b41e4 --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "with-nextjs-postgres", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "yarn migrate:deploy && next build", + "start": "next start --port ${PORT-3000}", + "migrate:dev": "prisma migrate dev --preview-feature", + "migrate:deploy": "prisma migrate deploy --preview-feature", + "migrate:status": "prisma migrate status --preview-feature", + "generate": "prisma generate" + }, + "dependencies": { + "@prisma/client": "2.30.0", + "next": "12.1.0", + "pg": "^8.5.1", + "react": "17.0.1", + "react-dom": "17.0.1", + "swr": "^0.4.1" + }, + "devDependencies": { + "prisma": "2.30.0", + "@types/node": "^14.14.22", + "@types/react": "^17.0.0", + "typescript": "^4.1.3" + } +} diff --git a/prisma/migrations/20210130000309_init/migration.sql b/prisma/migrations/20210130000309_init/migration.sql new file mode 100644 index 0000000..f4c6c6e --- /dev/null +++ b/prisma/migrations/20210130000309_init/migration.sql @@ -0,0 +1,9 @@ +-- CreateTable +CREATE TABLE "Todo" ( + "id" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "text" TEXT NOT NULL, + "completed" BOOLEAN NOT NULL, + + PRIMARY KEY ("id") +); diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..2cdb8f0 --- /dev/null +++ b/prisma/migrations/migration_lock.toml @@ -0,0 +1,2 @@ +# Please do not edit this file manually +provider = "postgresql" \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 0000000..9a95238 --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,18 @@ +// This is your Prisma schema file, +// learn more about it in the docs: https://pris.ly/d/prisma-schema + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +generator client { + provider = "prisma-client-js" +} + +model Todo { + id String @id @default(uuid()) + createdAt DateTime @default(now()) + text String + completed Boolean +} diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..4965832 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/vercel.svg b/public/vercel.svg new file mode 100644 index 0000000..fbf0e25 --- /dev/null +++ b/public/vercel.svg @@ -0,0 +1,4 @@ + + + \ No newline at end of file diff --git a/src/api.ts b/src/api.ts new file mode 100644 index 0000000..28d9e60 --- /dev/null +++ b/src/api.ts @@ -0,0 +1,42 @@ +import useSWR, { mutate } from "swr"; +import { Todo } from "./types"; + +const todoPath = "/api/todos"; + +export const useTodos = () => useSWR(todoPath); + +export const createTodo = async (text: string) => { + mutate( + todoPath, + todos => [{ text, completed: false, id: "new-todo" }, ...todos], + false, + ); + await fetch(todoPath, { + method: "POST", + body: JSON.stringify({ text }), + }); + + mutate(todoPath); +}; + +export const toggleTodo = async (todo: Todo) => { + mutate( + todoPath, + todos => + todos.map(t => + t.id === todo.id ? { ...todo, completed: !t.completed } : t, + ), + false, + ); + await fetch(`${todoPath}?todoId=${todo.id}`, { + method: "PUT", + body: JSON.stringify({ completed: !todo.completed }), + }); + mutate(todoPath); +}; + +export const deleteTodo = async (id: string) => { + mutate(todoPath, todos => todos.filter(t => t.id !== id), false); + await fetch(`${todoPath}?todoId=${id}`, { method: "DELETE" }); + mutate(todoPath); +}; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx new file mode 100644 index 0000000..1e1cec9 --- /dev/null +++ b/src/pages/_app.tsx @@ -0,0 +1,7 @@ +import '../styles/globals.css' + +function MyApp({ Component, pageProps }) { + return +} + +export default MyApp diff --git a/src/pages/api/todos.ts b/src/pages/api/todos.ts new file mode 100644 index 0000000..013fe3b --- /dev/null +++ b/src/pages/api/todos.ts @@ -0,0 +1,38 @@ +import type { NextApiRequest, NextApiResponse } from "next"; +import { PrismaClient } from "@prisma/client"; + +const prisma = new PrismaClient(); + +export default async (req: NextApiRequest, res: NextApiResponse) => { + if (req.method === "GET") { + // get all todos + const todos = await prisma.todo.findMany({ + orderBy: { createdAt: "desc" }, + }); + res.json(todos); + } else if (req.method === "POST") { + // create todo + const text = JSON.parse(req.body).text; + const todo = await prisma.todo.create({ + data: { text, completed: false }, + }); + + res.json(todo); + } else if (req.method === "PUT") { + // update todo + const id = req.query.todoId as string; + const data = JSON.parse(req.body); + const todo = await prisma.todo.update({ + where: { id }, + data, + }); + + res.json(todo); + } else if (req.method === "DELETE") { + // delete todo + const id = req.query.todoId as string; + await prisma.todo.delete({ where: { id } }); + + res.json({ status: "ok" }); + } +}; diff --git a/src/pages/index.tsx b/src/pages/index.tsx new file mode 100644 index 0000000..061b2a9 --- /dev/null +++ b/src/pages/index.tsx @@ -0,0 +1,95 @@ +import { NextPage } from "next"; +import Head from "next/head"; +import { useMemo, useState } from "react"; +import { createTodo, deleteTodo, toggleTodo, useTodos } from "../api"; +import styles from "../styles/Home.module.css"; +import { Todo } from "../types"; + +export const TodoList: React.FC = () => { + const { data: todos, error } = useTodos(); + + if (error != null) return
Error loading todos...
; + if (todos == null) return
Loading...
; + + if (todos.length === 0) { + return
Try adding a todo ☝️️
; + } + + return ( +
    + {todos.map(todo => ( + + ))} +
+ ); +}; + +const TodoItem: React.FC<{ todo: Todo }> = ({ todo }) => ( +
  • + + + +
  • +); + +const AddTodoInput = () => { + const [text, setText] = useState(""); + + return ( +
    { + e.preventDefault(); + createTodo(text); + setText(""); + }} + className={styles.addTodo} + > + setText(e.target.value)} + /> + +
    + ); +}; + +const Home: NextPage = () => { + return ( +
    + + Railway NextJS Prisma + + + +
    +

    Todos

    +

    + NextJS app connected to Postgres using Prisma and hosted on{" "} + Railway +

    +
    + +
    + + + +
    +
    + ); +}; + +export default Home; diff --git a/src/styles/Home.module.css b/src/styles/Home.module.css new file mode 100644 index 0000000..f67d477 --- /dev/null +++ b/src/styles/Home.module.css @@ -0,0 +1,131 @@ +.container { + padding: 0 0.5rem; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.header { + padding: 4rem 0 2rem 0; +} + +.main { + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + min-width: 500px; +} + +.title { + margin: 0; + line-height: 1.15; + font-size: 4rem; + font-weight: bold; +} + +.title, +.desc { + text-align: center; +} + +.desc { + line-height: 1.5; + font-size: 1rem; + font-weight: normal; +} + +.addTodo { + display: flex; + width: 100%; + margin: 0 auto; + margin-bottom: 2rem; +} + +.input { + padding: 1rem; + border: solid 2px; + border-color: #cccccc; + border-radius: 4px; + flex-grow: 1; + margin-right: 0.5rem; + font-size: 1em; +} + +.addButton { + width: 80px; + color: white; + background-color: #0070f3; + border-color: #0070f3; + font-size: 1em; + border: solid 2px; + border-radius: 4px; + cursor: pointer; +} + +.addButton:hover { + color: #0070f3; + background-color: white; + border-color: #0070f3; +} + +.code { + background: #fafafa; + border-radius: 5px; + padding: 0.75rem; + font-size: 1.1rem; + font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, + Bitstream Vera Sans Mono, Courier New, monospace; +} + +.todoList { + width: 100%; + margin: 0; + padding: 0; + list-style-type: none; +} + +.todo { + display: flex; + align-items: center; + justify-content: space-between; +} + +.label { + flex-grow: 1; + font-size: 1.1rem; + padding: 0.5rem 0; + cursor: pointer; +} + +.checkbox { + margin-right: 1rem; +} + +.checked { + text-decoration: line-through; +} + +.deleteButton { + font-size: 1rem; + border: none; + background: none; + color: darkgrey; + cursor: pointer; +} + +.deleteButton:hover { + color: var(--primary); +} + +.emptyState { +} + +@media (max-width: 600px) { + .main { + min-width: auto; + width: 100%; + } +} diff --git a/src/styles/globals.css b/src/styles/globals.css new file mode 100644 index 0000000..319e937 --- /dev/null +++ b/src/styles/globals.css @@ -0,0 +1,26 @@ +:root { + --primary: #c049ff; +} + +html, +body { + padding: 0; + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, + Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; +} + +a { + color: var(--primary); + text-decoration: none; +} + +a:hover, +a:focus, +a:active { + text-decoration: underline; +} + +* { + box-sizing: border-box; +} diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..279563b --- /dev/null +++ b/src/types.ts @@ -0,0 +1,6 @@ +export interface Todo { + id: string; + created: string; + text: string; + completed: boolean; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..35d51ea --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,29 @@ +{ + "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" + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..d28920d --- /dev/null +++ b/yarn.lock @@ -0,0 +1,333 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@next/env@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/env/-/env-12.1.0.tgz#73713399399b34aa5a01771fb73272b55b22c314" + integrity sha512-nrIgY6t17FQ9xxwH3jj0a6EOiQ/WDHUos35Hghtr+SWN/ntHIQ7UpuvSi0vaLzZVHQWaDupKI+liO5vANcDeTQ== + +"@next/swc-android-arm64@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-12.1.0.tgz#865ba3a9afc204ff2bdeea49dd64d58705007a39" + integrity sha512-/280MLdZe0W03stA69iL+v6I+J1ascrQ6FrXBlXGCsGzrfMaGr7fskMa0T5AhQIVQD4nA/46QQWxG//DYuFBcA== + +"@next/swc-darwin-arm64@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.1.0.tgz#08e8b411b8accd095009ed12efbc2f1d4d547135" + integrity sha512-R8vcXE2/iONJ1Unf5Ptqjk6LRW3bggH+8drNkkzH4FLEQkHtELhvcmJwkXcuipyQCsIakldAXhRbZmm3YN1vXg== + +"@next/swc-darwin-x64@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-12.1.0.tgz#fcd684497a76e8feaca88db3c394480ff0b007cd" + integrity sha512-ieAz0/J0PhmbZBB8+EA/JGdhRHBogF8BWaeqR7hwveb6SYEIJaDNQy0I+ZN8gF8hLj63bEDxJAs/cEhdnTq+ug== + +"@next/swc-linux-arm-gnueabihf@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.1.0.tgz#9ec6380a27938a5799aaa6035c205b3c478468a7" + integrity sha512-njUd9hpl6o6A5d08dC0cKAgXKCzm5fFtgGe6i0eko8IAdtAPbtHxtpre3VeSxdZvuGFh+hb0REySQP9T1ttkog== + +"@next/swc-linux-arm64-gnu@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.1.0.tgz#7f4196dff1049cea479607c75b81033ae2dbd093" + integrity sha512-OqangJLkRxVxMhDtcb7Qn1xjzFA3s50EIxY7mljbSCLybU+sByPaWAHY4px97ieOlr2y4S0xdPKkQ3BCAwyo6Q== + +"@next/swc-linux-arm64-musl@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.1.0.tgz#b445f767569cdc2dddee785ca495e1a88c025566" + integrity sha512-hB8cLSt4GdmOpcwRe2UzI5UWn6HHO/vLkr5OTuNvCJ5xGDwpPXelVkYW/0+C3g5axbDW2Tym4S+MQCkkH9QfWA== + +"@next/swc-linux-x64-gnu@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.1.0.tgz#67610e9be4fbc987de7535f1bcb17e45fe12f90e" + integrity sha512-OKO4R/digvrVuweSw/uBM4nSdyzsBV5EwkUeeG4KVpkIZEe64ZwRpnFB65bC6hGwxIBnTv5NMSnJ+0K/WmG78A== + +"@next/swc-linux-x64-musl@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-12.1.0.tgz#ea19a23db08a9f2e34ac30401f774cf7d1669d31" + integrity sha512-JohhgAHZvOD3rQY7tlp7NlmvtvYHBYgY0x5ZCecUT6eCCcl9lv6iV3nfu82ErkxNk1H893fqH0FUpznZ/H3pSw== + +"@next/swc-win32-arm64-msvc@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.1.0.tgz#eadf054fc412085659b98e145435bbba200b5283" + integrity sha512-T/3gIE6QEfKIJ4dmJk75v9hhNiYZhQYAoYm4iVo1TgcsuaKLFa+zMPh4056AHiG6n9tn2UQ1CFE8EoybEsqsSw== + +"@next/swc-win32-ia32-msvc@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.1.0.tgz#68faeae10c89f698bf9d28759172b74c9c21bda1" + integrity sha512-iwnKgHJdqhIW19H9PRPM9j55V6RdcOo6rX+5imx832BCWzkDbyomWnlzBfr6ByUYfhohb8QuH4hSGEikpPqI0Q== + +"@next/swc-win32-x64-msvc@12.1.0": + version "12.1.0" + resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.1.0.tgz#d27e7e76c87a460a4da99c5bfdb1618dcd6cd064" + integrity sha512-aBvcbMwuanDH4EMrL2TthNJy+4nP59Bimn8egqv6GHMVj0a44cU6Au4PjOhLNqEh9l+IpRGBqMTzec94UdC5xg== + +"@prisma/client@2.30.0": + version "2.30.0" + resolved "https://registry.yarnpkg.com/@prisma/client/-/client-2.30.0.tgz#b0ed9db67405f619e428577f2d45843104142e00" + integrity sha512-tjJNHVfgyNOwS2F+AkjMMCJGPnXzHuUCrOnAMJyidAu4aNzxbJ8jWwjt96rRMpyrg9Hwen3xqqQ2oA+ikK7nhQ== + dependencies: + "@prisma/engines-version" "2.30.0-28.60b19f4a1de4fe95741da371b4c44a92f4d1adcb" + +"@prisma/engines-version@2.30.0-28.60b19f4a1de4fe95741da371b4c44a92f4d1adcb": + version "2.30.0-28.60b19f4a1de4fe95741da371b4c44a92f4d1adcb" + resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-2.30.0-28.60b19f4a1de4fe95741da371b4c44a92f4d1adcb.tgz#1360113dc19e1d43d4442e3b638ccfa0e1711943" + integrity sha512-oThNpx7HtJ0eEmnvrWARYcNCs6dqFdAK3Smt2bJVDD6Go4HLuuhjx028osP+rHaFrGOTx7OslLZYtvvFlAXRDA== + +"@prisma/engines@2.30.0-28.60b19f4a1de4fe95741da371b4c44a92f4d1adcb": + version "2.30.0-28.60b19f4a1de4fe95741da371b4c44a92f4d1adcb" + resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-2.30.0-28.60b19f4a1de4fe95741da371b4c44a92f4d1adcb.tgz#b4d91ff876662b1de83e0cc913149a1c088becc7" + integrity sha512-LPKq88lIbYezvX0OOc1PU42hHdTsSMPJWmK8lusaHK7DaLHyXjDp/551LbsVapypbjW6N3Jx/If6GoMDASSMSw== + +"@types/node@^14.14.22": + version "14.14.22" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.22.tgz#0d29f382472c4ccf3bd96ff0ce47daf5b7b84b18" + integrity sha512-g+f/qj/cNcqKkc3tFqlXOYjrmZA+jNBiDzbP3kH+B+otKFqAdPgVTGP1IeKRdMml/aE69as5S4FqtxAbl+LaMw== + +"@types/prop-types@*": + version "15.7.3" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" + integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== + +"@types/react@^17.0.0": + version "17.0.0" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.0.tgz#5af3eb7fad2807092f0046a1302b7823e27919b8" + integrity sha512-aj/L7RIMsRlWML3YB6KZiXB3fV2t41+5RBGYF8z+tAKU43Px8C3cYUZsDvf1/+Bm4FK21QWBrDutu8ZJ/70qOw== + dependencies: + "@types/prop-types" "*" + csstype "^3.0.2" + +buffer-writer@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/buffer-writer/-/buffer-writer-2.0.0.tgz#ce7eb81a38f7829db09c873f2fbb792c0c98ec04" + integrity sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw== + +caniuse-lite@^1.0.30001283: + version "1.0.30001334" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001334.tgz#892e9965b35285033fc2b8a8eff499fe02f13d8b" + integrity sha512-kbaCEBRRVSoeNs74sCuq92MJyGrMtjWVfhltoHUCW4t4pXFvGjUBrfo47weBRViHkiV3eBYyIsfl956NtHGazw== + +csstype@^3.0.2: + version "3.0.5" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.5.tgz#7fdec6a28a67ae18647c51668a9ff95bb2fa7bb8" + integrity sha512-uVDi8LpBUKQj6sdxNaTetL6FpeCqTjOvAQuQUa/qAqq8oOd4ivkbhgnqayl0dnPal8Tb/yB1tF+gOvCBiicaiQ== + +dequal@2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.2.tgz#85ca22025e3a87e65ef75a7a437b35284a7e319d" + integrity sha512-q9K8BlJVxK7hQYqa6XISGmBZbtQQWVXSrRrWreHC94rMt1QL/Impruc+7p2CYSYuVIUr+YCt6hjrs1kkdJRTug== + +"js-tokens@^3.0.0 || ^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +loose-envify@^1.1.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +nanoid@^3.1.30: + version "3.3.3" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" + integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== + +next@12.1.0: + version "12.1.0" + resolved "https://registry.yarnpkg.com/next/-/next-12.1.0.tgz#c33d753b644be92fc58e06e5a214f143da61dd5d" + integrity sha512-s885kWvnIlxsUFHq9UGyIyLiuD0G3BUC/xrH0CEnH5lHEWkwQcHOORgbDF0hbrW9vr/7am4ETfX4A7M6DjrE7Q== + dependencies: + "@next/env" "12.1.0" + caniuse-lite "^1.0.30001283" + postcss "8.4.5" + styled-jsx "5.0.0" + use-subscription "1.5.1" + optionalDependencies: + "@next/swc-android-arm64" "12.1.0" + "@next/swc-darwin-arm64" "12.1.0" + "@next/swc-darwin-x64" "12.1.0" + "@next/swc-linux-arm-gnueabihf" "12.1.0" + "@next/swc-linux-arm64-gnu" "12.1.0" + "@next/swc-linux-arm64-musl" "12.1.0" + "@next/swc-linux-x64-gnu" "12.1.0" + "@next/swc-linux-x64-musl" "12.1.0" + "@next/swc-win32-arm64-msvc" "12.1.0" + "@next/swc-win32-ia32-msvc" "12.1.0" + "@next/swc-win32-x64-msvc" "12.1.0" + +object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + +packet-reader@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/packet-reader/-/packet-reader-1.0.0.tgz#9238e5480dedabacfe1fe3f2771063f164157d74" + integrity sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ== + +pg-connection-string@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.4.0.tgz#c979922eb47832999a204da5dbe1ebf2341b6a10" + integrity sha512-3iBXuv7XKvxeMrIgym7njT+HlZkwZqqGX4Bu9cci8xHZNT+Um1gWKqCsAzcC0d95rcKMU5WBg6YRUcHyV0HZKQ== + +pg-int8@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c" + integrity sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw== + +pg-pool@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.2.2.tgz#a560e433443ed4ad946b84d774b3f22452694dff" + integrity sha512-ORJoFxAlmmros8igi608iVEbQNNZlp89diFVx6yV5v+ehmpMY9sK6QgpmgoXbmkNaBAx8cOOZh9g80kJv1ooyA== + +pg-protocol@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/pg-protocol/-/pg-protocol-1.4.0.tgz#43a71a92f6fe3ac559952555aa3335c8cb4908be" + integrity sha512-El+aXWcwG/8wuFICMQjM5ZSAm6OWiJicFdNYo+VY3QP+8vI4SvLIWVe51PppTzMhikUJR+PsyIFKqfdXPz/yxA== + +pg-types@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-2.2.0.tgz#2d0250d636454f7cfa3b6ae0382fdfa8063254a3" + integrity sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA== + dependencies: + pg-int8 "1.0.1" + postgres-array "~2.0.0" + postgres-bytea "~1.0.0" + postgres-date "~1.0.4" + postgres-interval "^1.1.0" + +pg@^8.5.1: + version "8.5.1" + resolved "https://registry.yarnpkg.com/pg/-/pg-8.5.1.tgz#34dcb15f6db4a29c702bf5031ef2e1e25a06a120" + integrity sha512-9wm3yX9lCfjvA98ybCyw2pADUivyNWT/yIP4ZcDVpMN0og70BUWYEGXPCTAQdGTAqnytfRADb7NERrY1qxhIqw== + dependencies: + buffer-writer "2.0.0" + packet-reader "1.0.0" + pg-connection-string "^2.4.0" + pg-pool "^3.2.2" + pg-protocol "^1.4.0" + pg-types "^2.1.0" + pgpass "1.x" + +pgpass@1.x: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pgpass/-/pgpass-1.0.2.tgz#2a7bb41b6065b67907e91da1b07c1847c877b306" + integrity sha1-Knu0G2BltnkH6R2hsHwYR8h3swY= + dependencies: + split "^1.0.0" + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +postcss@8.4.5: + version "8.4.5" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.5.tgz#bae665764dfd4c6fcc24dc0fdf7e7aa00cc77f95" + integrity sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg== + dependencies: + nanoid "^3.1.30" + picocolors "^1.0.0" + source-map-js "^1.0.1" + +postgres-array@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-2.0.0.tgz#48f8fce054fbc69671999329b8834b772652d82e" + integrity sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA== + +postgres-bytea@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/postgres-bytea/-/postgres-bytea-1.0.0.tgz#027b533c0aa890e26d172d47cf9ccecc521acd35" + integrity sha1-AntTPAqokOJtFy1Hz5zOzFIazTU= + +postgres-date@~1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-1.0.5.tgz#710b27de5f27d550f6e80b5d34f7ba189213c2ee" + integrity sha512-pdau6GRPERdAYUQwkBnGKxEfPyhVZXG/JiS44iZWiNdSOWE09N2lUgN6yshuq6fVSon4Pm0VMXd1srUUkLe9iA== + +postgres-interval@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/postgres-interval/-/postgres-interval-1.2.0.tgz#b460c82cb1587507788819a06aa0fffdb3544695" + integrity sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ== + dependencies: + xtend "^4.0.0" + +prisma@2.30.0: + version "2.30.0" + resolved "https://registry.yarnpkg.com/prisma/-/prisma-2.30.0.tgz#5b12091c480d538540b898d364b73651d44b4a01" + integrity sha512-2XYpSibcVpMd1JDxYypGDU/JKq0W2f/HI1itdddr4Pfg+q6qxt/ItWKcftv4/lqN6u/BVlQ2gDzXVEjpHeO5kQ== + dependencies: + "@prisma/engines" "2.30.0-28.60b19f4a1de4fe95741da371b4c44a92f4d1adcb" + +react-dom@17.0.1: + version "17.0.1" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.1.tgz#1de2560474ec9f0e334285662ede52dbc5426fc6" + integrity sha512-6eV150oJZ9U2t9svnsspTMrWNyHc6chX0KzDeAOXftRa8bNeOKTTfCJ7KorIwenkHd2xqVTBTCZd79yk/lx/Ug== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + scheduler "^0.20.1" + +react@17.0.1: + version "17.0.1" + resolved "https://registry.yarnpkg.com/react/-/react-17.0.1.tgz#6e0600416bd57574e3f86d92edba3d9008726127" + integrity sha512-lG9c9UuMHdcAexXtigOZLX8exLWkW0Ku29qPRU8uhF2R9BN96dLCt0psvzPLlHc5OWkgymP3qwTRgbnw5BKx3w== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + +scheduler@^0.20.1: + version "0.20.1" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.1.tgz#da0b907e24026b01181ecbc75efdc7f27b5a000c" + integrity sha512-LKTe+2xNJBNxu/QhHvDR14wUXHRQbVY5ZOYpOGWRzhydZUqrLb2JBvLPY7cAqFmqrWuDED0Mjk7013SZiOz6Bw== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + +source-map-js@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + +split@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9" + integrity sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg== + dependencies: + through "2" + +styled-jsx@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.0.0.tgz#816b4b92e07b1786c6b7111821750e0ba4d26e77" + integrity sha512-qUqsWoBquEdERe10EW8vLp3jT25s/ssG1/qX5gZ4wu15OZpmSMFI2v+fWlRhLfykA5rFtlJ1ME8A8pm/peV4WA== + +swr@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/swr/-/swr-0.4.1.tgz#4bb1f9e994a8f8d7a2e9220dbc7aef296bc4a673" + integrity sha512-/jQWPjCwy7rNbEJKpuObfbtJHtneTKhlzjy8VkuAEGg/kkYhZoKWVdGtIXrz9vuTGdy/grLVWpA6DfiNk9ECWg== + dependencies: + dequal "2.0.2" + +through@2: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + +typescript@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.1.3.tgz#519d582bd94cba0cf8934c7d8e8467e473f53bb7" + integrity sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg== + +use-subscription@1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/use-subscription/-/use-subscription-1.5.1.tgz#73501107f02fad84c6dd57965beb0b75c68c42d1" + integrity sha512-Xv2a1P/yReAjAbhylMfFplFKj9GssgTwN7RlcTxBujFQcloStWNDQdc4g4NRWH9xS4i/FDk04vQBptAXoF3VcA== + dependencies: + object-assign "^4.1.1" + +xtend@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==