This commit is contained in:
Max Leiter 2022-11-17 22:36:53 -08:00
parent 4cf448c35d
commit 12d9eafcd9
39 changed files with 223 additions and 231 deletions

View file

@ -1,8 +1,3 @@
{ {
"extends": "next/core-web-vitals", "extends": "next/core-web-vitals"
"settings": {
"next": {
"rootDir": "client/"
}
}
} }

View file

@ -70,7 +70,7 @@ const Auth = ({
onClick={(e) => { onClick={(e) => {
e.preventDefault() e.preventDefault()
signIn("github", { signIn("github", {
callbackUrl: "/", callbackUrl: "/"
}) })
}} }}
> >

View file

@ -1,5 +1,5 @@
import Auth from "../components" import Auth from "../components"
export default function SignInPage() { export default function SignInPage() {
return (<Auth page="signin" />) return <Auth page="signin" />
} }

View file

@ -7,5 +7,5 @@ const getPasscode = async () => {
export default async function SignUpPage() { export default async function SignUpPage() {
const requiresPasscode = await getPasscode() const requiresPasscode = await getPasscode()
return (<Auth page="signup" requiresServerPassword={requiresPasscode} />) return <Auth page="signup" requiresServerPassword={requiresPasscode} />
} }

View file

@ -53,7 +53,9 @@ const FileDropdown = ({ files }: { files: File[] }) => {
<div className={buttonStyles.icon}> <div className={buttonStyles.icon}>
<ChevronDown /> <ChevronDown />
</div> </div>
<span>Jump to {files.length} {files.length === 1 ? "file" : "files"}</span> <span>
Jump to {files.length} {files.length === 1 ? "file" : "files"}
</span>
</Popover.Trigger> </Popover.Trigger>
<Popover.Content>{content}</Popover.Content> <Popover.Content>{content}</Popover.Content>
</Popover> </Popover>

View file

@ -52,7 +52,9 @@ const MarkdownPreview = ({
return ( return (
<> <>
{isLoading ? ( {isLoading ? (
<><Spinner /></> <>
<Spinner />
</>
) : ( ) : (
<StaticPreview preview={preview} height={height} /> <StaticPreview preview={preview} height={height} />
)} )}

View file

@ -1,4 +1,4 @@
import { useMediaQuery, useTheme, useToasts } from "@geist-ui/core/dist" import { useMediaQuery, useTheme, useToasts } from "@geist-ui/core/dist"
import { useDropzone } from "react-dropzone" import { useDropzone } from "react-dropzone"
import styles from "./drag-and-drop.module.css" import styles from "./drag-and-drop.module.css"
import generateUUID from "@lib/generate-uuid" import generateUUID from "@lib/generate-uuid"
@ -78,9 +78,7 @@ function FileDropzone({ setDocs }: { setDocs: (docs: Document[]) => void }) {
{file.name}: {file.name}:
<ul> <ul>
{errors.map((e) => ( {errors.map((e) => (
<li key={e.code}> <li key={e.code}>{e.message}</li>
{e.message}
</li>
))} ))}
</ul> </ul>
</li> </li>
@ -98,7 +96,9 @@ function FileDropzone({ setDocs }: { setDocs: (docs: Document[]) => void }) {
> >
<input {...getInputProps()} /> <input {...getInputProps()} />
{!isDragActive && ( {!isDragActive && (
<p style={{color: "var(--gray)"}}>Drag some files here, or {verb} to select files</p> <p style={{ color: "var(--gray)" }}>
Drag some files here, or {verb} to select files
</p>
)} )}
{isDragActive && <p>Release to drop the files here</p>} {isDragActive && <p>Release to drop the files here</p>}
</div> </div>

View file

@ -14,7 +14,7 @@ import ButtonGroup from "@components/button-group"
// TODO: clean up // TODO: clean up
const FormattingIcons = ({ const FormattingIcons = ({
textareaRef, textareaRef
}: { }: {
textareaRef?: RefObject<TextareaMarkdownRef> textareaRef?: RefObject<TextareaMarkdownRef>
}) => { }) => {

View file

@ -1,8 +1,4 @@
import { import { ChangeEvent, memo, useCallback } from "react"
ChangeEvent,
memo,
useCallback,
} from "react"
import styles from "./document.module.css" import styles from "./document.module.css"
import Trash from "@geist-ui/icons/trash" import Trash from "@geist-ui/icons/trash"
import Button from "@components/button" import Button from "@components/button"

View file

@ -17,26 +17,12 @@
flex: 1; flex: 1;
} }
.title {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
margin-bottom: var(--gap);
}
.description { .description {
width: 100%; width: 100%;
margin-bottom: var(--gap); margin-bottom: var(--gap);
} }
@media screen and (max-width: 650px) { @media screen and (max-width: 650px) {
.title {
align-items: flex-start;
flex-direction: column;
}
.buttons { .buttons {
flex-direction: column; flex-direction: column;
margin: 0; margin: 0;

View file

@ -1,8 +1,7 @@
import { ChangeEvent, memo, useEffect, useState } from "react" import type { ChangeEvent } from "react"
import ShiftBy from "@components/shift-by"
import styles from "../post.module.css"
import Input from "@components/input" import Input from "@components/input"
import styles from "./title.module.css"
const titlePlaceholders = [ const titlePlaceholders = [
"How to...", "How to...",
@ -20,26 +19,22 @@ type props = {
} }
const Title = ({ onChange, title }: props) => { const Title = ({ onChange, title }: props) => {
const [placeholder, setPlaceholder] = useState(titlePlaceholders[0]) const placeholder =
useEffect(() => { titlePlaceholders[Math.floor(Math.random() * titlePlaceholders.length)]
// set random placeholder on load
setPlaceholder(
titlePlaceholders[Math.floor(Math.random() * titlePlaceholders.length)]
)
}, [])
return ( return (
<div className={styles.title}> <div className={styles.title}>
<h1 className={styles.drift}>Drift</h1> <h1>Drift</h1>
<Input <Input
placeholder={placeholder} placeholder={placeholder}
value={title || ""} value={title || ""}
onChange={onChange} onChange={onChange}
height={"55px"} label="Title"
label="Post title" className={styles.labelAndInput}
style={{ width: "100%", fontSize: 18 }} style={{ width: "100%" }}
labelClassName={styles.labelAndInput}
/> />
</div> </div>
) )
} }
export default memo(Title) export default Title

View file

@ -0,0 +1,18 @@
.title {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
margin-bottom: var(--gap);
}
@media screen and (max-width: 650px) {
.title {
align-items: flex-start;
flex-direction: column;
}
}
.labelAndInput {
font-size: 1.2rem;
}

View file

@ -86,7 +86,7 @@ const Document = ({
<Input <Input
id={`${title}`} id={`${title}`}
width={"100%"} width={"100%"}
height={'2rem'} height={"2rem"}
style={{ borderRadius: 0 }} style={{ borderRadius: 0 }}
value={title || "Untitled"} value={title || "Untitled"}
disabled disabled

View file

@ -37,10 +37,7 @@ const getPost = async (id: string) => {
return { post, isAuthor: isAuthorOrAdmin } return { post, isAuthor: isAuthorOrAdmin }
} }
if ( if (post.visibility === "private" && !isAuthorOrAdmin) {
(post.visibility === "private") &&
!isAuthorOrAdmin
) {
return notFound() return notFound()
} }

View file

@ -1,17 +1,13 @@
'use client'; "use client"
import SettingsGroup from "@components/settings-group" import SettingsGroup from "@components/settings-group"
import { Fieldset, useToasts } from "@geist-ui/core/dist" import { Fieldset, useToasts } from "@geist-ui/core/dist"
import byteToMB from "@lib/byte-to-mb" import byteToMB from "@lib/byte-to-mb"
import { PostWithFiles } from "@lib/server/prisma"; import { PostWithFiles } from "@lib/server/prisma"
import Table from "rc-table" import Table from "rc-table"
import { useMemo } from "react" import { useMemo } from "react"
import ActionDropdown from "./action-dropdown" import ActionDropdown from "./action-dropdown"
const PostTable = ({ const PostTable = ({ posts }: { posts: PostWithFiles[] }) => {
posts,
}: {
posts: PostWithFiles[]
}) => {
const tablePosts = useMemo( const tablePosts = useMemo(
() => () =>
posts?.map((post) => { posts?.map((post) => {

View file

@ -26,7 +26,7 @@ const AdminPage = async () => {
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
alignItems: "center", alignItems: "center",
gap: "var(--gap)", gap: "var(--gap)"
}} }}
> >
<UserTable users={users} /> <UserTable users={users} />

View file

@ -48,7 +48,11 @@ const Button = forwardRef<HTMLButtonElement, Props>(
{iconLeft} {iconLeft}
</span> </span>
)} )}
{children ? children : <span className={`${styles.icon}`}>{iconLeft || iconRight}</span>} {children ? (
children
) : (
<span className={`${styles.icon}`}>{iconLeft || iconRight}</span>
)}
{children && iconRight && ( {children && iconRight && (
<span className={`${styles.icon} ${styles.iconRight}`}> <span className={`${styles.icon} ${styles.iconRight}`}>
{iconRight} {iconRight}

View file

@ -17,13 +17,7 @@ const Controls = () => {
return ( return (
<div className={styles.wrapper}> <div className={styles.wrapper}>
<Select <Select scale={0.5} h="28px" pure onChange={switchThemes} value={theme}>
scale={0.5}
h="28px"
pure
onChange={switchThemes}
value={theme}
>
<Select.Option value="light"> <Select.Option value="light">
<span className={styles.selectContent}> <span className={styles.selectContent}>
<SunIcon size={14} /> Light <SunIcon size={14} /> Light

View file

@ -1,10 +1,6 @@
"use client" "use client"
import { import { Page, useBodyScroll, useMediaQuery } from "@geist-ui/core/dist"
Page,
useBodyScroll,
useMediaQuery
} from "@geist-ui/core/dist"
import { useEffect, useState } from "react" import { useEffect, useState } from "react"
import styles from "./header.module.css" import styles from "./header.module.css"

View file

@ -1,5 +1,5 @@
"use client" "use client"
import { Tabs, Textarea } from "@geist-ui/core/dist" import { Tabs, Textarea } from "@geist-ui/core/dist"
import Image from "next/image" import Image from "next/image"
import styles from "./home.module.css" import styles from "./home.module.css"
// TODO:components/new-post/ move these styles // TODO:components/new-post/ move these styles

View file

@ -1,3 +1,4 @@
import clsx from "clsx"
import React from "react" import React from "react"
import styles from "./input.module.css" import styles from "./input.module.css"
@ -5,12 +6,12 @@ type Props = React.HTMLProps<HTMLInputElement> & {
label?: string label?: string
width?: number | string width?: number | string
height?: number | string height?: number | string
labelClassName?: string
} }
// eslint-disable-next-line react/display-name // eslint-disable-next-line react/display-name
const Input = React.forwardRef<HTMLInputElement, Props>( const Input = React.forwardRef<HTMLInputElement, Props>(
({ label, className, width, height, ...props }, ref) => { ({ label, className, width, height, labelClassName, ...props }, ref) => {
const classes = [styles.input, styles.withLabel, className].join(" ")
return ( return (
<div <div
className={styles.wrapper} className={styles.wrapper}
@ -19,10 +20,18 @@ const Input = React.forwardRef<HTMLInputElement, Props>(
height height
}} }}
> >
{label && <label className={styles.label}>{label}</label>} {label && (
<label
aria-labelledby={label}
className={clsx(styles.label, labelClassName)}
>
{label}
</label>
)}
<input <input
ref={ref} ref={ref}
className={classes} id={label}
className={clsx(styles.input, styles.withLabel, className)}
{...props} {...props}
style={{ style={{
width, width,

View file

@ -3,36 +3,33 @@
import * as React from "react" import * as React from "react"
import * as PopoverPrimitive from "@radix-ui/react-popover" import * as PopoverPrimitive from "@radix-ui/react-popover"
import clsx from "clsx" import clsx from "clsx"
import styles from './popover.module.css' import styles from "./popover.module.css"
type PopoverProps = PopoverPrimitive.PopoverProps type PopoverProps = PopoverPrimitive.PopoverProps
export function Popover({ ...props }: PopoverProps) { export function Popover({ ...props }: PopoverProps) {
return <PopoverPrimitive.Root {...props} /> return <PopoverPrimitive.Root {...props} />
} }
Popover.Trigger = React.forwardRef< Popover.Trigger = React.forwardRef<
HTMLButtonElement, HTMLButtonElement,
PopoverPrimitive.PopoverTriggerProps PopoverPrimitive.PopoverTriggerProps
>(function PopoverTrigger({ ...props }, ref) { >(function PopoverTrigger({ ...props }, ref) {
return <PopoverPrimitive.Trigger {...props} ref={ref} /> return <PopoverPrimitive.Trigger {...props} ref={ref} />
}) })
Popover.Portal = PopoverPrimitive.Portal Popover.Portal = PopoverPrimitive.Portal
Popover.Content = React.forwardRef< Popover.Content = React.forwardRef<
HTMLDivElement, HTMLDivElement,
PopoverPrimitive.PopoverContentProps PopoverPrimitive.PopoverContentProps
>(function PopoverContent({ className, ...props }, ref) { >(function PopoverContent({ className, ...props }, ref) {
return ( return (
<PopoverPrimitive.Content <PopoverPrimitive.Content
ref={ref} ref={ref}
align="end" align="end"
className={clsx( className={clsx(styles.root, className)}
styles.root, {...props}
className />
)} )
{...props}
/>
)
}) })

View file

@ -89,7 +89,7 @@ const PostList = ({
const deletePost = useCallback( const deletePost = useCallback(
(postId: string) => async () => { (postId: string) => async () => {
const res = await fetch(`/api/post/${postId}`, { const res = await fetch(`/api/post/${postId}`, {
method: "DELETE", method: "DELETE"
}) })
if (!res.ok) { if (!res.ok) {
@ -112,7 +112,7 @@ const PostList = ({
style={{ maxWidth: 300 }} style={{ maxWidth: 300 }}
/> />
</div> </div>
{!posts && <p style={{color: 'var(--warning)'}}>Failed to load.</p>} {!posts && <p style={{ color: "var(--warning)" }}>Failed to load.</p>}
{!posts?.length && searching && ( {!posts?.length && searching && (
<ul> <ul>
<li> <li>

View file

@ -62,10 +62,18 @@ const ListItem = ({
</Tooltip> </Tooltip>
)} )}
<Tooltip content={"Make a copy"}> <Tooltip content={"Make a copy"}>
<Button iconRight={<Edit />} onClick={editACopy} height={38} /> <Button
iconRight={<Edit />}
onClick={editACopy}
height={38}
/>
</Tooltip> </Tooltip>
<Tooltip content={"Delete"}> <Tooltip content={"Delete"}>
<Button iconRight={<Trash />} onClick={deletePost} height={38} /> <Button
iconRight={<Trash />}
onClick={deletePost}
height={38}
/>
</Tooltip> </Tooltip>
</span> </span>
)} )}

View file

@ -1,11 +1,11 @@
import styles from "./skeleton.module.css" import styles from "./skeleton.module.css"
export default function Skeleton({ export default function Skeleton({
width = 100, width = 100,
height = 24, height = 24
}: { }: {
width?: number | string width?: number | string
height?: number | string height?: number | string
}) { }) {
return <div className={styles.skeleton} style={{ width, height }} /> return <div className={styles.skeleton} style={{ width, height }} />
} }

View file

@ -1,26 +1,28 @@
import type { FunctionComponent, PropsWithChildren } from "react"; import type { FunctionComponent, PropsWithChildren } from "react"
import ThemeClientContextProvider from "./ThemeClientContextProvider"; import ThemeClientContextProvider from "./ThemeClientContextProvider"
import ThemeServerContextProvider, { import ThemeServerContextProvider, {
useServerTheme, useServerTheme
} from "./ThemeServerContextProvider"; } from "./ThemeServerContextProvider"
const ThemeProviderWrapper: FunctionComponent<PropsWithChildren<{}>> = ({ const ThemeProviderWrapper: FunctionComponent<PropsWithChildren<{}>> = ({
children, children
}) => { }) => {
const theme = useServerTheme(); const theme = useServerTheme()
return ( return (
<ThemeClientContextProvider defaultTheme={theme}> <ThemeClientContextProvider defaultTheme={theme}>
{children} {children}
</ThemeClientContextProvider> </ThemeClientContextProvider>
); )
}; }
const ThemeProvider: FunctionComponent<PropsWithChildren<{}>> = ({ children }) => { const ThemeProvider: FunctionComponent<PropsWithChildren<{}>> = ({
return ( children
<ThemeServerContextProvider> }) => {
<ThemeProviderWrapper>{children}</ThemeProviderWrapper> return (
</ThemeServerContextProvider> <ThemeServerContextProvider>
); <ThemeProviderWrapper>{children}</ThemeProviderWrapper>
}; </ThemeServerContextProvider>
)
}
export default ThemeProvider; export default ThemeProvider

View file

@ -1,24 +1,22 @@
import type { FunctionComponent, PropsWithChildren } from "react"; import type { FunctionComponent, PropsWithChildren } from "react"
// @ts-ignore -- createServerContext is not in @types/react atm // @ts-ignore -- createServerContext is not in @types/react atm
import { useContext, createServerContext } from "react"; import { useContext, createServerContext } from "react"
import { cookies } from "next/headers"; import { cookies } from "next/headers"
import { Theme, THEME_COOKIE_NAME } from "./theme"; import { Theme, THEME_COOKIE_NAME } from "./theme"
import { DEFAULT_THEME } from "./theme"; import { DEFAULT_THEME } from "./theme"
const ThemeContext = createServerContext<Theme | null>(null); const ThemeContext = createServerContext<Theme | null>(null)
export function useServerTheme(): Theme { export function useServerTheme(): Theme {
return useContext(ThemeContext); return useContext(ThemeContext)
} }
const ThemeServerContextProvider: FunctionComponent<PropsWithChildren<{}>> = ({ const ThemeServerContextProvider: FunctionComponent<PropsWithChildren<{}>> = ({
children, children
}) => { }) => {
const cookiesList = cookies(); const cookiesList = cookies()
const theme = cookiesList.get(THEME_COOKIE_NAME)?.value ?? DEFAULT_THEME; const theme = cookiesList.get(THEME_COOKIE_NAME)?.value ?? DEFAULT_THEME
return ( return <ThemeContext.Provider value={theme}>{children}</ThemeContext.Provider>
<ThemeContext.Provider value={theme}>{children}</ThemeContext.Provider> }
);
};
export default ThemeServerContextProvider; export default ThemeServerContextProvider

View file

@ -1,5 +1,5 @@
export type Theme = "light" | "dark"; export type Theme = "light" | "dark"
export const DEFAULT_THEME: Theme = "light"; export const DEFAULT_THEME: Theme = "light"
export const THEME_COOKIE_NAME = "drift-theme"; export const THEME_COOKIE_NAME = "drift-theme"

View file

@ -26,7 +26,7 @@ export default async function RootLayout({ children }: RootLayoutProps) {
.split('=')[1]; .split('=')[1];
document.documentElement.setAttribute('data-theme', theme); document.documentElement.setAttribute('data-theme', theme);
})(); })();
`, `
}} }}
/> />
</head> </head>

View file

@ -15,5 +15,11 @@ export default async function Mine() {
const hasMore = false const hasMore = false
const stringifiedPosts = JSON.stringify(posts) const stringifiedPosts = JSON.stringify(posts)
return <PostList userId={userId} morePosts={hasMore} initialPosts={stringifiedPosts} /> return (
<PostList
userId={userId}
morePosts={hasMore}
initialPosts={stringifiedPosts}
/>
)
} }

View file

@ -43,7 +43,7 @@ const Password = () => {
const res = await fetch("/server-api/auth/change-password", { const res = await fetch("/server-api/auth/change-password", {
method: "PUT", method: "PUT",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json"
}, },
body: JSON.stringify({ body: JSON.stringify({
oldPassword: password, oldPassword: password,

View file

@ -2,7 +2,7 @@
import { Note, Input, Textarea, Button, useToasts } from "@geist-ui/core/dist" import { Note, Input, Textarea, Button, useToasts } from "@geist-ui/core/dist"
import { User } from "next-auth" import { User } from "next-auth"
import { useState } from "react" import { useState } from "react"
const Profile = ({ user }: { user: User }) => { const Profile = ({ user }: { user: User }) => {
// TODO: make this displayName, requires fetching user from DB as session doesnt have it // TODO: make this displayName, requires fetching user from DB as session doesnt have it
@ -37,7 +37,7 @@ const Profile = ({ user }: { user: User }) => {
const res = await fetch(`/api/user/${user.id}`, { const res = await fetch(`/api/user/${user.id}`, {
method: "PUT", method: "PUT",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json"
}, },
body: JSON.stringify(data) body: JSON.stringify(data)
}) })

View file

@ -92,7 +92,6 @@ html,
body { body {
padding: 0; padding: 0;
margin: 0; margin: 0;
font-size: 15px;
text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
@ -173,13 +172,6 @@ hr {
isolation: isolate; isolation: isolate;
} }
/* TODO: these should not be necessary. */
main {
margin-top: 0 !important;
padding-top: 0 !important;
}
textarea { textarea {
resize: vertical; resize: vertical;
border: 1px solid var(--border); border: 1px solid var(--border);
@ -193,3 +185,4 @@ textarea:focus {
border-color: var(--fg); border-color: var(--fg);
} }

View file

@ -35,10 +35,7 @@ export const config = (env: Environment): Config => {
return value return value
} }
const defaultIfUndefined = ( const defaultIfUndefined = (str: string, defaultValue: string): string => {
str: string,
defaultValue: string
): string => {
const value = env[str] const value = env[str]
if (value === undefined) { if (value === undefined) {
return defaultValue return defaultValue
@ -59,10 +56,7 @@ export const config = (env: Environment): Config => {
const is_production = env.NODE_ENV === "production" const is_production = env.NODE_ENV === "production"
const developmentDefault = ( const developmentDefault = (name: string, defaultValue: string): string => {
name: string,
defaultValue: string
): string => {
if (is_production) return throwIfUndefined(name) if (is_production) return throwIfUndefined(name)
return defaultIfUndefined(name, defaultValue) return defaultIfUndefined(name, defaultValue)
} }
@ -77,7 +71,7 @@ export const config = (env: Environment): Config => {
welcome_title: env.WELCOME_TITLE ?? "", welcome_title: env.WELCOME_TITLE ?? "",
url: process.env.VERCEL_URL ?? throwIfUndefined("DRIFT_URL"), url: process.env.VERCEL_URL ?? throwIfUndefined("DRIFT_URL"),
github_client_id: env.GITHUB_CLIENT_ID ?? "", github_client_id: env.GITHUB_CLIENT_ID ?? "",
github_client_secret: env.GITHUB_CLIENT_SECRET ?? "", github_client_secret: env.GITHUB_CLIENT_SECRET ?? ""
} }
return config return config
} }

View file

@ -112,8 +112,7 @@ export const codeFileExtensions = [
"xml", "xml",
"y", "y",
"yaml", "yaml",
"fish", "fish"
] ]
export const allowedFileExtensions = [ export const allowedFileExtensions = [
@ -125,4 +124,3 @@ export const allowedFileExtensions = [
"log", "log",
...codeFileExtensions ...codeFileExtensions
] ]

View file

@ -54,7 +54,7 @@ export const authOptions: NextAuthOptions = {
name: dbUser.displayName, name: dbUser.displayName,
email: dbUser.email, email: dbUser.email,
picture: dbUser.image, picture: dbUser.image,
role: dbUser.role || "user", role: dbUser.role || "user"
} }
} }
} }

View file

@ -1,14 +1,12 @@
import { unstable_getServerSession } from "next-auth/next" import { unstable_getServerSession } from "next-auth/next"
import { authOptions } from "./auth" import { authOptions } from "./auth"
export async function getSession() { export async function getSession() {
return await unstable_getServerSession(authOptions) return await unstable_getServerSession(authOptions)
} }
export async function getCurrentUser() { export async function getCurrentUser() {
const session = await getSession() const session = await getSession()
return session?.user return session?.user
} }

View file

@ -16,6 +16,7 @@
"@geist-ui/core": "^2.3.8", "@geist-ui/core": "^2.3.8",
"@geist-ui/icons": "1.0.2", "@geist-ui/icons": "1.0.2",
"@next-auth/prisma-adapter": "^1.0.5", "@next-auth/prisma-adapter": "^1.0.5",
"@next/eslint-plugin-next": "13.0.4-canary.5",
"@prisma/client": "^4.6.1", "@prisma/client": "^4.6.1",
"@radix-ui/react-popover": "^1.0.2", "@radix-ui/react-popover": "^1.0.2",
"@radix-ui/react-tabs": "^1.0.1", "@radix-ui/react-tabs": "^1.0.1",
@ -25,7 +26,7 @@
"client-zip": "2.2.1", "client-zip": "2.2.1",
"cookies-next": "^2.1.1", "cookies-next": "^2.1.1",
"jest": "^29.3.1", "jest": "^29.3.1",
"next": "13.0.3", "next": "13.0.4-canary.5",
"next-auth": "^4.16.4", "next-auth": "^4.16.4",
"rc-table": "7.24.1", "rc-table": "7.24.1",
"react": "18.2.0", "react": "18.2.0",

View file

@ -5,6 +5,7 @@ specifiers:
'@geist-ui/icons': 1.0.2 '@geist-ui/icons': 1.0.2
'@next-auth/prisma-adapter': ^1.0.5 '@next-auth/prisma-adapter': ^1.0.5
'@next/bundle-analyzer': 12.1.6 '@next/bundle-analyzer': 12.1.6
'@next/eslint-plugin-next': 13.0.4-canary.5
'@prisma/client': ^4.6.1 '@prisma/client': ^4.6.1
'@radix-ui/react-popover': ^1.0.2 '@radix-ui/react-popover': ^1.0.2
'@radix-ui/react-tabs': ^1.0.1 '@radix-ui/react-tabs': ^1.0.1
@ -24,7 +25,7 @@ specifiers:
eslint-config-next: 13.0.3 eslint-config-next: 13.0.3
jest: ^29.3.1 jest: ^29.3.1
katex: ^0.16.3 katex: ^0.16.3
next: 13.0.3 next: 13.0.4-canary.5
next-auth: ^4.16.4 next-auth: ^4.16.4
next-unused: 0.0.6 next-unused: 0.0.6
prettier: 2.6.2 prettier: 2.6.2
@ -47,6 +48,7 @@ dependencies:
'@geist-ui/core': 2.3.8_biqbaboplfbrettd7655fr4n2y '@geist-ui/core': 2.3.8_biqbaboplfbrettd7655fr4n2y
'@geist-ui/icons': 1.0.2_zhza2kbnl2wkkf7vqdl3ton2f4 '@geist-ui/icons': 1.0.2_zhza2kbnl2wkkf7vqdl3ton2f4
'@next-auth/prisma-adapter': 1.0.5_2pl3b2nwmjya7el2zbe6cwkney '@next-auth/prisma-adapter': 1.0.5_2pl3b2nwmjya7el2zbe6cwkney
'@next/eslint-plugin-next': 13.0.4-canary.5
'@prisma/client': 4.6.1_prisma@4.6.1 '@prisma/client': 4.6.1_prisma@4.6.1
'@radix-ui/react-popover': 1.0.2_jbvntnid6ohjelon6ccj5dhg2u '@radix-ui/react-popover': 1.0.2_jbvntnid6ohjelon6ccj5dhg2u
'@radix-ui/react-tabs': 1.0.1_biqbaboplfbrettd7655fr4n2y '@radix-ui/react-tabs': 1.0.1_biqbaboplfbrettd7655fr4n2y
@ -56,8 +58,8 @@ dependencies:
client-zip: 2.2.1 client-zip: 2.2.1
cookies-next: 2.1.1 cookies-next: 2.1.1
jest: 29.3.1_@types+node@17.0.23 jest: 29.3.1_@types+node@17.0.23
next: 13.0.3_biqbaboplfbrettd7655fr4n2y next: 13.0.4-canary.5_biqbaboplfbrettd7655fr4n2y
next-auth: 4.16.4_ogpkrxaz2lg6nectum6dl66tn4 next-auth: 4.16.4_a5ilcdcv5bxnpznazj6hzbna64
rc-table: 7.24.1_biqbaboplfbrettd7655fr4n2y rc-table: 7.24.1_biqbaboplfbrettd7655fr4n2y
react: 18.2.0 react: 18.2.0
react-datepicker: 4.8.0_biqbaboplfbrettd7655fr4n2y react-datepicker: 4.8.0_biqbaboplfbrettd7655fr4n2y
@ -815,7 +817,7 @@ packages:
next-auth: ^4 next-auth: ^4
dependencies: dependencies:
'@prisma/client': 4.6.1_prisma@4.6.1 '@prisma/client': 4.6.1_prisma@4.6.1
next-auth: 4.16.4_ogpkrxaz2lg6nectum6dl66tn4 next-auth: 4.16.4_a5ilcdcv5bxnpznazj6hzbna64
dev: false dev: false
/@next/bundle-analyzer/12.1.6: /@next/bundle-analyzer/12.1.6:
@ -827,8 +829,8 @@ packages:
- utf-8-validate - utf-8-validate
dev: true dev: true
/@next/env/13.0.3: /@next/env/13.0.4-canary.5:
resolution: {integrity: sha512-/4WzeG61Ot/PxsghXkSqQJ6UohFfwXoZ3dtsypmR9EBP+OIax9JRq0trq8Z/LCT9Aq4JbihVkaazRWguORjTAw==} resolution: {integrity: sha512-Am4Ig7WRw9g7V/2EByyDY/IiJnIl/b1N6tOEEwdf0VwhQQGvnwFkQY0u9Y2/EC6Ywdai2VfQeCFtWM+qGVRQjA==}
dev: false dev: false
/@next/eslint-plugin-next/13.0.3: /@next/eslint-plugin-next/13.0.3:
@ -837,8 +839,14 @@ packages:
glob: 7.1.7 glob: 7.1.7
dev: true dev: true
/@next/swc-android-arm-eabi/13.0.3: /@next/eslint-plugin-next/13.0.4-canary.5:
resolution: {integrity: sha512-uxfUoj65CdFc1gX2q7GtBX3DhKv9Kn343LMqGNvXyuTpYTGMmIiVY7b9yF8oLWRV0gVKqhZBZifUmoPE8SJU6Q==} resolution: {integrity: sha512-LrV32iDGepmTjLQ/Df4Yd41OmIU7g5sZEAaHEC5BcnSCiNoAS5U+JrOFprmqElGQWZwSOiBrx8knBpe9Vln9Sw==}
dependencies:
glob: 7.1.7
dev: false
/@next/swc-android-arm-eabi/13.0.4-canary.5:
resolution: {integrity: sha512-eanQ/XLZQLz/+Rd9IInEcZGwosAHvU4aZcvnsHv8EtsTHf2MSx5orpde+6BYHWGs48f3nxq9n2E4e0PPrF/rEA==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm] cpu: [arm]
os: [android] os: [android]
@ -846,8 +854,8 @@ packages:
dev: false dev: false
optional: true optional: true
/@next/swc-android-arm64/13.0.3: /@next/swc-android-arm64/13.0.4-canary.5:
resolution: {integrity: sha512-t2k+WDfg7Cq2z/EnalKGsd/9E5F4Hdo1xu+UzZXYDpKUI9zgE6Bz8ajQb8m8txv3qOaWdKuDa5j5ziq9Acd1Xw==} resolution: {integrity: sha512-3JbGGoO/d/eqRzWym4/Ss6Vsw8cuASFa+8JVCVJB54BhTluFwjQgmSg6iTbKOgkt8wa4SBuu6D1i+1MAE3tNBg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [android] os: [android]
@ -855,8 +863,8 @@ packages:
dev: false dev: false
optional: true optional: true
/@next/swc-darwin-arm64/13.0.3: /@next/swc-darwin-arm64/13.0.4-canary.5:
resolution: {integrity: sha512-wV6j6SZ1bc/YHOLCLk9JVqaZTCCey6HBV7inl2DriHsHqIcO6F3+QiYf0KXwRP9BE0GSZZrYd5mZQm2JPTHdJA==} resolution: {integrity: sha512-5ZS3/VoSjfz1VygOw6FOLKGAPXw7YbwwwloGUca8IghvYqR9IaPGAGUWJQ6EdB/Zb0JsxuYnnAJzHVSI/VBujQ==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [darwin] os: [darwin]
@ -864,8 +872,8 @@ packages:
dev: false dev: false
optional: true optional: true
/@next/swc-darwin-x64/13.0.3: /@next/swc-darwin-x64/13.0.4-canary.5:
resolution: {integrity: sha512-jaI2CMuYWvUtRixV3AIjUhnxUDU1FKOR+8hADMhYt3Yz+pCKuj4RZ0n0nY5qUf3qT1AtvnJXEgyatSFJhSp/wQ==} resolution: {integrity: sha512-pGG1GrFXvY1BQ6lm//zIVVRXqYffScGE1J9dDtyAtVeOpSHL5t9JsJEvdNZG3LS8DU5QmF8hV+qtN775FGNfHw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [darwin] os: [darwin]
@ -873,8 +881,8 @@ packages:
dev: false dev: false
optional: true optional: true
/@next/swc-freebsd-x64/13.0.3: /@next/swc-freebsd-x64/13.0.4-canary.5:
resolution: {integrity: sha512-nbyT0toBTJrcj5TCB9pVnQpGJ3utGyQj4CWegZs1ulaeUQ5Z7CS/qt8nRyYyOKYHtOdSCJ9Nw5F/RgKNkdpOdw==} resolution: {integrity: sha512-0LeLCWfZr0GNFpVreUeO5jD76QfCgidOMf6JiAfTjsdyq4FxCm2PJtOVd9eWj6p5A+0du/ba0LtbMkICLo2atw==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [freebsd] os: [freebsd]
@ -882,8 +890,8 @@ packages:
dev: false dev: false
optional: true optional: true
/@next/swc-linux-arm-gnueabihf/13.0.3: /@next/swc-linux-arm-gnueabihf/13.0.4-canary.5:
resolution: {integrity: sha512-1naLxYvRUQCoFCU1nMkcQueRc0Iux9xBv1L5pzH2ejtIWFg8BrSgyuluJG4nyAhFCx4WG863IEIkAaefOowVdA==} resolution: {integrity: sha512-asFt/PF/xukSOup73sYaEYuY5XUEleoeYZ7KYaSTUetQTj5f/nkg8Sb0AGlnmJyiL4rbGjOAxm+0+HF82AbaUg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
@ -891,8 +899,8 @@ packages:
dev: false dev: false
optional: true optional: true
/@next/swc-linux-arm64-gnu/13.0.3: /@next/swc-linux-arm64-gnu/13.0.4-canary.5:
resolution: {integrity: sha512-3Z4A8JkuGWpMVbUhUPQInK/SLY+kijTT78Q/NZCrhLlyvwrVxaQALJNlXzxDLraUgv4oVH0Wz/FIw1W9PUUhxA==} resolution: {integrity: sha512-UbcZ5jjtnI4aEQ8VyWUtxCH2FyxJlgYCbiK+1+IkApJZ5HxD+SROP42hGNDccrljTKDogNtDauXeJUF8r3tt7Q==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
@ -900,8 +908,8 @@ packages:
dev: false dev: false
optional: true optional: true
/@next/swc-linux-arm64-musl/13.0.3: /@next/swc-linux-arm64-musl/13.0.4-canary.5:
resolution: {integrity: sha512-MoYe9SM40UaunTjC+01c9OILLH3uSoeri58kDMu3KF/EFEvn1LZ6ODeDj+SLGlAc95wn46hrRJS2BPmDDE+jFQ==} resolution: {integrity: sha512-vCrHkeDOo+gHQab/A5IL0EaXd0WLcCxN8AVyBDbB5KI5kljFNXO0Oc4Mj/rT/Ocxc+X4BLq5/xjmsCWCh4XmTg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
@ -909,8 +917,8 @@ packages:
dev: false dev: false
optional: true optional: true
/@next/swc-linux-x64-gnu/13.0.3: /@next/swc-linux-x64-gnu/13.0.4-canary.5:
resolution: {integrity: sha512-z22T5WGnRanjLMXdF0NaNjSpBlEzzY43t5Ysp3nW1oI6gOkub6WdQNZeHIY7A2JwkgSWZmtjLtf+Fzzz38LHeQ==} resolution: {integrity: sha512-1gow2qIvPPqsP4zdKVL8abADkZHZKjyql2mudnxj3Xa0Rt5pYlwFJok4O6k8sVMgUMr9QqZjOevBIDo2t2S7uQ==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
@ -918,8 +926,8 @@ packages:
dev: false dev: false
optional: true optional: true
/@next/swc-linux-x64-musl/13.0.3: /@next/swc-linux-x64-musl/13.0.4-canary.5:
resolution: {integrity: sha512-ZOMT7zjBFmkusAtr47k8xs/oTLsNlTH6xvYb+iux7yly2hZGwhfBLzPGBsbeMZukZ96IphJTagT+C033s6LNVA==} resolution: {integrity: sha512-vTazzl/gAdOphrEpDEzx2Ew5lNc9/sGQNMGcUm5t/PiZAZeM5pc7YzgQ+PNq8AVv2DXPKOR/3prL+X0D9K1ijQ==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
@ -927,8 +935,8 @@ packages:
dev: false dev: false
optional: true optional: true
/@next/swc-win32-arm64-msvc/13.0.3: /@next/swc-win32-arm64-msvc/13.0.4-canary.5:
resolution: {integrity: sha512-Q4BM16Djl+Oah9UdGrvjFYgoftYB2jNd+rtRGPX5Mmxo09Ry/KiLbOZnoUyoIxKc1xPyfqMXuaVsAFQLYs0KEQ==} resolution: {integrity: sha512-Cy/O6b4F3X+ng/ldsoL2hK59mwr/A2EfC2A5ThRaVCLseE2jaPVFm0uyYydAE58pIc1In8kadDsC3pJnoNUI5w==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [arm64] cpu: [arm64]
os: [win32] os: [win32]
@ -936,8 +944,8 @@ packages:
dev: false dev: false
optional: true optional: true
/@next/swc-win32-ia32-msvc/13.0.3: /@next/swc-win32-ia32-msvc/13.0.4-canary.5:
resolution: {integrity: sha512-Sa8yGkNeRUsic8Qjf7MLIAfP0p0+einK/wIqJ8UO1y76j+8rRQu42AMs5H4Ax1fm9GEYq6I8njHtY59TVpTtGQ==} resolution: {integrity: sha512-swKzWu/xe79nLfJjLq8CSAc62QLzufjskyOZ6K0SwlsiTyvfVw5083Xq+AFvJ7j0QkNS8Fa4wH+Jf9wc092Rcg==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [ia32] cpu: [ia32]
os: [win32] os: [win32]
@ -945,8 +953,8 @@ packages:
dev: false dev: false
optional: true optional: true
/@next/swc-win32-x64-msvc/13.0.3: /@next/swc-win32-x64-msvc/13.0.4-canary.5:
resolution: {integrity: sha512-IAptmSqA7k4tQzaw2NAkoEjj3+Dz9ceuvlEHwYh770MMDL4V0ku2m+UHrmn5HUCEDHhgwwjg2nyf6728q2jr1w==} resolution: {integrity: sha512-t/JJ45e+ASrDC1BlTluSfWbgbCTcyve3WHQ/5OWKyzYEkdHz4oxIO+KXlvHNZaUT9fhD/MF/K8vyKpB625c7IA==}
engines: {node: '>= 10'} engines: {node: '>= 10'}
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
@ -3328,7 +3336,6 @@ packages:
minimatch: 3.1.2 minimatch: 3.1.2
once: 1.4.0 once: 1.4.0
path-is-absolute: 1.0.1 path-is-absolute: 1.0.1
dev: true
/glob/7.2.3: /glob/7.2.3:
resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
@ -5154,7 +5161,7 @@ packages:
dev: true dev: true
optional: true optional: true
/next-auth/4.16.4_ogpkrxaz2lg6nectum6dl66tn4: /next-auth/4.16.4_a5ilcdcv5bxnpznazj6hzbna64:
resolution: {integrity: sha512-KXW578+ER1u5RcWLwCHMdb/RIBIO6JM8r6xlf9RIPSKzkvDcX9FHiZfJS2vOq/SurHXPJZc4J3OS4IDJpF74Dw==} resolution: {integrity: sha512-KXW578+ER1u5RcWLwCHMdb/RIBIO6JM8r6xlf9RIPSKzkvDcX9FHiZfJS2vOq/SurHXPJZc4J3OS4IDJpF74Dw==}
engines: {node: ^12.19.0 || ^14.15.0 || ^16.13.0 || ^18.12.0} engines: {node: ^12.19.0 || ^14.15.0 || ^16.13.0 || ^18.12.0}
peerDependencies: peerDependencies:
@ -5170,7 +5177,7 @@ packages:
'@panva/hkdf': 1.0.2 '@panva/hkdf': 1.0.2
cookie: 0.5.0 cookie: 0.5.0
jose: 4.11.0 jose: 4.11.0
next: 13.0.3_biqbaboplfbrettd7655fr4n2y next: 13.0.4-canary.5_biqbaboplfbrettd7655fr4n2y
oauth: 0.9.15 oauth: 0.9.15
openid-client: 5.3.0 openid-client: 5.3.0
preact: 10.11.2 preact: 10.11.2
@ -5191,8 +5198,8 @@ packages:
- supports-color - supports-color
dev: true dev: true
/next/13.0.3_biqbaboplfbrettd7655fr4n2y: /next/13.0.4-canary.5_biqbaboplfbrettd7655fr4n2y:
resolution: {integrity: sha512-rFQeepcenRxKzeKlh1CsmEnxsJwhIERtbUjmYnKZyDInZsU06lvaGw5DT44rlNp1Rv2MT/e9vffZ8vK+ytwXHA==} resolution: {integrity: sha512-VIvC0Xaurai1JT1MSN+p6rHBbZD0XlKvP0R6AU+jHy/p5hHYdKh5SOkcfNedGEuvkaiYFXhMe5+lwb+CRME2Dw==}
engines: {node: '>=14.6.0'} engines: {node: '>=14.6.0'}
hasBin: true hasBin: true
peerDependencies: peerDependencies:
@ -5209,7 +5216,7 @@ packages:
sass: sass:
optional: true optional: true
dependencies: dependencies:
'@next/env': 13.0.3 '@next/env': 13.0.4-canary.5
'@swc/helpers': 0.4.11 '@swc/helpers': 0.4.11
caniuse-lite: 1.0.30001431 caniuse-lite: 1.0.30001431
postcss: 8.4.14 postcss: 8.4.14
@ -5218,19 +5225,19 @@ packages:
styled-jsx: 5.1.0_react@18.2.0 styled-jsx: 5.1.0_react@18.2.0
use-sync-external-store: 1.2.0_react@18.2.0 use-sync-external-store: 1.2.0_react@18.2.0
optionalDependencies: optionalDependencies:
'@next/swc-android-arm-eabi': 13.0.3 '@next/swc-android-arm-eabi': 13.0.4-canary.5
'@next/swc-android-arm64': 13.0.3 '@next/swc-android-arm64': 13.0.4-canary.5
'@next/swc-darwin-arm64': 13.0.3 '@next/swc-darwin-arm64': 13.0.4-canary.5
'@next/swc-darwin-x64': 13.0.3 '@next/swc-darwin-x64': 13.0.4-canary.5
'@next/swc-freebsd-x64': 13.0.3 '@next/swc-freebsd-x64': 13.0.4-canary.5
'@next/swc-linux-arm-gnueabihf': 13.0.3 '@next/swc-linux-arm-gnueabihf': 13.0.4-canary.5
'@next/swc-linux-arm64-gnu': 13.0.3 '@next/swc-linux-arm64-gnu': 13.0.4-canary.5
'@next/swc-linux-arm64-musl': 13.0.3 '@next/swc-linux-arm64-musl': 13.0.4-canary.5
'@next/swc-linux-x64-gnu': 13.0.3 '@next/swc-linux-x64-gnu': 13.0.4-canary.5
'@next/swc-linux-x64-musl': 13.0.3 '@next/swc-linux-x64-musl': 13.0.4-canary.5
'@next/swc-win32-arm64-msvc': 13.0.3 '@next/swc-win32-arm64-msvc': 13.0.4-canary.5
'@next/swc-win32-ia32-msvc': 13.0.3 '@next/swc-win32-ia32-msvc': 13.0.4-canary.5
'@next/swc-win32-x64-msvc': 13.0.3 '@next/swc-win32-x64-msvc': 13.0.4-canary.5
transitivePeerDependencies: transitivePeerDependencies:
- '@babel/core' - '@babel/core'
- babel-plugin-macros - babel-plugin-macros