├── .eslintignore
├── .eslintrc
├── .gitignore
├── .prettierrc
├── README.md
├── components
├── container.tsx
├── docPane.tsx
├── editor.tsx
├── featureSection.tsx
├── folderList.tsx
├── folderPane.tsx
├── githubIcon.tsx
├── hero.tsx
├── homeNav.tsx
├── logo.tsx
├── newDocumentDialog.tsx
├── newFolderButton.tsx
├── newFolderDialog.tsx
├── postPreview.tsx
├── socialButton.tsx
└── user.tsx
├── content.ts
├── db
├── connect.ts
├── doc.ts
├── folder.ts
├── index.ts
└── user.ts
├── env.local
├── middleware
├── all.ts
├── auth.ts
├── db.ts
├── error.ts
└── index.ts
├── next-env.d.ts
├── next.config.js
├── package.json
├── pages
├── _app.tsx
├── _document.tsx
├── app
│ └── [[...id]].tsx
├── blog
│ ├── [slug].tsx
│ └── index.tsx
├── index.tsx
└── signin.tsx
├── posts
├── known-raises-series-a.mdx
├── top-10-wiki-apps.mdx
├── whats-next-for-known.mdx
└── your-team-needs-docs.mdx
├── public
├── docs.png
├── editor.png
├── favicon.ico
└── vercel.svg
├── styles
└── globals.css
├── tsconfig.json
├── types.d.ts
├── types.ts
├── utils
├── gradients.ts
└── isSSR.ts
└── yarn.lock
/.eslintignore:
--------------------------------------------------------------------------------
1 | **/node_modules/**
2 | lib
3 | build
4 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "@typescript-eslint/parser",
3 | "plugins": [
4 | "import",
5 | "jest"
6 | ],
7 | "settings": {
8 | "import/resolver": {
9 | "node": {
10 | "extensions": [".js", ".jsx", ".ts", ".tsx"]
11 | }
12 | }
13 | },
14 | "rules": {
15 | "semi": "off",
16 | "react/jsx-filename-extension": "off",
17 | "react/jsx-props-no-spreading": "off",
18 | "react/destructuring-assignment": "off",
19 | "react/forbid-prop-types": "off",
20 | "react/prop-types": "off",
21 | "react/no-unused-prop-types": "warn",
22 | "import/extensions": "off",
23 | "import/no-unresolved": "error",
24 | "no-underscore-dangle": "off",
25 | "import/prefer-default-export": "off",
26 | "consistent-return": "off",
27 | "no-param-reassign": "off",
28 | "radix": "off",
29 | "no-use-before-define": "off",
30 | "react/prefer-stateless-function": "off",
31 | "no-unused-vars": "warn",
32 | "unicorn/no-null": "off",
33 | // Place to specify ESLint rules. Can be used to overwrite rules specified from the extended configs
34 | // e.g. "@typescript-eslint/explicit-function-return-type": "off",
35 | "@typescript-eslint/no-explicit-any": "off",
36 | "@typescript-eslint/no-non-null-assertion": "off",
37 | "@typescript-eslint/no-var-requires": "off",
38 | "@typescript-eslint/camelcase": "off",
39 | "@typescript-eslint/explicit-function-return-type": "off",
40 | "@typescript-eslint/no-empty-function": "off",
41 | "jsx-a11y/click-events-have-key-events": "off",
42 | "jsx-a11y/no-static-element-interactions": "off",
43 | "jsx-a11y/html-has-lang": "off",
44 | "jsx-a11y/anchor-is-valid": "off"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # next.js
12 | /.next/
13 | /out/
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | *.pem
21 |
22 | # debug
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 |
27 | # local env files
28 | .env.local
29 | .env.development.local
30 | .env.test.local
31 | .env.production.local
32 |
33 | # vercel
34 | .vercel
35 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "trailingComma": "all",
4 | "singleQuote": true,
5 | "printWidth": 120,
6 | "tabWidth": 2
7 | }
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Production Grade Next.js course
2 | > by scott moss and frontendmasters
3 |
4 | [Curriculum](https://production-grade-nextjs.vercel.app)
5 |
--------------------------------------------------------------------------------
/components/container.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Pane } from 'evergreen-ui'
3 |
4 | const Container = ({ children, ...styles }) => (
5 |
6 | {children}
7 |
8 | )
9 |
10 | export default Container
11 |
--------------------------------------------------------------------------------
/components/docPane.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react'
2 | import { Pane, Heading, majorScale } from 'evergreen-ui'
3 | import Link from 'next/link'
4 | import dynamic from 'next/dynamic'
5 | import { getRandomGradientCss } from '../utils/gradients'
6 |
7 | const Editor = dynamic(() => import('./editor'), { ssr: false })
8 |
9 | const DocPane: FC<{ folder: any; doc: any }> = ({ folder, doc }) => {
10 | const { bg, image } = getRandomGradientCss()
11 |
12 | return (
13 |
14 |
15 |
16 |
17 |
18 | {folder.name}
19 |
20 | {` / `}
21 | {doc.name}
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | )
30 | }
31 |
32 | export default DocPane
33 |
--------------------------------------------------------------------------------
/components/editor.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC, useState, useEffect, useRef } from 'react'
2 | import Embed from '@editorjs/embed'
3 | import Table from '@editorjs/table'
4 | import List from '@editorjs/list'
5 | import Warning from '@editorjs/warning'
6 | import Code from '@editorjs/code'
7 | import LinkTool from '@editorjs/link'
8 | import Image from '@editorjs/image'
9 | import Raw from '@editorjs/raw'
10 | import Header from '@editorjs/header'
11 | import Quote from '@editorjs/quote'
12 | import Marker from '@editorjs/marker'
13 | import CheckList from '@editorjs/checklist'
14 | import Delimiter from '@editorjs/delimiter'
15 | import InlineCode from '@editorjs/inline-code'
16 | import SimpleImage from '@editorjs/simple-image'
17 | import EditorJS from '@editorjs/editorjs'
18 | import { Icon, Pane, Text, TickIcon, Spinner, majorScale } from 'evergreen-ui'
19 | import { useThrottleCallback } from '@react-hook/throttle'
20 |
21 | const saveEditor = async (docId: string, data: any) => {
22 | await fetch(`${process.env.NEXT_PUBLIC_API_HOST}/api/doc/${docId}`, {
23 | method: 'PUT',
24 | body: JSON.stringify(data),
25 | headers: {
26 | 'Content-Type': 'application/json',
27 | },
28 | })
29 | }
30 |
31 | const EDITOR_JS_TOOLS = {
32 | embed: Embed,
33 | table: Table,
34 | marker: Marker,
35 | list: List,
36 | warning: Warning,
37 | code: Code,
38 | linkTool: LinkTool,
39 | image: Image,
40 | raw: Raw,
41 | header: Header,
42 | quote: Quote,
43 | checklist: CheckList,
44 | delimiter: Delimiter,
45 | inlineCode: InlineCode,
46 | simpleImage: SimpleImage,
47 | }
48 |
49 | const Editor: FC<{ docId: string; content: any }> = ({ content, docId }) => {
50 | const editor = useRef(null)
51 | const [saving, setSaving] = useState(false)
52 | const [doneSaving, setDoneSaving] = useState(false)
53 |
54 | const save = useThrottleCallback(async () => {
55 | if (editor.current) {
56 | const data = await editor.current.save()
57 |
58 | setSaving(true)
59 | setDoneSaving(false)
60 |
61 | await saveEditor(docId, { content: data })
62 |
63 | setTimeout(() => {
64 | setSaving(false)
65 | setDoneSaving(true)
66 |
67 | setTimeout(() => {
68 | setDoneSaving(false)
69 | }, 3000)
70 | }, 2500)
71 | }
72 | }, 30)
73 |
74 | useEffect(() => {
75 | const editorJs = new EditorJS({
76 | tools: EDITOR_JS_TOOLS,
77 | holder: 'editorjs',
78 | data: content,
79 | autofocus: true,
80 | placeholder: 'Let it be known.',
81 | onChange: save,
82 | })
83 |
84 | editor.current = editorJs
85 |
86 | return () => {
87 | if (editor.current) {
88 | try {
89 | editor.current.destroy()
90 | } catch {
91 | console.warn('error destroying editor')
92 | }
93 | }
94 | }
95 | }, [save, content])
96 |
97 | return (
98 |
99 |
100 | {saving || doneSaving ? (
101 |
114 | {saving ? : }
115 | {saving ? '...auto saving' : 'saved'}
116 |
117 | ) : null}
118 |
119 | )
120 | }
121 |
122 | Editor.defaultProps = {
123 | content: {},
124 | }
125 |
126 | export default Editor
127 |
--------------------------------------------------------------------------------
/components/featureSection.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react'
2 | import { Pane, Heading, Paragraph, majorScale } from 'evergreen-ui'
3 | import Image from 'next/image'
4 | import Container from './container'
5 |
6 | const FeatureSection: FC<{ invert?: boolean; title: string; body: string; image: string }> = ({
7 | title,
8 | body,
9 | image,
10 | invert,
11 | }) => {
12 | const Left = () => (
13 |
14 | {title}
15 | {body}
16 |
17 | )
18 | const Right = () => (
19 |
20 |
21 |
22 | )
23 |
24 | const children = invert ? [Right, Left] : [Left, Right]
25 | return (
26 |
36 |
37 |
38 | {children.map((Child, i) => (
39 |
40 |
41 |
42 | ))}
43 |
44 |
45 |
46 | )
47 | }
48 |
49 | export default FeatureSection
50 |
--------------------------------------------------------------------------------
/components/folderList.tsx:
--------------------------------------------------------------------------------
1 | import { Pane, majorScale, Menu, FolderCloseIcon } from 'evergreen-ui'
2 | import React, { FC } from 'react'
3 | import { useRouter } from 'next/router'
4 |
5 | const FolderList: FC<{ folders: any[] }> = ({ folders }) => {
6 | const router = useRouter()
7 | return (
8 |
9 |
16 |
17 | )
18 | }
19 |
20 | FolderList.defaultProps = {
21 | folders: [],
22 | }
23 |
24 | export default FolderList
25 |
--------------------------------------------------------------------------------
/components/folderPane.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC, useState } from 'react'
2 | import { Pane, Heading, majorScale, DocumentIcon, Button } from 'evergreen-ui'
3 | import Link from 'next/link'
4 | import { getRandomGradientCss } from '../utils/gradients'
5 | import NewFolderButton from './newFolderButton'
6 | import NewDocDialog from './newDocumentDialog'
7 |
8 | const FolderPane: FC<{ folder: any; docs: any[] }> = ({ folder, docs }) => {
9 | const { bg, image } = getRandomGradientCss()
10 | const [isShown, setIsShown] = useState(false)
11 | const [allDocs, setDocs] = useState(docs || [])
12 |
13 | const handleNewDoc = async (name: string) => {
14 | const res = await fetch(`${process.env.NEXT_PUBLIC_API_HOST}/api/doc/`, {
15 | method: 'POST',
16 | body: JSON.stringify({ name, folder: folder._id }),
17 | headers: {
18 | 'Content-Type': 'application/json',
19 | },
20 | })
21 |
22 | const { data } = await res.json()
23 | setDocs((state) => [...state, data])
24 | }
25 |
26 | return (
27 |
28 |
29 |
30 |
31 | setIsShown(true)} />
32 |
33 | {folder.name}
34 |
35 |
36 |
37 |
38 | {allDocs.map((doc) => (
39 |
40 |
41 |
42 |
45 |
46 |
47 |
48 | ))}
49 |
50 |
51 |
52 | setIsShown(false)} />
53 |
54 | )
55 | }
56 |
57 | FolderPane.defaultProps = {
58 | docs: [],
59 | }
60 |
61 | export default FolderPane
62 |
--------------------------------------------------------------------------------
/components/githubIcon.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const GithubIcon = () => {
4 | return (
5 |
12 | )
13 | }
14 |
15 | export default GithubIcon
16 |
--------------------------------------------------------------------------------
/components/hero.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react'
2 | import { Pane, Heading, Paragraph, majorScale } from 'evergreen-ui'
3 |
4 | const Hero: FC<{ content: { title: string; body: string } }> = ({ content }) => {
5 | return (
6 |
13 |
14 |
15 | {content.title}
16 |
17 |
18 | {content.body}
19 |
20 |
21 |
22 | )
23 | }
24 |
25 | export default Hero
26 |
--------------------------------------------------------------------------------
/components/homeNav.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react'
2 | import { Pane, majorScale, Text, Button } from 'evergreen-ui'
3 | import NextLink from 'next/link'
4 | import { useSession } from 'next-auth/client'
5 | import Container from './container'
6 | import Logo from './logo'
7 |
8 | const HomeNav: FC<{ links?: { name: string; link: string }[] }> = ({ links }) => {
9 | const [session] = useSession()
10 |
11 | return (
12 |
45 | )
46 | }
47 |
48 | HomeNav.defaultProps = {
49 | links: [{ name: 'Blog', link: '/blog' }],
50 | }
51 |
52 | export default HomeNav
53 |
--------------------------------------------------------------------------------
/components/logo.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Link from 'next/link'
3 | import { Text } from 'evergreen-ui'
4 |
5 | const Logo = ({ ...styles }) => {
6 | return (
7 |
8 |
9 |
10 | Known.
11 |
12 |
13 |
14 | )
15 | }
16 |
17 | export default Logo
18 |
--------------------------------------------------------------------------------
/components/newDocumentDialog.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import { Dialog, TextInput } from 'evergreen-ui'
3 |
4 | const NewDocDialog = ({ onNewDoc, close, ...props }) => {
5 | const [name, setName] = useState('')
6 | const [saving, setSaving] = useState(false)
7 |
8 | const handleNewDocument = async () => {
9 | setSaving(true)
10 | await onNewDoc(name)
11 | setSaving(false)
12 | setName('')
13 | close()
14 | }
15 |
16 | return (
17 |
29 | )
30 | }
31 |
32 | export default NewDocDialog
33 |
--------------------------------------------------------------------------------
/components/newFolderButton.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react'
2 | import { Icon, PlusIcon, Pane, Tooltip } from 'evergreen-ui'
3 |
4 | const NewFolderButton: FC<{ onClick: any; tooltip?: string; size?: number }> = ({ onClick, tooltip, size }) => {
5 | return (
6 |
7 |
8 |
9 |
10 |
11 | )
12 | }
13 |
14 | NewFolderButton.defaultProps = {
15 | tooltip: 'New Folder',
16 | size: 42,
17 | }
18 |
19 | export default NewFolderButton
20 |
--------------------------------------------------------------------------------
/components/newFolderDialog.tsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from 'react'
2 | import { Dialog, TextInput } from 'evergreen-ui'
3 |
4 | const NewFolderDialog = ({ onNewFolder, close, ...props }) => {
5 | const [name, setName] = useState('')
6 | const [saving, setSaving] = useState(false)
7 |
8 | const handleNewFolder = async () => {
9 | setSaving(true)
10 | await onNewFolder(name)
11 | setSaving(false)
12 | setName('')
13 | close()
14 | }
15 |
16 | return (
17 |
29 | )
30 | }
31 |
32 | export default NewFolderDialog
33 |
--------------------------------------------------------------------------------
/components/postPreview.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react'
2 | import { Pane, Heading, Paragraph, Button, majorScale } from 'evergreen-ui'
3 | import Link from 'next/link'
4 |
5 | const PostPreview: FC<{ post: { title: string; summary: string; slug: string } }> = ({ post }) => {
6 | return (
7 |
8 |
9 | {post.title}
10 |
11 | {post.summary}
12 |
13 |
14 |
15 |
18 |
19 |
20 |
21 |
22 | )
23 | }
24 | export default PostPreview
25 |
--------------------------------------------------------------------------------
/components/socialButton.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Pane, Card } from 'evergreen-ui'
3 | import GithubIcon from './githubIcon'
4 |
5 | const icons = { github: GithubIcon }
6 | const SocialButton = ({ type, onClick }) => {
7 | const Icon = icons[type] || GithubIcon
8 |
9 | return (
10 |
24 |
25 |
26 |
27 | {'Continue with '}
28 | {type}
29 |
30 |
31 |
32 | )
33 | }
34 |
35 | export default SocialButton
36 |
--------------------------------------------------------------------------------
/components/user.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react'
2 | import { Pane, Position, Avatar, Popover, Menu, LogOutIcon, majorScale, Text } from 'evergreen-ui'
3 | import { signOut } from 'next-auth/client'
4 | import { UserSession } from '../types'
5 |
6 | const User: FC<{ user: UserSession }> = ({ user }) => {
7 | return (
8 |
9 |
13 |
14 |
15 | {user.name}
16 |
17 |
18 | {user.email}
19 |
20 |
21 |
22 |
27 |
28 |
29 | }
30 | >
31 |
39 |
40 |
41 |
42 |
43 | )
44 | }
45 |
46 | export default User
47 |
--------------------------------------------------------------------------------
/content.ts:
--------------------------------------------------------------------------------
1 | export const home = {
2 | draft: {
3 | hero: {
4 | title: 'Hmm, need something clever here',
5 | body: 'blah blah blah, our product is the best!',
6 | },
7 | features: [
8 | { title: 'Feature 1', body: 'something dope about feature 1 here soon.' },
9 | { title: 'Feature 2', body: 'something dope about feature 2 here soon.' },
10 | ],
11 | },
12 | published: {
13 | hero: {
14 | title: 'A beautiful knowledge base for your whole team.',
15 | body: 'High performing teams use Known to document and record everything. Some other cool SaaS tag line here.',
16 | },
17 | features: [
18 | {
19 | title: 'Next gen editor',
20 | body:
21 | 'Forget about markdown and rich text. Our editor is superchared to handle any content you can throug at it.',
22 | },
23 | { title: 'Stay organized', body: 'Use folders to put your docs right where you need them when you need them.' },
24 | { title: 'Beautiful design', body: 'Award wining design that you will fall in love with.' },
25 | ],
26 | },
27 | }
28 |
29 | export const posts = {
30 | draft: [
31 | `---
32 | title: "We're hiring"
33 | summary: 'Will come up with summary later'
34 | slug: 'we-are-hiring'
35 | publsihedOn: ''
36 | ---
37 |
38 | ## nice post, who dis
39 | `,
40 | `---
41 | title: Why you should write down everything
42 | summary: Will come up with summary later
43 | slug: why-you-should-write-down-everything
44 | publsihedOn: ''
45 | ---
46 | ## Writing is fun`,
47 | ],
48 | published: [
49 | `---
50 | title: "We're hiring"
51 | summary: Come work at a really nice company.
52 | slug: we-are-hiring
53 | publsihedOn: '12-03-2020'
54 | ---
55 | ## Come with with us
56 | `,
57 | `---
58 | title: Why you should write down everything
59 | summary: If you write it down, you won't forget.
60 | slug: why-you-should-write-down-everything
61 | publsihedOn: '3-20-2020'
62 | ---
63 | ## Elon musk writes`,
64 | ],
65 | }
66 |
--------------------------------------------------------------------------------
/db/connect.ts:
--------------------------------------------------------------------------------
1 | import { Db, MongoClient } from 'mongodb'
2 |
3 | /**
4 | * We have to cache the DB connection
5 | * when used in a serverless environment otherwise
6 | * we may encounter performance loss due to
7 | * time to connect. Also, depending on your DB,
8 | * you might night be able to have many concurrent
9 | * DB connections. Most traditional DBs were not made for a stateless
10 | * environment like serverless. A serverless DB (HTTP based DB) whould work
11 | * better.
12 | */
13 | global.mongo = global.mongo || {}
14 |
15 | export const connectToDB = async () => {
16 | const db = {}
17 |
18 | return { db, dbClient: global.mongo.client }
19 | }
20 |
--------------------------------------------------------------------------------
/db/doc.ts:
--------------------------------------------------------------------------------
1 | import { Db } from 'mongodb'
2 | import { nanoid } from 'nanoid'
3 |
4 | export const getOneDoc = async (db: Db, id: string) => {}
5 |
6 | export const getDocsByFolder = async (db: Db, folderId: string) => {}
7 |
8 | export const createDoc = async (db: Db, doc: { createdBy: string; folder: string; name: string; content?: any }) => {}
9 |
10 | export const updateOne = async (db: Db, id: string, updates: any) => {}
11 |
--------------------------------------------------------------------------------
/db/folder.ts:
--------------------------------------------------------------------------------
1 | import { Db } from 'mongodb'
2 | import { nanoid } from 'nanoid'
3 |
4 | export const createFolder = async (db: Db, folder: { createdBy: string; name: string }) => {}
5 |
6 | export const getFolders = async (db: Db, userId: string) => {}
7 |
--------------------------------------------------------------------------------
/db/index.ts:
--------------------------------------------------------------------------------
1 | import * as doc from './doc'
2 | import * as folder from './folder'
3 | import * as user from './user'
4 |
5 | export { connectToDB } from './connect'
6 | export { doc, folder, user }
7 |
--------------------------------------------------------------------------------
/db/user.ts:
--------------------------------------------------------------------------------
1 | import { Db } from 'mongodb'
2 |
3 | export const getUserById = async (db: Db, id: string) => {}
4 |
--------------------------------------------------------------------------------
/env.local:
--------------------------------------------------------------------------------
1 | GITHUB_ID=
2 | GITHUB_SECRET=
3 | DATABASE_URL=
4 | JWT_SECRET=
5 | NEXT_PUBLIC_API_HOST=
6 |
--------------------------------------------------------------------------------
/middleware/all.ts:
--------------------------------------------------------------------------------
1 | import nc from 'next-connect'
2 | import db from './db'
3 | import auth from './auth'
4 |
5 | const middleware = nc()
6 |
7 | middleware.use(db).use(auth)
8 | export default middleware
9 |
--------------------------------------------------------------------------------
/middleware/auth.ts:
--------------------------------------------------------------------------------
1 | import jwt from 'next-auth/jwt'
2 |
3 | export default async (req, res, next) => {
4 | const token = await jwt.getToken({ req, secret: process.env.JWT_SECRET })
5 |
6 | if (token) {
7 | // Signed in
8 | req.user = token
9 | next()
10 | } else {
11 | // Not Signed in
12 | res.status(401)
13 | res.end()
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/middleware/db.ts:
--------------------------------------------------------------------------------
1 | import { connectToDB } from '../db/connect'
2 |
3 | declare global {
4 | namespace NodeJS {
5 | interface Global {
6 | mongo: any
7 | }
8 | }
9 | }
10 |
11 | export default async function database(req, res, next) {
12 | const { db, dbClient } = await connectToDB()
13 | req.db = db
14 | req.dbClinet = dbClient
15 |
16 | next()
17 | }
18 |
--------------------------------------------------------------------------------
/middleware/error.ts:
--------------------------------------------------------------------------------
1 | export default async function onError(error, req, res, next) {
2 | console.log(error)
3 | res.status(500).end()
4 | }
5 |
--------------------------------------------------------------------------------
/middleware/index.ts:
--------------------------------------------------------------------------------
1 | export { default as db } from './db'
2 | export { default as auth } from './auth'
3 |
--------------------------------------------------------------------------------
/next-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
--------------------------------------------------------------------------------
/next.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | typescript: {
3 | // !! WARN !!
4 | // Dangerously allow production builds to successfully complete even if
5 | // your project has type errors.
6 | // !! WARN !!
7 | ignoreBuildErrors: true,
8 | },
9 | }
10 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "app",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start"
9 | },
10 | "dependencies": {
11 | "@bomdi/codebox": "^1.0.10",
12 | "@editorjs/checklist": "^1.3.0",
13 | "@editorjs/code": "^2.6.0",
14 | "@editorjs/delimiter": "^1.2.0",
15 | "@editorjs/editorjs": "^2.19.0",
16 | "@editorjs/embed": "^2.4.0",
17 | "@editorjs/header": "^2.6.1",
18 | "@editorjs/image": "^2.6.0",
19 | "@editorjs/inline-code": "^1.3.1",
20 | "@editorjs/link": "^2.3.1",
21 | "@editorjs/list": "^1.6.1",
22 | "@editorjs/marker": "^1.2.2",
23 | "@editorjs/paragraph": "^2.8.0",
24 | "@editorjs/quote": "^2.4.0",
25 | "@editorjs/raw": "^2.3.0",
26 | "@editorjs/simple-image": "^1.4.0",
27 | "@editorjs/table": "^1.3.0",
28 | "@editorjs/warning": "^1.2.0",
29 | "@react-hook/throttle": "^2.2.0",
30 | "evergreen-ui": "^5.1.2",
31 | "gray-matter": "^4.0.2",
32 | "lodash.orderby": "^4.6.0",
33 | "mongodb": "^3.6.3",
34 | "morgan": "^1.10.0",
35 | "nanoid": "^3.1.18",
36 | "next": "10.0.3",
37 | "next-auth": "^3.1.0",
38 | "next-connect": "^0.9.1",
39 | "next-mdx-remote": "^1.0.0",
40 | "react": "17.0.1",
41 | "react-dom": "17.0.1",
42 | "react-editor-js": "^1.7.0",
43 | "reflect-metadata": "^0.1.13",
44 | "typeorm": "^0.2.29"
45 | },
46 | "devDependencies": {
47 | "@types/mongodb": "^3.5.34",
48 | "@types/mongoose": "^5.10.1",
49 | "@types/node": "^14.14.10",
50 | "@typescript-eslint/parser": "^4.8.2",
51 | "eslint-plugin-import": "^2.22.1",
52 | "eslint-plugin-jest": "^24.1.3",
53 | "jest": "^26.6.3",
54 | "typescript": "^4.1.2"
55 | },
56 | "peerOptionalDependencies": {
57 | "mongodb": "^3.6.3"
58 | },
59 | "resolutions": {
60 | "typeorm": "0.2.28"
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/pages/_app.tsx:
--------------------------------------------------------------------------------
1 | // fixes a bug for next-auth and mongodb atlas somehow
2 | // https://github.com/nextauthjs/next-auth/issues/833
3 | import 'reflect-metadata'
4 | import React from 'react'
5 | import '../styles/globals.css'
6 |
7 | function MyApp({ Component, pageProps }) {
8 | return
9 | }
10 |
11 | export default MyApp
12 |
--------------------------------------------------------------------------------
/pages/_document.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import Document, { Head, Main, NextScript, Html } from 'next/document'
3 | import { extractStyles } from 'evergreen-ui'
4 |
5 | export default class MyDocument extends Document {
6 | static getInitialProps({ renderPage }) {
7 | const page = renderPage()
8 | const { css, hydrationScript } = extractStyles()
9 |
10 | return {
11 | ...page,
12 | css,
13 | hydrationScript,
14 | }
15 | }
16 |
17 | render() {
18 | const { css, hydrationScript } = this.props
19 |
20 | return (
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | {hydrationScript}
29 |
30 |
31 |
32 | )
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/pages/app/[[...id]].tsx:
--------------------------------------------------------------------------------
1 | import React, { FC, useState } from 'react'
2 | import { Pane, Dialog, majorScale } from 'evergreen-ui'
3 | import { useRouter } from 'next/router'
4 | import Logo from '../../components/logo'
5 | import FolderList from '../../components/folderList'
6 | import NewFolderButton from '../../components/newFolderButton'
7 | import User from '../../components/user'
8 | import FolderPane from '../../components/folderPane'
9 | import DocPane from '../../components/docPane'
10 | import NewFolderDialog from '../../components/newFolderDialog'
11 |
12 | const App: FC<{ folders?: any[]; activeFolder?: any; activeDoc?: any; activeDocs?: any[] }> = ({
13 | folders,
14 | activeDoc,
15 | activeFolder,
16 | activeDocs,
17 | }) => {
18 | const router = useRouter()
19 | const [newFolderIsShown, setIsShown] = useState(false)
20 |
21 | const Page = () => {
22 | if (activeDoc) {
23 | return
24 | }
25 |
26 | if (activeFolder) {
27 | return
28 | }
29 |
30 | return null
31 | }
32 |
33 | if (false) {
34 | return (
35 |
47 | )
48 | }
49 |
50 | return (
51 |
52 |
53 |
54 |
55 |
56 | setIsShown(true)} />
57 |
58 |
59 | {' '}
60 |
61 |
62 |
63 |
64 |
65 |
66 | setIsShown(false)} isShown={newFolderIsShown} onNewFolder={() => {}} />
67 |
68 | )
69 | }
70 |
71 | App.defaultProps = {
72 | folders: [],
73 | }
74 |
75 | /**
76 | * Catch all handler. Must handle all different page
77 | * states.
78 | * 1. Folders - none selected
79 | * 2. Folders => Folder selected
80 | * 3. Folders => Folder selected => Document selected
81 | *
82 | * An unauth user should not be able to access this page.
83 | *
84 | * @param context
85 | */
86 | export default App
87 |
--------------------------------------------------------------------------------
/pages/blog/[slug].tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react'
2 | import hydrate from 'next-mdx-remote/hydrate'
3 | import { majorScale, Pane, Heading, Spinner } from 'evergreen-ui'
4 | import Head from 'next/head'
5 | import { useRouter } from 'next/router'
6 | import { Post } from '../../types'
7 | import Container from '../../components/container'
8 | import HomeNav from '../../components/homeNav'
9 |
10 | const BlogPost: FC = ({ source, frontMatter }) => {
11 | const content = hydrate(source)
12 | const router = useRouter()
13 |
14 | if (router.isFallback) {
15 | return (
16 |
17 |
18 |
19 | )
20 | }
21 | return (
22 |
23 |
24 | {`Known Blog | ${frontMatter.title}`}
25 |
26 |
27 |
30 |
31 |
32 |
33 | {frontMatter.title}
34 |
35 | {content}
36 |
37 |
38 |
39 | )
40 | }
41 |
42 | BlogPost.defaultProps = {
43 | source: '',
44 | frontMatter: { title: 'default title', summary: 'summary', publishedOn: '' },
45 | }
46 |
47 | /**
48 | * Need to get the paths here
49 | * then the the correct post for the matching path
50 | * Posts can come from the fs or our CMS
51 | */
52 | export default BlogPost
53 |
--------------------------------------------------------------------------------
/pages/blog/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Pane, majorScale } from 'evergreen-ui'
3 | import matter from 'gray-matter'
4 | import path from 'path'
5 | import fs from 'fs'
6 | import orderby from 'lodash.orderby'
7 | import Container from '../../components/container'
8 | import HomeNav from '../../components/homeNav'
9 | import PostPreview from '../../components/postPreview'
10 | import { posts as postsFromCMS } from '../../content'
11 |
12 | const Blog = ({ posts }) => {
13 | return (
14 |
15 |
18 |
19 |
20 | {posts.map((post) => (
21 |
22 |
23 |
24 | ))}
25 |
26 |
27 |
28 | )
29 | }
30 |
31 | Blog.defaultProps = {
32 | posts: [],
33 | }
34 |
35 | export default Blog
36 |
37 | /**
38 | * Need to get the posts from the
39 | * fs and our CMS
40 | */
41 |
--------------------------------------------------------------------------------
/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { FC } from 'react'
2 | import { Pane, majorScale } from 'evergreen-ui'
3 | import Container from '../components/container'
4 | import Hero from '../components/hero'
5 | import HomeNav from '../components/homeNav'
6 | import FeatureSection from '../components/featureSection'
7 |
8 | const Home: FC<{ content: { hero: any; features: any[] } }> = ({ content }) => {
9 | return (
10 |
11 |
17 |
18 | {content.features.map((feature, i) => (
19 |
26 | ))}
27 |
28 |
33 |
34 | )
35 | }
36 |
37 | /**
38 | * Should really get this content from our CMS
39 | */
40 |
41 | Home.defaultProps = {
42 | content: {
43 | features: [{ title: 'default feature', body: 'default body' }],
44 | hero: { title: 'default title', body: 'default body' },
45 | },
46 | }
47 |
48 | export default Home
49 |
--------------------------------------------------------------------------------
/pages/signin.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { Pane, majorScale, Text } from 'evergreen-ui'
3 | import Logo from '../components/logo'
4 |
5 | import SocialButton from '../components/socialButton'
6 |
7 | const Signin = () => {
8 | return (
9 |
10 |
21 |
22 |
23 |
24 |
25 | Sign in.
26 |
27 |
28 |
29 |
30 |
39 |
40 | {}} />
41 |
42 |
43 |
44 | )
45 | }
46 |
47 | export default Signin
48 |
--------------------------------------------------------------------------------
/posts/known-raises-series-a.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Known raises series A
3 | publishedOn: "2020-10-23"
4 | author: Naomi Wills
5 | summary: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur
6 | slug: known-raises-series-a
7 | ---
8 | An h1 header
9 | ============
10 |
11 | Paragraphs are separated by a blank line.
12 |
13 | 2nd paragraph. *Italic*, **bold**, and `monospace`. Itemized lists
14 | look like:
15 |
16 | * this one
17 | * that one
18 | * the other one
19 |
20 | Note that --- not considering the asterisk --- the actual text
21 | content starts at 4-columns in.
22 |
23 | > Block quotes are
24 | > written like so.
25 | >
26 | > They can span multiple paragraphs,
27 | > if you like.
28 |
29 | Use 3 dashes for an em-dash. Use 2 dashes for ranges (ex., "it's all
30 | in chapters 12--14"). Three dots ... will be converted to an ellipsis.
31 | Unicode is supported. ☺
32 |
33 |
34 |
35 | An h2 header
36 | ------------
37 |
38 | Here's a numbered list:
39 |
40 | 1. first item
41 | 2. second item
42 | 3. third item
43 |
44 | Note again how the actual text starts at 4 columns in (4 characters
45 | from the left side). Here's a code sample:
46 |
47 | # Let me re-iterate ...
48 | for i in 1 .. 10 { do-something(i) }
49 |
50 | As you probably guessed, indented 4 spaces. By the way, instead of
51 | indenting the block, you can use delimited blocks, if you like:
52 |
53 | ~~~
54 | define foobar() {
55 | print "Welcome to flavor country!";
56 | }
57 | ~~~
58 |
59 | (which makes copying & pasting easier). You can optionally mark the
60 | delimited block for Pandoc to syntax highlight it:
61 |
62 | ~~~python
63 | import time
64 | # Quick, count to ten!
65 | for i in range(10):
66 | # (but not *too* quick)
67 | time.sleep(0.5)
68 | print i
69 | ~~~
70 |
71 |
72 |
73 | ### An h3 header ###
74 |
75 | Now a nested list:
76 |
77 | 1. First, get these ingredients:
78 |
79 | * carrots
80 | * celery
81 | * lentils
82 |
83 | 2. Boil some water.
84 |
85 | 3. Dump everything in the pot and follow
86 | this algorithm:
87 |
88 | find wooden spoon
89 | uncover pot
90 | stir
91 | cover pot
92 | balance wooden spoon precariously on pot handle
93 | wait 10 minutes
94 | goto first step (or shut off burner when done)
95 |
96 | Do not bump wooden spoon or it will fall.
97 |
98 | Notice again how text always lines up on 4-space indents (including
99 | that last line which continues item 3 above).
100 |
101 | Here's a link to [a website](http://foo.bar), to a [local
102 | doc](local-doc.html), and to a [section heading in the current
103 | doc](#an-h2-header). Here's a footnote [^1].
104 |
105 | [^1]: Footnote text goes here.
106 |
107 | Tables can look like this:
108 |
109 | size material color
110 | ---- ------------ ------------
111 | 9 leather brown
112 | 10 hemp canvas natural
113 | 11 glass transparent
114 |
115 | Table: Shoes, their sizes, and what they're made of
116 |
117 | (The above is the caption for the table.) Pandoc also supports
118 | multi-line tables:
119 |
120 | -------- -----------------------
121 | keyword text
122 | -------- -----------------------
123 | red Sunsets, apples, and
124 | other red or reddish
125 | things.
126 |
127 | green Leaves, grass, frogs
128 | and other things it's
129 | not easy being.
130 | -------- -----------------------
131 |
132 | A horizontal rule follows.
133 |
134 | ***
135 |
136 | Here's a definition list:
137 |
138 | apples
139 | : Good for making applesauce.
140 | oranges
141 | : Citrus!
142 | tomatoes
143 | : There's no "e" in tomatoe.
144 |
145 | Again, text is indented 4 spaces. (Put a blank line between each
146 | term/definition pair to spread things out more.)
147 |
148 | Here's a "line block":
149 |
150 | | Line one
151 | | Line too
152 | | Line tree
153 |
154 | and images can be specified like so:
155 |
156 | 
157 |
158 | Inline math equations go in like so: $\omega = d\phi / dt$. Display
159 | math should get its own line and be put in in double-dollarsigns:
160 |
161 | $$I = \int \rho R^{2} dV$$
162 |
163 | And note that you can backslash-escape any punctuation characters
164 | which you wish to be displayed literally, ex.: \`foo\`, \*bar\*, etc.
165 |
--------------------------------------------------------------------------------
/posts/top-10-wiki-apps.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Top 10 wiki apps
3 | publishedOn: "2020-11-11"
4 | author: Naomi Wills
5 | summary: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur
6 | slug: top-10-wiki-apps
7 | ---
8 | An h1 header
9 | ============
10 |
11 | Paragraphs are separated by a blank line.
12 |
13 | 2nd paragraph. *Italic*, **bold**, and `monospace`. Itemized lists
14 | look like:
15 |
16 | * this one
17 | * that one
18 | * the other one
19 |
20 | Note that --- not considering the asterisk --- the actual text
21 | content starts at 4-columns in.
22 |
23 | > Block quotes are
24 | > written like so.
25 | >
26 | > They can span multiple paragraphs,
27 | > if you like.
28 |
29 | Use 3 dashes for an em-dash. Use 2 dashes for ranges (ex., "it's all
30 | in chapters 12--14"). Three dots ... will be converted to an ellipsis.
31 | Unicode is supported. ☺
32 |
33 |
34 |
35 | An h2 header
36 | ------------
37 |
38 | Here's a numbered list:
39 |
40 | 1. first item
41 | 2. second item
42 | 3. third item
43 |
44 | Note again how the actual text starts at 4 columns in (4 characters
45 | from the left side). Here's a code sample:
46 |
47 | # Let me re-iterate ...
48 | for i in 1 .. 10 { do-something(i) }
49 |
50 | As you probably guessed, indented 4 spaces. By the way, instead of
51 | indenting the block, you can use delimited blocks, if you like:
52 |
53 | ~~~
54 | define foobar() {
55 | print "Welcome to flavor country!";
56 | }
57 | ~~~
58 |
59 | (which makes copying & pasting easier). You can optionally mark the
60 | delimited block for Pandoc to syntax highlight it:
61 |
62 | ~~~python
63 | import time
64 | # Quick, count to ten!
65 | for i in range(10):
66 | # (but not *too* quick)
67 | time.sleep(0.5)
68 | print i
69 | ~~~
70 |
71 |
72 |
73 | ### An h3 header ###
74 |
75 | Now a nested list:
76 |
77 | 1. First, get these ingredients:
78 |
79 | * carrots
80 | * celery
81 | * lentils
82 |
83 | 2. Boil some water.
84 |
85 | 3. Dump everything in the pot and follow
86 | this algorithm:
87 |
88 | find wooden spoon
89 | uncover pot
90 | stir
91 | cover pot
92 | balance wooden spoon precariously on pot handle
93 | wait 10 minutes
94 | goto first step (or shut off burner when done)
95 |
96 | Do not bump wooden spoon or it will fall.
97 |
98 | Notice again how text always lines up on 4-space indents (including
99 | that last line which continues item 3 above).
100 |
101 | Here's a link to [a website](http://foo.bar), to a [local
102 | doc](local-doc.html), and to a [section heading in the current
103 | doc](#an-h2-header). Here's a footnote [^1].
104 |
105 | [^1]: Footnote text goes here.
106 |
107 | Tables can look like this:
108 |
109 | size material color
110 | ---- ------------ ------------
111 | 9 leather brown
112 | 10 hemp canvas natural
113 | 11 glass transparent
114 |
115 | Table: Shoes, their sizes, and what they're made of
116 |
117 | (The above is the caption for the table.) Pandoc also supports
118 | multi-line tables:
119 |
120 | -------- -----------------------
121 | keyword text
122 | -------- -----------------------
123 | red Sunsets, apples, and
124 | other red or reddish
125 | things.
126 |
127 | green Leaves, grass, frogs
128 | and other things it's
129 | not easy being.
130 | -------- -----------------------
131 |
132 | A horizontal rule follows.
133 |
134 | ***
135 |
136 | Here's a definition list:
137 |
138 | apples
139 | : Good for making applesauce.
140 | oranges
141 | : Citrus!
142 | tomatoes
143 | : There's no "e" in tomatoe.
144 |
145 | Again, text is indented 4 spaces. (Put a blank line between each
146 | term/definition pair to spread things out more.)
147 |
148 | Here's a "line block":
149 |
150 | | Line one
151 | | Line too
152 | | Line tree
153 |
154 | and images can be specified like so:
155 |
156 | 
157 |
158 | Inline math equations go in like so: $\omega = d\phi / dt$. Display
159 | math should get its own line and be put in in double-dollarsigns:
160 |
161 | $$I = \int \rho R^{2} dV$$
162 |
163 | And note that you can backslash-escape any punctuation characters
164 | which you wish to be displayed literally, ex.: \`foo\`, \*bar\*, etc.
165 |
--------------------------------------------------------------------------------
/posts/whats-next-for-known.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: What's next for Known
3 | publishedOn: "2020-11-04"
4 | author: Naomi Wills
5 | summary: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur
6 | slug: whats-next-for-known
7 | ---
8 | An h1 header
9 | ============
10 |
11 | Paragraphs are separated by a blank line.
12 |
13 | 2nd paragraph. *Italic*, **bold**, and `monospace`. Itemized lists
14 | look like:
15 |
16 | * this one
17 | * that one
18 | * the other one
19 |
20 | Note that --- not considering the asterisk --- the actual text
21 | content starts at 4-columns in.
22 |
23 | > Block quotes are
24 | > written like so.
25 | >
26 | > They can span multiple paragraphs,
27 | > if you like.
28 |
29 | Use 3 dashes for an em-dash. Use 2 dashes for ranges (ex., "it's all
30 | in chapters 12--14"). Three dots ... will be converted to an ellipsis.
31 | Unicode is supported. ☺
32 |
33 |
34 |
35 | An h2 header
36 | ------------
37 |
38 | Here's a numbered list:
39 |
40 | 1. first item
41 | 2. second item
42 | 3. third item
43 |
44 | Note again how the actual text starts at 4 columns in (4 characters
45 | from the left side). Here's a code sample:
46 |
47 | # Let me re-iterate ...
48 | for i in 1 .. 10 { do-something(i) }
49 |
50 | As you probably guessed, indented 4 spaces. By the way, instead of
51 | indenting the block, you can use delimited blocks, if you like:
52 |
53 | ~~~
54 | define foobar() {
55 | print "Welcome to flavor country!";
56 | }
57 | ~~~
58 |
59 | (which makes copying & pasting easier). You can optionally mark the
60 | delimited block for Pandoc to syntax highlight it:
61 |
62 | ~~~python
63 | import time
64 | # Quick, count to ten!
65 | for i in range(10):
66 | # (but not *too* quick)
67 | time.sleep(0.5)
68 | print i
69 | ~~~
70 |
71 |
72 |
73 | ### An h3 header ###
74 |
75 | Now a nested list:
76 |
77 | 1. First, get these ingredients:
78 |
79 | * carrots
80 | * celery
81 | * lentils
82 |
83 | 2. Boil some water.
84 |
85 | 3. Dump everything in the pot and follow
86 | this algorithm:
87 |
88 | find wooden spoon
89 | uncover pot
90 | stir
91 | cover pot
92 | balance wooden spoon precariously on pot handle
93 | wait 10 minutes
94 | goto first step (or shut off burner when done)
95 |
96 | Do not bump wooden spoon or it will fall.
97 |
98 | Notice again how text always lines up on 4-space indents (including
99 | that last line which continues item 3 above).
100 |
101 | Here's a link to [a website](http://foo.bar), to a [local
102 | doc](local-doc.html), and to a [section heading in the current
103 | doc](#an-h2-header). Here's a footnote [^1].
104 |
105 | [^1]: Footnote text goes here.
106 |
107 | Tables can look like this:
108 |
109 | size material color
110 | ---- ------------ ------------
111 | 9 leather brown
112 | 10 hemp canvas natural
113 | 11 glass transparent
114 |
115 | Table: Shoes, their sizes, and what they're made of
116 |
117 | (The above is the caption for the table.) Pandoc also supports
118 | multi-line tables:
119 |
120 | -------- -----------------------
121 | keyword text
122 | -------- -----------------------
123 | red Sunsets, apples, and
124 | other red or reddish
125 | things.
126 |
127 | green Leaves, grass, frogs
128 | and other things it's
129 | not easy being.
130 | -------- -----------------------
131 |
132 | A horizontal rule follows.
133 |
134 | ***
135 |
136 | Here's a definition list:
137 |
138 | apples
139 | : Good for making applesauce.
140 | oranges
141 | : Citrus!
142 | tomatoes
143 | : There's no "e" in tomatoe.
144 |
145 | Again, text is indented 4 spaces. (Put a blank line between each
146 | term/definition pair to spread things out more.)
147 |
148 | Here's a "line block":
149 |
150 | | Line one
151 | | Line too
152 | | Line tree
153 |
154 | and images can be specified like so:
155 |
156 | 
157 |
158 | Inline math equations go in like so: $\omega = d\phi / dt$. Display
159 | math should get its own line and be put in in double-dollarsigns:
160 |
161 | $$I = \int \rho R^{2} dV$$
162 |
163 | And note that you can backslash-escape any punctuation characters
164 | which you wish to be displayed literally, ex.: \`foo\`, \*bar\*, etc.
165 |
--------------------------------------------------------------------------------
/posts/your-team-needs-docs.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Your team needs docs
3 | publishedOn: "2020-12-01"
4 | author: Naomi Wills
5 | summary: Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur
6 | slug: your-team-needs-docs
7 | ---
8 | An h1 header
9 | ============
10 |
11 | Paragraphs are separated by a blank line.
12 |
13 | 2nd paragraph. *Italic*, **bold**, and `monospace`. Itemized lists
14 | look like:
15 |
16 | * this one
17 | * that one
18 | * the other one
19 |
20 | Note that --- not considering the asterisk --- the actual text
21 | content starts at 4-columns in.
22 |
23 | > Block quotes are
24 | > written like so.
25 | >
26 | > They can span multiple paragraphs,
27 | > if you like.
28 |
29 | Use 3 dashes for an em-dash. Use 2 dashes for ranges (ex., "it's all
30 | in chapters 12--14"). Three dots ... will be converted to an ellipsis.
31 | Unicode is supported. ☺
32 |
33 |
34 |
35 | An h2 header
36 | ------------
37 |
38 | Here's a numbered list:
39 |
40 | 1. first item
41 | 2. second item
42 | 3. third item
43 |
44 | Note again how the actual text starts at 4 columns in (4 characters
45 | from the left side). Here's a code sample:
46 |
47 | # Let me re-iterate ...
48 | for i in 1 .. 10 { do-something(i) }
49 |
50 | As you probably guessed, indented 4 spaces. By the way, instead of
51 | indenting the block, you can use delimited blocks, if you like:
52 |
53 | ~~~
54 | define foobar() {
55 | print "Welcome to flavor country!";
56 | }
57 | ~~~
58 |
59 | (which makes copying & pasting easier). You can optionally mark the
60 | delimited block for Pandoc to syntax highlight it:
61 |
62 | ~~~python
63 | import time
64 | # Quick, count to ten!
65 | for i in range(10):
66 | # (but not *too* quick)
67 | time.sleep(0.5)
68 | print i
69 | ~~~
70 |
71 |
72 |
73 | ### An h3 header ###
74 |
75 | Now a nested list:
76 |
77 | 1. First, get these ingredients:
78 |
79 | * carrots
80 | * celery
81 | * lentils
82 |
83 | 2. Boil some water.
84 |
85 | 3. Dump everything in the pot and follow
86 | this algorithm:
87 |
88 | find wooden spoon
89 | uncover pot
90 | stir
91 | cover pot
92 | balance wooden spoon precariously on pot handle
93 | wait 10 minutes
94 | goto first step (or shut off burner when done)
95 |
96 | Do not bump wooden spoon or it will fall.
97 |
98 | Notice again how text always lines up on 4-space indents (including
99 | that last line which continues item 3 above).
100 |
101 | Here's a link to [a website](http://foo.bar), to a [local
102 | doc](local-doc.html), and to a [section heading in the current
103 | doc](#an-h2-header). Here's a footnote [^1].
104 |
105 | [^1]: Footnote text goes here.
106 |
107 | Tables can look like this:
108 |
109 | size material color
110 | ---- ------------ ------------
111 | 9 leather brown
112 | 10 hemp canvas natural
113 | 11 glass transparent
114 |
115 | Table: Shoes, their sizes, and what they're made of
116 |
117 | (The above is the caption for the table.) Pandoc also supports
118 | multi-line tables:
119 |
120 | -------- -----------------------
121 | keyword text
122 | -------- -----------------------
123 | red Sunsets, apples, and
124 | other red or reddish
125 | things.
126 |
127 | green Leaves, grass, frogs
128 | and other things it's
129 | not easy being.
130 | -------- -----------------------
131 |
132 | A horizontal rule follows.
133 |
134 | ***
135 |
136 | Here's a definition list:
137 |
138 | apples
139 | : Good for making applesauce.
140 | oranges
141 | : Citrus!
142 | tomatoes
143 | : There's no "e" in tomatoe.
144 |
145 | Again, text is indented 4 spaces. (Put a blank line between each
146 | term/definition pair to spread things out more.)
147 |
148 | Here's a "line block":
149 |
150 | | Line one
151 | | Line too
152 | | Line tree
153 |
154 | and images can be specified like so:
155 |
156 | 
157 |
158 | Inline math equations go in like so: $\omega = d\phi / dt$. Display
159 | math should get its own line and be put in in double-dollarsigns:
160 |
161 | $$I = \int \rho R^{2} dV$$
162 |
163 | And note that you can backslash-escape any punctuation characters
164 | which you wish to be displayed literally, ex.: \`foo\`, \*bar\*, etc.
165 |
--------------------------------------------------------------------------------
/public/docs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hendrixer/production-grade-nextjs/5d15bdccef5c766908ae4bf0c55a979134a74865/public/docs.png
--------------------------------------------------------------------------------
/public/editor.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hendrixer/production-grade-nextjs/5d15bdccef5c766908ae4bf0c55a979134a74865/public/editor.png
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Hendrixer/production-grade-nextjs/5d15bdccef5c766908ae4bf0c55a979134a74865/public/favicon.ico
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/styles/globals.css:
--------------------------------------------------------------------------------
1 | html,
2 | body {
3 | padding: 0;
4 | margin: 0;
5 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
6 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
7 | }
8 |
9 | a {
10 | color: inherit;
11 | text-decoration: none;
12 | }
13 |
14 | * {
15 | box-sizing: border-box;
16 | }
17 |
18 | .ce-block__content {
19 | margin: 0;
20 | }
21 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "strict": false,
12 | "forceConsistentCasingInFileNames": true,
13 | "noEmit": true,
14 | "esModuleInterop": true,
15 | "module": "esnext",
16 | "moduleResolution": "node",
17 | "resolveJsonModule": true,
18 | "isolatedModules": true,
19 | "jsx": "preserve"
20 | },
21 | "include": [
22 | "next-env.d.ts",
23 | "**/*.ts",
24 | "**/*.tsx"
25 | ],
26 | "exclude": [
27 | "node_modules"
28 | ]
29 | }
30 |
--------------------------------------------------------------------------------
/types.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.module.css' {
2 | const classes: { [key: string]: string }
3 | export default classes
4 | }
5 |
--------------------------------------------------------------------------------
/types.ts:
--------------------------------------------------------------------------------
1 | import { Db, MongoClient } from 'mongodb'
2 | import { NextApiRequest, NextApiResponse } from 'next'
3 |
4 | export interface PostFrontMatter {
5 | title: string
6 | summary: string
7 | publishedOn: string
8 | }
9 |
10 | export interface Post {
11 | source: string
12 | frontMatter: PostFrontMatter
13 | }
14 |
15 | export interface UserSession {
16 | id: string
17 | image: string
18 | email: string
19 | name: string
20 | }
21 |
22 | export interface Request extends NextApiRequest {
23 | db: Db
24 | dbClient: MongoClient
25 | user: { email: string; id: string }
26 | }
27 |
--------------------------------------------------------------------------------
/utils/gradients.ts:
--------------------------------------------------------------------------------
1 | const gradients = [
2 | { bg: '#4158D0', image: 'linear-gradient(43deg, #4158D0 0%, #C850C0 46%, #FFCC70 100%)' },
3 | { bg: '#0093E9', image: 'linear-gradient(160deg, #0093E9 0%, #80D0C7 100%);' },
4 | { bg: '#8EC5FC', image: 'linear-gradient(62deg, #8EC5FC 0%, #E0C3FC 100%)' },
5 | { bg: '#85FFBD', image: 'linear-gradient(45deg, #85FFBD 0%, #FFFB7D 100%)' },
6 | ]
7 |
8 | export const getRandomGradientCss = () => gradients[Math.floor(Math.random() * gradients.length)]
9 |
--------------------------------------------------------------------------------
/utils/isSSR.ts:
--------------------------------------------------------------------------------
1 | export const isSSR = () => typeof window === 'undefined'
2 |
--------------------------------------------------------------------------------