remove more of geist-ui: add spinner, button dropdown, toasts. bump deps

This commit is contained in:
Max Leiter 2022-11-28 18:33:06 -08:00
parent 0cab3acd62
commit d6894ffb8b
35 changed files with 397 additions and 459 deletions

View file

@ -60,7 +60,7 @@
}
.chevron {
transition: transform 0.2s ease-in-out;
transition: transform 0.1s ease-in-out;
}
[data-state="open"] .chevron {

View file

@ -2,7 +2,8 @@ import { memo, useEffect, useState } from "react"
import styles from "./preview.module.css"
import "@styles/markdown.css"
import "@styles/syntax.css"
import { Spinner } from "@geist-ui/core/dist"
import Skeleton from "@components/skeleton"
import { Spinner } from "@components/spinner"
type Props = {
height?: number | string
@ -52,9 +53,7 @@ const MarkdownPreview = ({
return (
<>
{isLoading ? (
<>
<Spinner />
</>
<Spinner />
) : (
<StaticPreview preview={preview} height={height} />
)}

View file

@ -1,3 +1,5 @@
"use client"
import * as RadixTabs from "@radix-ui/react-tabs"
import FormattingIcons from "app/(posts)/new/components/edit-document-list/edit-document/formatting-icons"
import { ChangeEvent, useRef } from "react"

View file

@ -17,7 +17,7 @@
border-radius: 2px;
border: 2px dashed var(--border) !important;
outline: none;
transition: all 0.24s ease-in-out;
transition: all 0.14s ease-in-out;
cursor: pointer;
}
@ -32,7 +32,7 @@
.error {
color: red;
font-size: 0.8rem;
transition: border 0.24s ease-in-out;
transition: border 0.14s ease-in-out;
border: 2px solid red;
border-radius: 2px;
padding: 20px;
@ -42,3 +42,13 @@
margin: 0;
padding-left: var(--gap-double);
}
.verb:after {
content: "click";
}
@media (hover: none) {
.verb:after {
content: "tap";
}
}

View file

@ -1,4 +1,3 @@
import { useMediaQuery, useTheme, useToasts } from "@geist-ui/core/dist"
import { useDropzone } from "react-dropzone"
import styles from "./drag-and-drop.module.css"
import generateUUID from "@lib/generate-uuid"
@ -9,13 +8,10 @@ import {
} from "@lib/constants"
import byteToMB from "@lib/byte-to-mb"
import type { Document } from "../new"
import { useToasts } from "@components/toasts"
function FileDropzone({ setDocs }: { setDocs: (docs: Document[]) => void }) {
const { palette } = useTheme()
const { setToast } = useToasts()
const isMobile = useMediaQuery("xs", {
match: "down"
})
const onDrop = async (acceptedFiles: File[]) => {
const newDocs = await Promise.all(
acceptedFiles.map((file) => {
@ -23,9 +19,9 @@ function FileDropzone({ setDocs }: { setDocs: (docs: Document[]) => void }) {
const reader = new FileReader()
reader.onabort = () =>
setToast({ text: "File reading was aborted", type: "error" })
setToast({ message: "File reading was aborted", type: "error" })
reader.onerror = () =>
setToast({ text: "File reading failed", type: "error" })
setToast({ message: "File reading failed", type: "error" })
reader.onload = () => {
const content = reader.result as string
resolve({
@ -84,20 +80,13 @@ function FileDropzone({ setDocs }: { setDocs: (docs: Document[]) => void }) {
</li>
))
const verb = isMobile ? "tap" : "click"
return (
<div className={styles.container}>
<div
{...getRootProps()}
className={styles.dropzone}
style={{
borderColor: palette.accents_3
}}
>
<div {...getRootProps()} className={styles.dropzone}>
<input {...getInputProps()} />
{!isDragActive && (
<p style={{ color: "var(--gray)" }}>
Drag some files here, or {verb} to select files
Drag some files here, or <span className={styles.verb} /> to select files
</p>
)}
{isDragActive && <p>Release to drop the files here</p>}

View file

@ -1,7 +1,7 @@
.card {
margin: var(--gap) auto;
padding: var(--gap);
border: 1px solid var(--light-gray);
border: 1px solid var(--lighter-gray);
border-radius: var(--radius);
}

View file

@ -6,7 +6,7 @@
.actionWrapper .actions {
position: absolute;
right: 0;
top: -34px;
top: -40px;
}
/* small screens, top: 0 */

View file

@ -1,6 +1,5 @@
"use client"
import { useToasts, ButtonDropdown } from "@geist-ui/core/dist"
import { useRouter } from "next/navigation"
import { useCallback, useState } from "react"
import generateUUID from "@lib/generate-uuid"
@ -16,6 +15,8 @@ import Title from "./title"
import FileDropzone from "./drag-and-drop"
import Button from "@components/button"
import Input from "@components/input"
import ButtonDropdown from "@components/button-dropdown"
import { useToasts } from "@components/toasts"
const emptyDoc = {
title: "",
content: "",
@ -87,7 +88,8 @@ const Post = ({
const json = await res.json()
console.error(json)
setToast({
text: "Please fill out all fields",
id: "error",
message: "Please fill out all fields",
type: "error"
})
setPasswordModalVisible(false)
@ -114,7 +116,7 @@ const Post = ({
if (!title) {
setToast({
text: "Please fill out the post title",
message: "Please fill out the post title",
type: "error"
})
hasErrored = true
@ -122,7 +124,7 @@ const Post = ({
if (!docs.length) {
setToast({
text: "Please add at least one document",
message: "Please add at least one document",
type: "error"
})
hasErrored = true
@ -131,7 +133,7 @@ const Post = ({
for (const doc of docs) {
if (!doc.title) {
setToast({
text: "Please fill out all the document titles",
message: "Please fill out all the document titles",
type: "error"
})
hasErrored = true
@ -308,19 +310,27 @@ const Post = ({
enableTabLoop={false}
minDate={new Date()}
/>
<ButtonDropdown loading={isSubmitting} type="success">
<ButtonDropdown.Item main onClick={() => onSubmit("unlisted")}>
<ButtonDropdown iconHeight={40}>
<Button
height={40}
width={251}
onClick={() => onSubmit("unlisted")}
>
Create Unlisted
</ButtonDropdown.Item>
<ButtonDropdown.Item onClick={() => onSubmit("private")}>
</Button>
<Button height={40} width={300} onClick={() => onSubmit("private")}>
Create Private
</ButtonDropdown.Item>
<ButtonDropdown.Item onClick={() => onSubmit("public")}>
</Button>
<Button height={40} width={300} onClick={() => onSubmit("public")}>
Create Public
</ButtonDropdown.Item>
<ButtonDropdown.Item onClick={() => onSubmit("protected")}>
</Button>
<Button
height={40}
width={300}
onClick={() => onSubmit("protected")}
>
Create with Password
</ButtonDropdown.Item>
</Button>
</ButtonDropdown>
</div>
</div>

View file

@ -13,14 +13,14 @@ const titlePlaceholders = [
"I'm thinking about ..."
]
const placeholder = titlePlaceholders[3]
type props = {
onChange: (e: ChangeEvent<HTMLInputElement>) => void
title?: string
}
const Title = ({ onChange, title }: props) => {
const placeholder =
titlePlaceholders[Math.floor(Math.random() * titlePlaceholders.length)]
return (
<div className={styles.title}>
<h1>Drift</h1>

View file

@ -4,7 +4,6 @@ import VisibilityBadge from "@components/badges/visibility-badge"
import DocumentComponent from "./view-document"
import styles from "./post-page.module.css"
import { useMediaQuery } from "@geist-ui/core/dist"
import { useEffect, useState } from "react"
import Archive from "@geist-ui/icons/archive"
import Edit from "@geist-ui/icons/edit"
@ -32,7 +31,6 @@ const PostPage = ({ post: initialPost, isProtected, isAuthor }: Props) => {
)
const [visibility, setVisibility] = useState<string>(post.visibility)
const router = useRouter()
const isMobile = useMediaQuery("mobile")
useEffect(() => {
if (post.expiresAt) {
@ -101,7 +99,7 @@ const PostPage = ({ post: initialPost, isProtected, isAuthor }: Props) => {
{!isAvailable && <PasswordModalPage setPost={setPost} postId={post.id} />}
<div className={styles.header}>
<span className={styles.buttons}>
<ButtonGroup vertical={isMobile}>
<ButtonGroup verticalIfMobile>
<Button
iconLeft={<Edit />}
onClick={editACopy}

View file

@ -1,8 +1,8 @@
import { useToasts } from "@geist-ui/core/dist"
import { Post, PostWithFilesAndAuthor } from "@lib/server/prisma"
import PasswordModal from "@components/password-modal"
import { useRouter } from "next/navigation"
import { useState } from "react"
import { useToasts } from "@components/toasts"
type Props = {
setPost: (post: PostWithFilesAndAuthor) => void
@ -25,7 +25,7 @@ const PasswordModalPage = ({ setPost, postId }: Props) => {
if (!res.ok) {
setToast({
type: "error",
text: "Wrong password"
message: "Wrong password"
})
return
}
@ -34,7 +34,7 @@ const PasswordModalPage = ({ setPost, postId }: Props) => {
if (data) {
if (data.error) {
setToast({
text: data.error,
message: data.error,
type: "error"
})
} else {

View file

@ -35,7 +35,7 @@
}
}
@media screen and (max-width: 700px) {
@media screen and (max-width: 768px) {
.header .title {
flex-direction: column;
gap: var(--gap-half);
@ -50,4 +50,8 @@
display: flex;
justify-content: center;
}
.controls {
justify-content: center;
}
}

View file

@ -1,6 +1,6 @@
"use client"
import SettingsGroup from "@components/settings-group"
import { Fieldset, useToasts } from "@geist-ui/core/dist"
import { Fieldset } from "@geist-ui/core/dist"
import byteToMB from "@lib/byte-to-mb"
import { PostWithFiles } from "@lib/server/prisma"
import Table from "rc-table"

View file

@ -1,10 +1,11 @@
"use client"
import { Fieldset, useToasts } from "@geist-ui/core/dist"
import { Fieldset } from "@geist-ui/core/dist"
import Table from "rc-table"
import ActionDropdown from "./action-dropdown"
import SettingsGroup from "@components/settings-group"
import type { User, UserWithPosts } from "@lib/server/prisma"
import { useState } from "react"
import { useToasts } from "@components/toasts"
const UserTable = ({ users: initial }: { users: UserWithPosts[] }) => {
const [users, setUsers] = useState(initial)
@ -21,7 +22,7 @@ const UserTable = ({ users: initial }: { users: UserWithPosts[] }) => {
if (res.status === 200) {
setToast({
text: "Role updated",
message: "Role updated",
type: "success"
})
@ -39,7 +40,7 @@ const UserTable = ({ users: initial }: { users: UserWithPosts[] }) => {
})
} else {
setToast({
text: "Something went wrong",
message: "Something went wrong",
type: "error"
})
}

View file

@ -1,8 +1,9 @@
import { Loading, useToasts } from "@geist-ui/core/dist"
import PasswordModal from "@components/password-modal"
import { useCallback, useState } from "react"
import ButtonGroup from "@components/button-group"
import Button from "@components/button"
import { useToasts } from "@components/toasts"
import { Spinner } from "@components/spinner"
type Props = {
postId: string
@ -30,7 +31,7 @@ const VisibilityControl = ({ postId, visibility, setVisibility }: Props) => {
setVisibility(json.visibility)
} else {
setToast({
text: "An error occurred",
message: "An error occurred",
type: "error"
})
setPasswordModalVisible(false)
@ -65,9 +66,9 @@ const VisibilityControl = ({ postId, visibility, setVisibility }: Props) => {
return (
<>
{isSubmitting ? (
<Loading />
<Spinner />
) : (
<ButtonGroup>
<ButtonGroup verticalIfMobile>
<Button
disabled={visibility === "private"}
onClick={() => onSubmit("private")}

View file

@ -1,26 +1,27 @@
.main {
margin-bottom: 2rem;
}
.dropdown {
position: relative;
display: inline-block;
vertical-align: middle;
cursor: pointer;
padding: 0;
border: 0;
background: transparent;
display: flex;
align-items: stretch;
}
.dropdownContent {
background-clip: padding-box;
border: 1px solid rgba(0, 0, 0, 0.15);
border-radius: 0.25rem;
box-shadow: 0 3px 12px rgba(0, 0, 0, 0.15);
.dropdown > button:first-child {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.dropdown > button:last-child {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
/* if we draw bother borders it will be 2 thick */
border-left: 0;
}
.dropdown > button:last-child:hover {
/* because we removed the left border we need to mock it */
outline: 1px solid var(--darker-gray);
}
.icon {
display: flex;
align-items: center;
justify-content: center;
display: flex;
align-items: center;
justify-content: center;
}

View file

@ -3,6 +3,7 @@ import React, { useCallback, useEffect } from "react"
import { useState } from "react"
import styles from "./dropdown.module.css"
import DownIcon from "@geist-ui/icons/arrowDown"
import * as DropdownMenu from "@radix-ui/react-dropdown-menu"
type Props = {
type?: "primary" | "secondary"
loading?: boolean
@ -17,99 +18,37 @@ type ButtonDropdownProps = Props & Attrs
const ButtonDropdown: React.FC<
React.PropsWithChildren<ButtonDropdownProps>
> = ({ type, className, disabled, loading, iconHeight = 24, ...props }) => {
const [visible, setVisible] = useState(false)
const [dropdown, setDropdown] = useState<HTMLDivElement | null>(null)
const onClick = (e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation()
e.nativeEvent.stopImmediatePropagation()
setVisible(!visible)
}
const onBlur = () => {
setVisible(false)
}
const onMouseDown = (e: React.MouseEvent<HTMLDivElement>) => {
e.stopPropagation()
e.nativeEvent.stopImmediatePropagation()
}
const onMouseUp = (e: React.MouseEvent<HTMLDivElement>) => {
e.stopPropagation()
e.nativeEvent.stopImmediatePropagation()
}
const onMouseLeave = (e: React.MouseEvent<HTMLDivElement>) => {
e.stopPropagation()
e.nativeEvent.stopImmediatePropagation()
setVisible(false)
}
const onKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
if (e.key === "Escape") {
setVisible(false)
}
}
const onClickOutside = useCallback(
() => (e: React.MouseEvent<HTMLDivElement>) => {
if (dropdown && !dropdown.contains(e.target as Node)) {
setVisible(false)
}
},
[dropdown]
)
useEffect(() => {
if (visible) {
document.addEventListener("mousedown", onClickOutside)
} else {
document.removeEventListener("mousedown", onClickOutside)
}
return () => {
document.removeEventListener("mousedown", onClickOutside)
}
}, [visible, onClickOutside])
if (!Array.isArray(props.children)) {
return null
}
return (
<div
className={`${styles.main} ${className || ""}`}
onMouseDown={onMouseDown}
onMouseUp={onMouseUp}
onMouseLeave={onMouseLeave}
onKeyDown={onKeyDown}
onBlur={onBlur}
>
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "flex-end"
}}
>
<DropdownMenu.Root>
<div className={styles.dropdown}>
{props.children[0]}
<Button
style={{ height: iconHeight, width: iconHeight }}
className={styles.icon}
onClick={() => setVisible(!visible)}
<DropdownMenu.Trigger
style={{
display: "flex",
flexDirection: "row",
justifyContent: "flex-end"
}}
asChild
>
<DownIcon />
</Button>
<Button
iconLeft={<DownIcon />}
type={type}
className={styles.icon}
/>
</DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content align="end">
{props.children.slice(1).map((child, index) => (
<DropdownMenu.Item key={index}>{child}</DropdownMenu.Item>
))}
</DropdownMenu.Content>
</DropdownMenu.Portal>
</div>
{visible && (
<div className={`${styles.dropdown}`}>
<div className={`${styles.dropdownContent}`}>
{props.children.slice(1)}
</div>
</div>
)}
</div>
</DropdownMenu.Root>
)
}

View file

@ -1,29 +1,45 @@
.button-group {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: flex-start;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: flex-start;
}
.button-group > * {
flex: 1 1 auto;
margin: 0;
flex: 1 1 auto;
margin: 0;
}
.button-group > * {
border-radius: 0 !important;
border-radius: 0 !important;
}
.button-group > button:first-of-type {
border-top-left-radius: var(--radius) !important;
border-bottom-left-radius: var(--radius) !important;
border-top-left-radius: var(--radius) !important;
border-bottom-left-radius: var(--radius) !important;
}
.button-group > button:last-of-type {
border-top-right-radius: var(--radius) !important;
border-bottom-right-radius: var(--radius) !important;
border-top-right-radius: var(--radius) !important;
border-bottom-right-radius: var(--radius) !important;
}
.vertical {
flex-direction: column;
@media screen and (max-width: 768px) {
.verticalIfMobile {
flex-direction: column;
}
.verticalIfMobile.button-group > button:first-of-type {
border-top-left-radius: var(--radius) !important;
border-top-right-radius: var(--radius) !important;
border-bottom-left-radius: 0 !important;
border-bottom-right-radius: 0 !important;
}
.verticalIfMobile.button-group > button:last-of-type {
border-top-left-radius: 0 !important;
border-top-right-radius: 0 !important;
border-bottom-left-radius: var(--radius) !important;
border-bottom-right-radius: var(--radius) !important;
}
}

View file

@ -2,18 +2,18 @@ import styles from "./button-group.module.css"
import clsx from "clsx"
export default function ButtonGroup({
children,
vertical,
verticalIfMobile,
...props
}: {
children: React.ReactNode | React.ReactNode[]
vertical?: boolean
verticalIfMobile?: boolean
} & React.HTMLAttributes<HTMLDivElement>) {
return (
<div
className={clsx(
props.className,
styles["button-group"],
vertical && styles.vertical
verticalIfMobile && styles.verticalIfMobile
)}
{...props}
>

View file

@ -1,36 +0,0 @@
import React, { useEffect, useState } from "react"
import MoonIcon from "@geist-ui/icons/moon"
import SunIcon from "@geist-ui/icons/sun"
import styles from "./header.module.css"
import { Select } from "@geist-ui/core/dist"
import { useTheme } from "@components/theme/ThemeClientContextProvider"
const Controls = () => {
const { theme, setTheme } = useTheme()
const switchThemes = () => {
if (theme === "dark") {
setTheme("light")
} else {
setTheme("dark")
}
}
return (
<div className={styles.wrapper}>
<Select scale={0.5} h="28px" pure onChange={switchThemes} value={theme}>
<Select.Option value="light">
<span className={styles.selectContent}>
<SunIcon size={14} /> Light
</span>
</Select.Option>
<Select.Option value="dark">
<span className={styles.selectContent}>
<MoonIcon size={14} /> Dark
</span>
</Select.Option>
</Select>
</div>
)
}
export default React.memo(Controls)

View file

@ -1,6 +1,6 @@
"use client"
import { Page, useBodyScroll, useMediaQuery } from "@geist-ui/core/dist"
import { useBodyScroll, useMediaQuery } from "@geist-ui/core/dist"
import { useEffect, useState } from "react"
import styles from "./header.module.css"
@ -174,7 +174,7 @@ const Header = ({ signedIn = false, isAdmin = false }) => {
const buttons = pages.map(getButton)
return (
<Page.Header>
<header>
<div className={styles.tabs}>
<div className={styles.buttons}>{buttons}</div>
</div>
@ -189,7 +189,7 @@ const Header = ({ signedIn = false, isAdmin = false }) => {
{buttons}
</div>
)}
</Page.Header>
</header>
)
}

View file

@ -1,9 +1,4 @@
"use client"
import { Tabs, Textarea } from "@geist-ui/core/dist"
import Image from "next/image"
import styles from "./home.module.css"
// TODO:components/new-post/ move these styles
import markdownStyles from "app/(posts)/components/preview/preview.module.css"
import Card from "./card"
import DocumentTabs from "app/(posts)/components/tabs"
const Home = ({

View file

@ -12,7 +12,8 @@
}
.warning {
background: #f33;
background: var(--warning);
color: var(--bg);
}
.error {
@ -21,7 +22,7 @@
[data-theme="light"] .warning,
[data-theme="light"] .error {
color: var(--bg);
color: var(--fg);
}
.type {

View file

@ -1,7 +1,7 @@
"use client"
import styles from "./post-list.module.css"
import ListItemSkeleton from "./list-item-skeleton"
import { ListItemSkeleton } from "./list-item-skeleton"
import ListItem from "./list-item"
import { ChangeEvent, useCallback, useEffect, useState } from "react"
import useDebounce from "@lib/hooks/use-debounce"
@ -9,6 +9,7 @@ import Link from "@components/link"
import type { PostWithFiles } from "@lib/server/prisma"
import Input from "@components/input"
import Button from "@components/button"
import { useToasts } from "@components/toasts"
type Props = {
initialPosts: string | PostWithFiles[]
@ -29,6 +30,7 @@ const PostList = ({
const [posts, setPosts] = useState<PostWithFiles[]>(initialPosts)
const [searching, setSearching] = useState(false)
const [hasMorePosts, setHasMorePosts] = useState(morePosts)
const { setToast } = useToasts()
const debouncedSearchValue = useDebounce(search, 200)
@ -71,7 +73,7 @@ const PostList = ({
}
)
const json = await res.json()
setPosts(json.posts)
setPosts(json)
setSearching(false)
}
fetchPosts()
@ -97,9 +99,13 @@ const PostList = ({
return
} else {
setPosts((posts) => posts.filter((post) => post.id !== postId))
setToast({
message: "Post deleted",
type: "success"
})
}
},
[]
[setToast]
)
return (
@ -108,21 +114,18 @@ const PostList = ({
<Input
placeholder="Search..."
onChange={handleSearchChange}
disabled={Boolean(!posts?.length)}
disabled={!posts}
style={{ maxWidth: 300 }}
aria-label="Search"
/>
</div>
{!posts && <p style={{ color: "var(--warning)" }}>Failed to load.</p>}
{!posts?.length && searching && (
{/* {!posts?.length && (
<ul>
<li>
<ListItemSkeleton />
</li>
<li>
<ListItemSkeleton />
</li>
<ListItemSkeleton />
<ListItemSkeleton />
</ul>
)}
)} */}
{posts?.length === 0 && posts && (
<p>
No posts found. Create one{" "}

View file

@ -1,25 +1,22 @@
import styles from "./list-item.module.css"
import Card from "@components/card"
import Skeleton from "@components/skeleton"
import { Divider, Grid, Spacer } from "@geist-ui/core/dist"
const ListItemSkeleton = () => (
<Card>
<Spacer height={1 / 2} />
<Grid.Container justify={"space-between"} marginBottom={1 / 2}>
<Grid xs={8} paddingLeft={1 / 2}>
<Skeleton width={150} />
</Grid>
<Grid xs={7}>
<Skeleton width={100} />
</Grid>
<Grid xs={4}>
<Skeleton width={70} />
</Grid>
</Grid.Container>
export const ListItemSkeleton = () => (
<li>
<Card style={{ overflowY: "scroll" }}>
<>
<div className={styles.title}>
{/* title */}
<Skeleton width={80} height={32} />
</div>
<Divider h="1px" my={0} />
<Skeleton width={200} />
</Card>
<div className={styles.badges}>
<Skeleton width={30} height={32} />
</div>
</>
<hr />
<Skeleton width={100} height={32} />
</Card>
</li>
)
export default ListItemSkeleton

View file

@ -0,0 +1,3 @@
import styles from './spinner.module.css'
export const Spinner = () => <div className={styles.spinner} />

View file

@ -0,0 +1,17 @@
.spinner {
border: 4px solid var(--light-gray);
border-top: 4px solid var(--gray);
border-radius: 50%;
width: 24px;
height: 24px;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}

View file

@ -0,0 +1,69 @@
"use client"
import Toast, { Toaster } from "react-hot-toast"
export type ToastType = "success" | "error" | "loading" | "default"
export type ToastProps = {
id?: string
type: ToastType
message: string
duration?: number
icon?: string
style?: React.CSSProperties
className?: string
loading?: boolean
loadingProgress?: number
}
export const useToasts = () => {
const setToast = (toast: ToastProps) => {
const { type, message, ...rest } = toast
if (toast.id) {
Toast.dismiss(toast.id)
}
switch (type) {
case "success":
Toast.success(message, rest)
break
case "error":
Toast.error(message, rest)
break
case "loading":
Toast.loading(message, rest)
break
default:
Toast(message, rest)
break
}
}
return { setToast }
}
export const Toasts = () => {
return (
<Toaster
position="bottom-right"
toastOptions={{
error: {
style: {
background: "var(--warning)",
color: "#fff"
}
},
success: {
style: {
background: "var(--light-gray)",
color: "var(--fg)"
}
},
iconTheme: {
primary: "var(--fg)",
secondary: "var(--bg)"
}
}}
/>
)
}

View file

@ -4,7 +4,6 @@ import styles from "@styles/Home.module.css"
import { getSession } from "@lib/server/session"
import ThemeProvider from "@components/theme/ThemeProvider"
import { THEME_COOKIE_NAME } from "@components/theme/theme"
import { useServerTheme } from "@components/theme/ThemeServerContextProvider"
interface RootLayoutProps {
children: React.ReactNode

View file

@ -2,7 +2,9 @@
import Header from "@components/header"
import Page from "@components/page"
import { Toasts } from "@components/toasts"
import * as RadixTooltip from "@radix-ui/react-tooltip"
import { Toaster } from "react-hot-toast"
export function LayoutWrapper({
children,
@ -15,6 +17,7 @@ export function LayoutWrapper({
}) {
return (
<RadixTooltip.Provider delayDuration={200}>
<Toasts />
<Page>
<Header isAdmin={isAdmin} signedIn={signedIn} />
{children}

View file

@ -1,134 +0,0 @@
"use client"
import { Input, Button, useToasts } from "@geist-ui/core/dist"
import { useState } from "react"
const Password = () => {
const [password, setPassword] = useState<string>("")
const [newPassword, setNewPassword] = useState<string>("")
const [confirmPassword, setConfirmPassword] = useState<string>("")
const { setToast } = useToasts()
const handlePasswordChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setPassword(e.target.value)
}
const handleNewPasswordChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setNewPassword(e.target.value)
}
const handleConfirmPasswordChange = (
e: React.ChangeEvent<HTMLInputElement>
) => {
setConfirmPassword(e.target.value)
}
const onSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
if (!password || !newPassword || !confirmPassword) {
setToast({
text: "Please fill out all fields",
type: "error"
})
}
if (newPassword !== confirmPassword) {
setToast({
text: "New password and confirm password do not match",
type: "error"
})
}
const res = await fetch("/server-api/auth/change-password", {
method: "PUT",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
oldPassword: password,
newPassword
})
})
if (res.status === 200) {
setToast({
text: "Password updated successfully",
type: "success"
})
setPassword("")
setNewPassword("")
setConfirmPassword("")
} else {
const data = await res.json()
setToast({
text: data.error ?? "Failed to update password",
type: "error"
})
}
}
return (
<form
style={{
display: "flex",
flexDirection: "column",
gap: "var(--gap)",
maxWidth: "300px"
}}
onSubmit={onSubmit}
>
<div>
<label htmlFor="current-password">Current password</label>
<Input
onChange={handlePasswordChange}
minLength={6}
maxLength={128}
value={password}
id="current-password"
htmlType="password"
required
autoComplete="current-password"
placeholder="Current Password"
width={"100%"}
/>
</div>
<div>
<label htmlFor="new-password">New password</label>
<Input
onChange={handleNewPasswordChange}
minLength={6}
maxLength={128}
value={newPassword}
id="new-password"
htmlType="password"
required
autoComplete="new-password"
placeholder="New Password"
width={"100%"}
/>
</div>
<div>
<label htmlFor="confirm-password">Confirm password</label>
<Input
onChange={handleConfirmPasswordChange}
minLength={6}
maxLength={128}
value={confirmPassword}
id="confirm-password"
htmlType="password"
required
autoComplete="confirm-password"
placeholder="Confirm Password"
width={"100%"}
/>
</div>
<Button htmlType="submit" auto>
Change Password
</Button>
</form>
)
}
export default Password

View file

@ -93,7 +93,7 @@ const Profile = ({ user }: { user: User }) => {
disabled
/>
</div>
<div>
{/* <div>
<label htmlFor="bio">Biography (max 250 characters)</label>
<textarea
id="bio"
@ -103,8 +103,8 @@ const Profile = ({ user }: { user: User }) => {
value={bio}
onChange={handleBioChange}
/>
</div>
<Button htmlType="submit" auto>
</div> */}
<Button type="submit">
Submit
</Button>
</form>

View file

@ -45,7 +45,7 @@
--gray-alpha: rgba(255, 255, 255, 0.5);
--selection: rgba(255, 255, 255, 0.99);
--border: var(--lighter-gray);
--warning: rgb(27, 134, 23);
--warning: #ff6700;
--link: #3291ff;
color-scheme: dark;
}
@ -78,8 +78,9 @@
box-sizing: border-box;
}
// TODO: replace this with an accessible alternative
*:focus-visible {
outline: 1px solid var(--gray);
outline: none;
}
::selection {
@ -179,3 +180,8 @@ textarea {
padding: var(--gap-half);
border-radius: var(--radius);
}
textarea:focus {
border-color: var(--light-gray);
outline: none;
}

View file

@ -18,6 +18,7 @@
"@next-auth/prisma-adapter": "^1.0.5",
"@next/eslint-plugin-next": "13.0.5-canary.3",
"@prisma/client": "^4.6.1",
"@radix-ui/react-dropdown-menu": "^2.0.1",
"@radix-ui/react-popover": "^1.0.2",
"@radix-ui/react-tabs": "^1.0.1",
"@radix-ui/react-tooltip": "^1.0.2",
@ -26,8 +27,8 @@
"client-zip": "2.2.1",
"cookies-next": "^2.1.1",
"jest": "^29.3.1",
"next": "13.0.5-canary.3",
"next-auth": "^4.16.4",
"next": "13.0.6-canary.1",
"next-auth": "^4.17.0",
"rc-table": "7.24.1",
"react": "18.2.0",
"react-datepicker": "4.8.0",

View file

@ -7,6 +7,7 @@ specifiers:
'@next/bundle-analyzer': 12.1.6
'@next/eslint-plugin-next': 13.0.5-canary.3
'@prisma/client': ^4.6.1
'@radix-ui/react-dropdown-menu': ^2.0.1
'@radix-ui/react-popover': ^1.0.2
'@radix-ui/react-tabs': ^1.0.1
'@radix-ui/react-tooltip': ^1.0.2
@ -25,8 +26,8 @@ specifiers:
eslint-config-next: 13.0.3
jest: ^29.3.1
katex: ^0.16.3
next: 13.0.5-canary.3
next-auth: ^4.16.4
next: 13.0.6-canary.1
next-auth: ^4.17.0
next-unused: 0.0.6
prettier: 2.6.2
prisma: ^4.6.1
@ -47,9 +48,10 @@ specifiers:
dependencies:
'@geist-ui/core': 2.3.8_biqbaboplfbrettd7655fr4n2y
'@geist-ui/icons': 1.0.2_zhza2kbnl2wkkf7vqdl3ton2f4
'@next-auth/prisma-adapter': 1.0.5_2pl3b2nwmjya7el2zbe6cwkney
'@next-auth/prisma-adapter': 1.0.5_o53gfpk3vz2btjrokqfjjwwn3m
'@next/eslint-plugin-next': 13.0.5-canary.3
'@prisma/client': 4.6.1_prisma@4.6.1
'@radix-ui/react-dropdown-menu': 2.0.1_jbvntnid6ohjelon6ccj5dhg2u
'@radix-ui/react-popover': 1.0.2_jbvntnid6ohjelon6ccj5dhg2u
'@radix-ui/react-tabs': 1.0.1_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-tooltip': 1.0.2_jbvntnid6ohjelon6ccj5dhg2u
@ -58,8 +60,8 @@ dependencies:
client-zip: 2.2.1
cookies-next: 2.1.1
jest: 29.3.1_@types+node@17.0.23
next: 13.0.5-canary.3_biqbaboplfbrettd7655fr4n2y
next-auth: 4.16.4_p6ldyhl3eilwbxhfzedqikw7ni
next: 13.0.6-canary.1_biqbaboplfbrettd7655fr4n2y
next-auth: 4.17.0_cejjzyjft5qpe7pbv5t5jzassa
rc-table: 7.24.1_biqbaboplfbrettd7655fr4n2y
react: 18.2.0
react-datepicker: 4.8.0_biqbaboplfbrettd7655fr4n2y
@ -810,14 +812,14 @@ packages:
- supports-color
dev: false
/@next-auth/prisma-adapter/1.0.5_2pl3b2nwmjya7el2zbe6cwkney:
/@next-auth/prisma-adapter/1.0.5_o53gfpk3vz2btjrokqfjjwwn3m:
resolution: {integrity: sha512-VqMS11IxPXrPGXw6Oul6jcyS/n8GLOWzRMrPr3EMdtD6eOalM6zz05j08PcNiis8QzkfuYnCv49OvufTuaEwYQ==}
peerDependencies:
'@prisma/client': '>=2.26.0 || >=3'
next-auth: ^4
dependencies:
'@prisma/client': 4.6.1_prisma@4.6.1
next-auth: 4.16.4_p6ldyhl3eilwbxhfzedqikw7ni
next-auth: 4.17.0_cejjzyjft5qpe7pbv5t5jzassa
dev: false
/@next/bundle-analyzer/12.1.6:
@ -829,8 +831,8 @@ packages:
- utf-8-validate
dev: true
/@next/env/13.0.5-canary.3:
resolution: {integrity: sha512-x8hWJis9ICO6y5e7BNBPjFmFvgDRe5URd5YV3qgkw1flCRDLL3QtfTLig6nXH9DaBHaQ1a0ej7vvlzyDq6iHEg==}
/@next/env/13.0.6-canary.1:
resolution: {integrity: sha512-L88vP2GvU2NvF5YSIDjqYzzJQRNDg3F08qE/pTLClsYXLusRWAJ1lgI9sZFqLrMbZuj2xd0hWYyYjCrrg/LLLw==}
dev: false
/@next/eslint-plugin-next/13.0.3:
@ -845,8 +847,8 @@ packages:
glob: 7.1.7
dev: false
/@next/swc-android-arm-eabi/13.0.5-canary.3:
resolution: {integrity: sha512-t/HW4YMqsXMnyaOvgK4U5wue8sgV6+yjp7eNKVqLCDxMdXemLUIqPTdVOdfU2oVScUNYj5VQFWO52HE/x6LugQ==}
/@next/swc-android-arm-eabi/13.0.6-canary.1:
resolution: {integrity: sha512-3/ZSIsm2yYZXUyqupAri4+6J97wVdHIP2yrcTLoKdYQKm1lp1z5hIZLtxQCV4vLyYNlNIafvIqeasfYWuYzCgw==}
engines: {node: '>= 10'}
cpu: [arm]
os: [android]
@ -854,8 +856,8 @@ packages:
dev: false
optional: true
/@next/swc-android-arm64/13.0.5-canary.3:
resolution: {integrity: sha512-WLNjvoC9sDWqDgYOJDEPKeYwS/tymTvUDBDzQfGyl0XOW8g01wD4S1TBlHDP+D9y85BxLdwlUJoYbOwhAfwI0A==}
/@next/swc-android-arm64/13.0.6-canary.1:
resolution: {integrity: sha512-/J2EXT8L82HgV8sf2SzSdpTuT5gtpPGgekPRCF1ttYyN8FAR/VHPKOLYTbKz3NQ95ogttrW28RFPsMSekQpLaQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [android]
@ -863,8 +865,8 @@ packages:
dev: false
optional: true
/@next/swc-darwin-arm64/13.0.5-canary.3:
resolution: {integrity: sha512-JeGAnktTy7fQeF8PrQJgT25LlNnRFiH6zXWVfHTRk4ufcERifaOnwpzPLjIxF6euo3qD8VynCQhYH/JsVS5HNQ==}
/@next/swc-darwin-arm64/13.0.6-canary.1:
resolution: {integrity: sha512-n+IxXIJSEm3HdumauULSZCehyKyQ0EfWU/qf5oMjyGOQ/sHQfgT5sGYtfnnNvvhSlVgFhyz7MalPRwJQWAIIZQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
@ -872,8 +874,8 @@ packages:
dev: false
optional: true
/@next/swc-darwin-x64/13.0.5-canary.3:
resolution: {integrity: sha512-yehsw+60uE47but0FUJD2+/06tQSsIm1/XFlFFhHv4d+bq39GB2fT+wL+h/aLK+es0yY7rx1+6Yad7CQ7a2J+Q==}
/@next/swc-darwin-x64/13.0.6-canary.1:
resolution: {integrity: sha512-0gQ2zLBFJT8rO080APvY/fHxjSIRVD7VpF0C12BXroFJ3XMnCoa6scniVgKwV4pjhTtlJoLSk3y4wNITyouLDQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
@ -881,8 +883,8 @@ packages:
dev: false
optional: true
/@next/swc-freebsd-x64/13.0.5-canary.3:
resolution: {integrity: sha512-Nd1RicUhogxTsassJNGG+4ha5ezyjJb58J6Vjs9kT5I40Iu8Mw98hCwn6VmiCtJMpY4HGJcuYtCYyXh5hY6ByQ==}
/@next/swc-freebsd-x64/13.0.6-canary.1:
resolution: {integrity: sha512-zAnm1JtxgrNkjiBef7U8lO9JOqNhy13mKA0Z27XCeAunhaar0hNZNHnKUlISVhAZovpHs96fp0mm7Skw0WCkpA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [freebsd]
@ -890,8 +892,8 @@ packages:
dev: false
optional: true
/@next/swc-linux-arm-gnueabihf/13.0.5-canary.3:
resolution: {integrity: sha512-t8TNq1rVBaYJz5dzAJIXgfYh7KRFich6gIT38zJ82McbKWFccuY6+9+FP6n0zkNFAZAd0CBvTQJ8fQFX6XPMog==}
/@next/swc-linux-arm-gnueabihf/13.0.6-canary.1:
resolution: {integrity: sha512-JJCdGBEeNzGXm0AQUfVY9x3C3WvxCcawZ1tCYYkOqLD21Dj5gDTNSZT2cAGFDovfyn5hN/Vltf20Zs8ilUPr1w==}
engines: {node: '>= 10'}
cpu: [arm]
os: [linux]
@ -899,8 +901,8 @@ packages:
dev: false
optional: true
/@next/swc-linux-arm64-gnu/13.0.5-canary.3:
resolution: {integrity: sha512-jsHOBJSkBn247KlW2BG5j1hoLG3EIa1gE/FLCj7w/cQBCtrhb83oGBxat52Wh7UmwajrvNl13clQV0JlmkX3sg==}
/@next/swc-linux-arm64-gnu/13.0.6-canary.1:
resolution: {integrity: sha512-oE9D+Mm240fRpZWZBhFiztmJ/D7uwQ3jfOZcQVf1aN3P+9De+5jyv2TKoHrjrRh5xjloTEDIpt2wNKimlwZvkg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
@ -908,8 +910,8 @@ packages:
dev: false
optional: true
/@next/swc-linux-arm64-musl/13.0.5-canary.3:
resolution: {integrity: sha512-AVsfjFzRKx9I4rbHBOK1WZyl3jF635uLcb7ssrJtxyMGHcr4R9KCTcl+kv7RZ2WNKVafun0pUAITKHoqKVO4fg==}
/@next/swc-linux-arm64-musl/13.0.6-canary.1:
resolution: {integrity: sha512-+DukK2LdoZo4Ds0o2XGAC9324O9auxvHMF4MwDzrXrherBFEeSZUDIxcz4zu5/pd8dyQ8JY9nwILJEevla23pA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
@ -917,8 +919,8 @@ packages:
dev: false
optional: true
/@next/swc-linux-x64-gnu/13.0.5-canary.3:
resolution: {integrity: sha512-E83c9cs+8hwvpTUcn991ec/Q6tDG54SzvVrvNqAj23yiWGfHx0MDt1NqYtF0WOJ7W+6G6PtlD9SR0KKiaPMSeA==}
/@next/swc-linux-x64-gnu/13.0.6-canary.1:
resolution: {integrity: sha512-1bULat6aif5E48s23lAh4vmuVogxz3arXMawlqHwHqwJWpzxF2vyAl3Ilh4mXJnD1xvwiGRH7Aov7HMQJhF2+g==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
@ -926,8 +928,8 @@ packages:
dev: false
optional: true
/@next/swc-linux-x64-musl/13.0.5-canary.3:
resolution: {integrity: sha512-d9CT6qNDxf/C2j8oU6sb2XK2C0qE5N6uI+JL2r19fZKgQIgjCMEWbuyI2oVpq/LrwJ4w0vmjxuRg/9YEb8zX9w==}
/@next/swc-linux-x64-musl/13.0.6-canary.1:
resolution: {integrity: sha512-LN9qXV3Rh1jMcaSXg5DGZYbN6nVCn6A/aQBTw4K5DWu7bHTiEkq4d7/OZN+gfJcqW8BuRtG4zLeraae/1RO9pw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
@ -935,8 +937,8 @@ packages:
dev: false
optional: true
/@next/swc-win32-arm64-msvc/13.0.5-canary.3:
resolution: {integrity: sha512-4Mcw9pc8HgZ0FoOybyfDW17dXXgJnqs8TDXbWDlV9tGrbYJ5yPPe2r0gZaPCGCtbYLLz3XQfd6kDDf2J0hx7Kw==}
/@next/swc-win32-arm64-msvc/13.0.6-canary.1:
resolution: {integrity: sha512-792QCYO+lF4N1c2bwYVwhLwMJMhnmvQRVOnCmRlGhbaZJ0f6ZuIYCxjqAqV8Por+u0ErenRMv5BGleK8wHfQJA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
@ -944,8 +946,8 @@ packages:
dev: false
optional: true
/@next/swc-win32-ia32-msvc/13.0.5-canary.3:
resolution: {integrity: sha512-vGHnW+l+aE264lmz7warC7FPEUb3RDypooKZHDb7fTtlbqnEBf8MVlPYLVj73MSwbW8vKfoatScCG09tljYztA==}
/@next/swc-win32-ia32-msvc/13.0.6-canary.1:
resolution: {integrity: sha512-hwL9JjT7C1ZP+Wj7F4QIoewEErqc70Ax0aeKqQZdKU57djmIWvvvnyh9p/uUQFHLc3M4Um+XvtqpyFZB6RcYbg==}
engines: {node: '>= 10'}
cpu: [ia32]
os: [win32]
@ -953,8 +955,8 @@ packages:
dev: false
optional: true
/@next/swc-win32-x64-msvc/13.0.5-canary.3:
resolution: {integrity: sha512-BvqxBWnY2D0DDBh+NxSQ70+f/kLEEQAbkbyjOcKIxjtYY9Gu6FHAHHyyTNwXMRKr7X3yiX/olLTy1EekCZ0M6A==}
/@next/swc-win32-x64-msvc/13.0.6-canary.1:
resolution: {integrity: sha512-KA6/9W7ZEfkN/ecH8f/fg+WyQaJnRG/UE2IKOdQHbL4uVtHJk5PQmYT+nBZFVnbNHJB2ouNmL4pFJM/7JjNBww==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
@ -1092,6 +1094,26 @@ packages:
react-dom: 18.2.0_react@18.2.0
dev: false
/@radix-ui/react-dropdown-menu/2.0.1_jbvntnid6ohjelon6ccj5dhg2u:
resolution: {integrity: sha512-WDZqmwsNuxdBMkvgB85UeSPAT0wSBd+ojxtzX7lU7uYYh47gacCj6Spo0l9+X4TMe3JA1BBMN9c7OhIMaQeKbg==}
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
dependencies:
'@babel/runtime': 7.20.1
'@radix-ui/primitive': 1.0.0
'@radix-ui/react-compose-refs': 1.0.0_react@18.2.0
'@radix-ui/react-context': 1.0.0_react@18.2.0
'@radix-ui/react-id': 1.0.0_react@18.2.0
'@radix-ui/react-menu': 2.0.1_jbvntnid6ohjelon6ccj5dhg2u
'@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-use-controllable-state': 1.0.0_react@18.2.0
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
transitivePeerDependencies:
- '@types/react'
dev: false
/@radix-ui/react-focus-guards/1.0.0_react@18.2.0:
resolution: {integrity: sha512-UagjDk4ijOAnGu4WMUPj9ahi7/zJJqNZ9ZAiGPp7waUWJO0O1aWXi/udPphI0IUjvrhBsZJGSN66dR2dsueLWQ==}
peerDependencies:
@ -1125,6 +1147,37 @@ packages:
react: 18.2.0
dev: false
/@radix-ui/react-menu/2.0.1_jbvntnid6ohjelon6ccj5dhg2u:
resolution: {integrity: sha512-I5FFZQxCl2fHoJ7R0m5/oWA9EX8/ttH4AbgneoCH7DAXQioFeb0XMAYnOVSp1GgJZ1Nx/mohxNQSeTMcaF1YPw==}
peerDependencies:
react: ^16.8 || ^17.0 || ^18.0
react-dom: ^16.8 || ^17.0 || ^18.0
dependencies:
'@babel/runtime': 7.20.1
'@radix-ui/primitive': 1.0.0
'@radix-ui/react-collection': 1.0.1_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-compose-refs': 1.0.0_react@18.2.0
'@radix-ui/react-context': 1.0.0_react@18.2.0
'@radix-ui/react-direction': 1.0.0_react@18.2.0
'@radix-ui/react-dismissable-layer': 1.0.2_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-focus-guards': 1.0.0_react@18.2.0
'@radix-ui/react-focus-scope': 1.0.1_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-id': 1.0.0_react@18.2.0
'@radix-ui/react-popper': 1.0.1_jbvntnid6ohjelon6ccj5dhg2u
'@radix-ui/react-portal': 1.0.1_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-presence': 1.0.0_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-primitive': 1.0.1_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-roving-focus': 1.0.1_biqbaboplfbrettd7655fr4n2y
'@radix-ui/react-slot': 1.0.1_react@18.2.0
'@radix-ui/react-use-callback-ref': 1.0.0_react@18.2.0
aria-hidden: 1.2.1_ulhmhxukhxjgxaybrsjlob7ffu
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
react-remove-scroll: 2.5.5_ulhmhxukhxjgxaybrsjlob7ffu
transitivePeerDependencies:
- '@types/react'
dev: false
/@radix-ui/react-popover/1.0.2_jbvntnid6ohjelon6ccj5dhg2u:
resolution: {integrity: sha512-4tqZEl9w95R5mlZ/sFdgBnfhCBOEPepLIurBA5kt/qaAhldJ1tNQd0ngr0ET0AHbPotT4mwxMPr7a+MA/wbK0g==}
peerDependencies:
@ -5161,8 +5214,8 @@ packages:
dev: true
optional: true
/next-auth/4.16.4_p6ldyhl3eilwbxhfzedqikw7ni:
resolution: {integrity: sha512-KXW578+ER1u5RcWLwCHMdb/RIBIO6JM8r6xlf9RIPSKzkvDcX9FHiZfJS2vOq/SurHXPJZc4J3OS4IDJpF74Dw==}
/next-auth/4.17.0_cejjzyjft5qpe7pbv5t5jzassa:
resolution: {integrity: sha512-aN2tdnjS0MDeUpB2tBDOaWnegkgeMWrsccujbXRGMJ607b+EwRcy63MFGSr0OAboDJEe0902piXQkt94GqF8Qw==}
engines: {node: ^12.19.0 || ^14.15.0 || ^16.13.0 || ^18.12.0}
peerDependencies:
next: ^12.2.5 || ^13
@ -5177,7 +5230,7 @@ packages:
'@panva/hkdf': 1.0.2
cookie: 0.5.0
jose: 4.11.0
next: 13.0.5-canary.3_biqbaboplfbrettd7655fr4n2y
next: 13.0.6-canary.1_biqbaboplfbrettd7655fr4n2y
oauth: 0.9.15
openid-client: 5.3.0
preact: 10.11.2
@ -5198,8 +5251,8 @@ packages:
- supports-color
dev: true
/next/13.0.5-canary.3_biqbaboplfbrettd7655fr4n2y:
resolution: {integrity: sha512-u3As6SkLXf2u9Mt06B3gBdQzVi45qoH27fyj2UGH+GcWjloz0x0Q+99CEz0sr93zS6iQRZuDn93PMZXoXIa8Og==}
/next/13.0.6-canary.1_biqbaboplfbrettd7655fr4n2y:
resolution: {integrity: sha512-aLHWxU8tMcsVKyStTQvfATZ8TzX/X/VbfV1erVn+pF4uI5+ETvAGWO1geo1FrLzrW+xB/h+9UPAUiqh21oVZEA==}
engines: {node: '>=14.6.0'}
hasBin: true
peerDependencies:
@ -5216,28 +5269,27 @@ packages:
sass:
optional: true
dependencies:
'@next/env': 13.0.5-canary.3
'@next/env': 13.0.6-canary.1
'@swc/helpers': 0.4.14
caniuse-lite: 1.0.30001431
postcss: 8.4.14
react: 18.2.0
react-dom: 18.2.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
optionalDependencies:
'@next/swc-android-arm-eabi': 13.0.5-canary.3
'@next/swc-android-arm64': 13.0.5-canary.3
'@next/swc-darwin-arm64': 13.0.5-canary.3
'@next/swc-darwin-x64': 13.0.5-canary.3
'@next/swc-freebsd-x64': 13.0.5-canary.3
'@next/swc-linux-arm-gnueabihf': 13.0.5-canary.3
'@next/swc-linux-arm64-gnu': 13.0.5-canary.3
'@next/swc-linux-arm64-musl': 13.0.5-canary.3
'@next/swc-linux-x64-gnu': 13.0.5-canary.3
'@next/swc-linux-x64-musl': 13.0.5-canary.3
'@next/swc-win32-arm64-msvc': 13.0.5-canary.3
'@next/swc-win32-ia32-msvc': 13.0.5-canary.3
'@next/swc-win32-x64-msvc': 13.0.5-canary.3
'@next/swc-android-arm-eabi': 13.0.6-canary.1
'@next/swc-android-arm64': 13.0.6-canary.1
'@next/swc-darwin-arm64': 13.0.6-canary.1
'@next/swc-darwin-x64': 13.0.6-canary.1
'@next/swc-freebsd-x64': 13.0.6-canary.1
'@next/swc-linux-arm-gnueabihf': 13.0.6-canary.1
'@next/swc-linux-arm64-gnu': 13.0.6-canary.1
'@next/swc-linux-arm64-musl': 13.0.6-canary.1
'@next/swc-linux-x64-gnu': 13.0.6-canary.1
'@next/swc-linux-x64-musl': 13.0.6-canary.1
'@next/swc-win32-arm64-msvc': 13.0.6-canary.1
'@next/swc-win32-ia32-msvc': 13.0.6-canary.1
'@next/swc-win32-x64-msvc': 13.0.6-canary.1
transitivePeerDependencies:
- '@babel/core'
- babel-plugin-macros
@ -7100,14 +7152,6 @@ packages:
tslib: 2.4.1
dev: false
/use-sync-external-store/1.2.0_react@18.2.0:
resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==}
peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0
dependencies:
react: 18.2.0
dev: false
/util-deprecate/1.0.2:
resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}