oauth2 client editing and adding
This commit is contained in:
parent
3feff2a367
commit
9b9141d5a9
@ -27,6 +27,23 @@
|
|||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.urls {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import useSWR from 'swr';
|
import useSWR, { mutate } from 'swr';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import {
|
import {
|
||||||
OAuth2ClientListItem,
|
OAuth2ClientListItem,
|
||||||
@ -24,6 +24,9 @@ import { ModalProps } from '../../lib/types/modal.interface';
|
|||||||
import { useForm } from '../../lib/hooks/useForm';
|
import { useForm } from '../../lib/hooks/useForm';
|
||||||
import { FormWrapper } from '../common/Form/FormWrapper/FormWrapper';
|
import { FormWrapper } from '../common/Form/FormWrapper/FormWrapper';
|
||||||
import { FormControl } from '../common/Form/FormControl/FormControl';
|
import { FormControl } from '../common/Form/FormControl/FormControl';
|
||||||
|
import toast from 'react-hot-toast';
|
||||||
|
import { Button } from '../common/Button/Button';
|
||||||
|
import userHasPrivileges from '../../lib/utils/has-privileges';
|
||||||
|
|
||||||
const LINK_NAMES = {
|
const LINK_NAMES = {
|
||||||
redirect_uri: 'Redirect URI',
|
redirect_uri: 'Redirect URI',
|
||||||
@ -59,7 +62,10 @@ const LinkEdit = ({
|
|||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
if (!formUrl.type) {
|
if (!formUrl.type) {
|
||||||
formUrl.type = linkType;
|
formUrl.type = linkType;
|
||||||
(formData.urls as Partial<OAuth2ClientURL>[])?.push(formUrl);
|
(formData.urls as Partial<OAuth2ClientURL>[]) = [
|
||||||
|
...(formData.urls || []),
|
||||||
|
formUrl,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
formUrl.url = e.target.value;
|
formUrl.url = e.target.value;
|
||||||
handleInputChange(e, formData.urls, 'urls');
|
handleInputChange(e, formData.urls, 'urls');
|
||||||
@ -73,31 +79,69 @@ const EditClientModal = ({
|
|||||||
close,
|
close,
|
||||||
modalRef,
|
modalRef,
|
||||||
client,
|
client,
|
||||||
}: ModalProps<{ client?: OAuth2ClientListItem }>) => {
|
isAdmin,
|
||||||
|
update,
|
||||||
|
}: ModalProps<{
|
||||||
|
client?: OAuth2ClientListItem;
|
||||||
|
update: () => void;
|
||||||
|
isAdmin: boolean;
|
||||||
|
}>) => {
|
||||||
const formRef = useRef<HTMLFormElement>(null);
|
const formRef = useRef<HTMLFormElement>(null);
|
||||||
|
|
||||||
const { formData, handleInputChange, handleSubmit } = useForm<
|
const { formData, handleInputChange, handleSubmit } = useForm<
|
||||||
Partial<OAuth2ClientListItem>
|
Partial<OAuth2ClientListItem>
|
||||||
>(client || {}, () => {
|
>(client || {}, () => {
|
||||||
console.log(formData);
|
toast
|
||||||
|
.promise(
|
||||||
|
fetch(
|
||||||
|
client
|
||||||
|
? `/api/admin/oauth2/clients/${client.id}`
|
||||||
|
: '/api/admin/oauth2/clients',
|
||||||
|
{
|
||||||
|
method: client ? 'PATCH' : 'POST',
|
||||||
|
headers: {
|
||||||
|
'content-type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ activated: true, ...formData }),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then(async (data) => {
|
||||||
|
if (data.error) {
|
||||||
|
throw data;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
loading: 'Saving client...',
|
||||||
|
success: 'Client saved!',
|
||||||
|
error: (err) => `Saving the client failed: ${err.message}`,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then((data) => {
|
||||||
|
if (data) {
|
||||||
|
close(true);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
});
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal modalRef={modalRef}>
|
<Modal modalRef={modalRef}>
|
||||||
<ModalHeader>
|
<ModalHeader>
|
||||||
<h3>Edit OAuth2 Client</h3>
|
<h3>{client ? 'Edit OAuth2 Client' : 'New OAuth2 Client'}</h3>
|
||||||
</ModalHeader>
|
</ModalHeader>
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<FormWrapper>
|
<FormWrapper>
|
||||||
<form ref={formRef} onSubmit={handleSubmit}>
|
<form ref={formRef} onSubmit={handleSubmit} autoComplete="off">
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<label htmlFor="title">Title</label>
|
<label htmlFor="title">Title</label>
|
||||||
<input
|
<input
|
||||||
id="title"
|
id="title"
|
||||||
type="text"
|
type="text"
|
||||||
name="title"
|
name="title"
|
||||||
value={formData.title}
|
value={formData.title || ''}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@ -106,7 +150,7 @@ const EditClientModal = ({
|
|||||||
<textarea
|
<textarea
|
||||||
id="description"
|
id="description"
|
||||||
name="description"
|
name="description"
|
||||||
value={formData.description}
|
value={formData.description || ''}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@ -115,7 +159,7 @@ const EditClientModal = ({
|
|||||||
<input
|
<input
|
||||||
id="scope"
|
id="scope"
|
||||||
name="scope"
|
name="scope"
|
||||||
value={formData.scope}
|
value={formData.scope || ''}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
@ -124,48 +168,103 @@ const EditClientModal = ({
|
|||||||
<input
|
<input
|
||||||
id="grants"
|
id="grants"
|
||||||
name="grants"
|
name="grants"
|
||||||
value={formData.grants}
|
value={formData.grants || ''}
|
||||||
onChange={handleInputChange}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormControl inline={true}>
|
|
||||||
<label htmlFor="activated">Activated</label>
|
|
||||||
<input
|
|
||||||
id="activated"
|
|
||||||
name="activated"
|
|
||||||
type="checkbox"
|
|
||||||
checked={formData.activated}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormControl inline={true}>
|
|
||||||
<label htmlFor="verified">Verified</label>
|
|
||||||
<input
|
|
||||||
id="verified"
|
|
||||||
name="verified"
|
|
||||||
type="checkbox"
|
|
||||||
checked={formData.verified}
|
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
{isAdmin && (
|
||||||
|
<>
|
||||||
|
<FormControl inline={true}>
|
||||||
|
<label htmlFor="activated">Activated</label>
|
||||||
|
<input
|
||||||
|
id="activated"
|
||||||
|
name="activated"
|
||||||
|
type="checkbox"
|
||||||
|
checked={formData.activated ?? true}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormControl inline={true}>
|
||||||
|
<label htmlFor="verified">Verified</label>
|
||||||
|
<input
|
||||||
|
id="verified"
|
||||||
|
name="verified"
|
||||||
|
type="checkbox"
|
||||||
|
checked={formData.verified ?? false}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
<LinkEdit
|
<LinkEdit
|
||||||
formData={formData}
|
formData={formData}
|
||||||
handleInputChange={handleInputChange}
|
handleInputChange={handleInputChange}
|
||||||
linkType={OAuth2ClientURLType.REDIRECT_URI}
|
linkType={OAuth2ClientURLType.REDIRECT_URI}
|
||||||
></LinkEdit>
|
></LinkEdit>
|
||||||
|
<LinkEdit
|
||||||
|
formData={formData}
|
||||||
|
handleInputChange={handleInputChange}
|
||||||
|
linkType={OAuth2ClientURLType.WEBSITE}
|
||||||
|
></LinkEdit>
|
||||||
|
<LinkEdit
|
||||||
|
formData={formData}
|
||||||
|
handleInputChange={handleInputChange}
|
||||||
|
linkType={OAuth2ClientURLType.PRIVACY}
|
||||||
|
></LinkEdit>
|
||||||
|
<LinkEdit
|
||||||
|
formData={formData}
|
||||||
|
handleInputChange={handleInputChange}
|
||||||
|
linkType={OAuth2ClientURLType.TERMS}
|
||||||
|
></LinkEdit>
|
||||||
</form>
|
</form>
|
||||||
</FormWrapper>
|
</FormWrapper>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<button onClick={() => close(true)}>Cancel</button>
|
<Button onClick={() => close(true)}>Cancel</Button>
|
||||||
<button>New secret</button>
|
{client && (
|
||||||
<button onClick={() => handleSubmit()}>Submit</button>
|
<Button
|
||||||
|
onClick={() =>
|
||||||
|
toast
|
||||||
|
.promise(
|
||||||
|
fetch(`/api/admin/oauth2/clients/${client!.id}/new-secret`, {
|
||||||
|
method: 'POST',
|
||||||
|
})
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then(async (data) => {
|
||||||
|
if (data.error) {
|
||||||
|
throw data;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
loading: 'Generating new secret...',
|
||||||
|
success: 'New secret generated.',
|
||||||
|
error: 'Failed to generate new secret.',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(() => update())
|
||||||
|
}
|
||||||
|
>
|
||||||
|
New secret
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<Button onClick={() => handleSubmit()} variant="primary">
|
||||||
|
Submit
|
||||||
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const OAuth2ClientCard = ({ client }: { client: OAuth2ClientListItem }) => (
|
const OAuth2ClientCard = ({
|
||||||
|
client,
|
||||||
|
isAdmin,
|
||||||
|
update,
|
||||||
|
}: {
|
||||||
|
client: OAuth2ClientListItem;
|
||||||
|
isAdmin: boolean;
|
||||||
|
update: () => void;
|
||||||
|
}) => (
|
||||||
<div className={styles.clientCard}>
|
<div className={styles.clientCard}>
|
||||||
<div className={styles.pictureWrapper}>
|
<div className={styles.pictureWrapper}>
|
||||||
{client.picture ? (
|
{client.picture ? (
|
||||||
@ -185,11 +284,29 @@ const OAuth2ClientCard = ({ client }: { client: OAuth2ClientListItem }) => (
|
|||||||
<Dropdown opens="right">
|
<Dropdown opens="right">
|
||||||
<button
|
<button
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
ModalService.open(EditClientModal, { client: client })
|
ModalService.open(EditClientModal, {
|
||||||
|
client: client,
|
||||||
|
isAdmin,
|
||||||
|
update,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Edit client
|
Edit client
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
toast.promise(
|
||||||
|
navigator.clipboard.writeText(client.client_secret),
|
||||||
|
{
|
||||||
|
loading: 'Copying',
|
||||||
|
success: 'Copied to clipboard',
|
||||||
|
error: 'Copying to clipboard failed.',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Copy secret
|
||||||
|
</button>
|
||||||
{!client.activated && <button>Delete client</button>}
|
{!client.activated && <button>Delete client</button>}
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
@ -201,12 +318,43 @@ const OAuth2ClientCard = ({ client }: { client: OAuth2ClientListItem }) => (
|
|||||||
<dd>{client.scope?.split(' ').join(', ')}</dd>
|
<dd>{client.scope?.split(' ').join(', ')}</dd>
|
||||||
<dt>Allowed grant types</dt>
|
<dt>Allowed grant types</dt>
|
||||||
<dd>{client.grants?.split(' ').join(', ')}</dd>
|
<dd>{client.grants?.split(' ').join(', ')}</dd>
|
||||||
<dt>Activated</dt>
|
{isAdmin && (
|
||||||
<dd>{client.activated ? 'Yes' : <b>NOT ACTIVATED</b>}</dd>
|
<>
|
||||||
<dt>Verified</dt>
|
<dt>Activated</dt>
|
||||||
<dd>{client.verified ? 'Yes' : 'No'}</dd>
|
<dd>{client.activated ? 'Yes' : <b>NOT ACTIVATED</b>}</dd>
|
||||||
|
<dt>Verified</dt>
|
||||||
|
<dd>{client.verified ? 'Yes' : 'No'}</dd>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{client.owner ? (
|
||||||
|
<>
|
||||||
|
<dt>Owner</dt>
|
||||||
|
<dd>
|
||||||
|
{client.owner.uuid} ({client.owner.username})
|
||||||
|
</dd>
|
||||||
|
</>
|
||||||
|
) : undefined}
|
||||||
<dt>Created</dt>
|
<dt>Created</dt>
|
||||||
<dd>{new Date(client.created_at).toDateString()}</dd>
|
<dd>{new Date(client.created_at).toDateString()}</dd>
|
||||||
|
{client.urls?.length ? (
|
||||||
|
<>
|
||||||
|
<dt>URLs</dt>
|
||||||
|
<dd>
|
||||||
|
<div className={styles.urls}>
|
||||||
|
{client.urls.map((url) => (
|
||||||
|
<a
|
||||||
|
key={url.id}
|
||||||
|
href={url.url}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
>
|
||||||
|
{LINK_NAMES[url.type]} <{url.url}>
|
||||||
|
</a>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</dd>
|
||||||
|
</>
|
||||||
|
) : undefined}
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -215,13 +363,15 @@ const OAuth2ClientCard = ({ client }: { client: OAuth2ClientListItem }) => (
|
|||||||
const OAuth2ClientList = ({
|
const OAuth2ClientList = ({
|
||||||
pageIndex,
|
pageIndex,
|
||||||
searchTerm,
|
searchTerm,
|
||||||
|
isAdmin,
|
||||||
setPage,
|
setPage,
|
||||||
}: {
|
}: {
|
||||||
pageIndex: number;
|
pageIndex: number;
|
||||||
searchTerm: string;
|
searchTerm: string;
|
||||||
|
isAdmin: boolean;
|
||||||
setPage: (page: number) => void;
|
setPage: (page: number) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const { data } = useSWR<PaginatedResponse<OAuth2ClientListItem>>(
|
const { data, mutate } = useSWR<PaginatedResponse<OAuth2ClientListItem>>(
|
||||||
`/api/admin/oauth2/clients?page=${pageIndex}${
|
`/api/admin/oauth2/clients?page=${pageIndex}${
|
||||||
searchTerm ? `&q=${searchTerm}` : ''
|
searchTerm ? `&q=${searchTerm}` : ''
|
||||||
}`
|
}`
|
||||||
@ -229,9 +379,24 @@ const OAuth2ClientList = ({
|
|||||||
|
|
||||||
return data ? (
|
return data ? (
|
||||||
<>
|
<>
|
||||||
|
<div className={styles.header}>
|
||||||
|
<h1>OAuth2 clients</h1>
|
||||||
|
<Button
|
||||||
|
onClick={() =>
|
||||||
|
ModalService.open(EditClientModal, { isAdmin, update: mutate })
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Create new
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
<div className={styles.clientList}>
|
<div className={styles.clientList}>
|
||||||
{data.list.map((client) => (
|
{data.list.map((client) => (
|
||||||
<OAuth2ClientCard client={client} key={client.client_id} />
|
<OAuth2ClientCard
|
||||||
|
client={client}
|
||||||
|
key={client.client_id}
|
||||||
|
isAdmin={isAdmin}
|
||||||
|
update={mutate}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
{data?.pagination && (
|
{data?.pagination && (
|
||||||
<Paginator setPage={setPage} pagination={data.pagination}></Paginator>
|
<Paginator setPage={setPage} pagination={data.pagination}></Paginator>
|
||||||
@ -245,6 +410,7 @@ const OAuth2ClientList = ({
|
|||||||
|
|
||||||
export const OAuth2Page = () => {
|
export const OAuth2Page = () => {
|
||||||
const { user } = useUser({ redirectTo: '/login' });
|
const { user } = useUser({ redirectTo: '/login' });
|
||||||
|
const isAdmin = userHasPrivileges(user, 'admin:oauth2');
|
||||||
const [pageIndex, setPageIndex] = useState(1);
|
const [pageIndex, setPageIndex] = useState(1);
|
||||||
const [searchTerm, setSearchTerm] = useState('');
|
const [searchTerm, setSearchTerm] = useState('');
|
||||||
|
|
||||||
@ -252,10 +418,10 @@ export const OAuth2Page = () => {
|
|||||||
<>
|
<>
|
||||||
<Header user={user}></Header>
|
<Header user={user}></Header>
|
||||||
<Container>
|
<Container>
|
||||||
<h1>OAuth2 clients</h1>
|
|
||||||
<OAuth2ClientList
|
<OAuth2ClientList
|
||||||
pageIndex={pageIndex}
|
pageIndex={pageIndex}
|
||||||
searchTerm={searchTerm}
|
searchTerm={searchTerm}
|
||||||
|
isAdmin={isAdmin}
|
||||||
setPage={setPageIndex}
|
setPage={setPageIndex}
|
||||||
/>
|
/>
|
||||||
</Container>
|
</Container>
|
||||||
|
60
components/common/Button/Button.module.scss
Normal file
60
components/common/Button/Button.module.scss
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
.button {
|
||||||
|
appearance: none;
|
||||||
|
padding: 0.5rem 1.5rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background linear 0.33s;
|
||||||
|
|
||||||
|
&.default {
|
||||||
|
border: 1px solid #b9b9b9;
|
||||||
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgb(246 246 246) 0%,
|
||||||
|
rgb(241 241 241) 100%
|
||||||
|
);
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus-visible {
|
||||||
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgb(255 255 255) 0%,
|
||||||
|
rgb(250 250 250) 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgb(241 241 241) 0%,
|
||||||
|
rgb(246 246 246) 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.primary {
|
||||||
|
border: 1px solid #00aaff;
|
||||||
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgb(133 216 255) 0%,
|
||||||
|
rgba(59, 190, 255, 1) 100%
|
||||||
|
);
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus-visible {
|
||||||
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgb(145 220 255) 0%,
|
||||||
|
rgb(84 198 255) 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgb(77, 196, 255) 0%,
|
||||||
|
rgb(124, 213, 255) 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.secondary {
|
||||||
|
}
|
||||||
|
}
|
23
components/common/Button/Button.tsx
Normal file
23
components/common/Button/Button.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { MouseEventHandler } from 'react';
|
||||||
|
import styles from './Button.module.scss';
|
||||||
|
|
||||||
|
export const Button = ({
|
||||||
|
variant = 'default',
|
||||||
|
onClick,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: {
|
||||||
|
variant?: 'default' | 'primary' | 'secondary';
|
||||||
|
onClick: MouseEventHandler<HTMLButtonElement>;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
{...props}
|
||||||
|
className={[styles.button, styles[variant]].join(' ')}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
};
|
@ -17,4 +17,25 @@
|
|||||||
margin-left: 1rem;
|
margin-left: 1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='text'],
|
||||||
|
input[type='password'],
|
||||||
|
input:not([type]),
|
||||||
|
textarea {
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
font-family: inherit;
|
||||||
|
font-weight: 400;
|
||||||
|
border: 1px solid #a4a4a4;
|
||||||
|
box-shadow: inset 0 0 4px #0000001f;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
margin-top: 8%;
|
margin-top: 8%;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 8px 32px #0000006b;
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
|
@ -46,11 +46,13 @@ export default function ModalRoot() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('keyup', handleEscapeKey);
|
window.addEventListener('keyup', handleEscapeKey);
|
||||||
|
document.body.style.overflow = 'hidden';
|
||||||
document.addEventListener('mousedown', handleClickOutside);
|
document.addEventListener('mousedown', handleClickOutside);
|
||||||
document.addEventListener('touchstart', handleClickOutside);
|
document.addEventListener('touchstart', handleClickOutside);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('keyup', handleEscapeKey);
|
window.removeEventListener('keyup', handleEscapeKey);
|
||||||
|
document.body.style.overflow = '';
|
||||||
document.removeEventListener('mousedown', handleClickOutside);
|
document.removeEventListener('mousedown', handleClickOutside);
|
||||||
document.removeEventListener('touchstart', handleClickOutside);
|
document.removeEventListener('touchstart', handleClickOutside);
|
||||||
};
|
};
|
||||||
|
@ -9,9 +9,11 @@ export function useForm<T>(initialState: T, onSubmit: (data: T) => void) {
|
|||||||
formField?: string
|
formField?: string
|
||||||
) => {
|
) => {
|
||||||
const target = e.target as HTMLInputElement;
|
const target = e.target as HTMLInputElement;
|
||||||
|
const checkedOrValue =
|
||||||
|
target.type === 'checkbox' ? target.checked : target.value;
|
||||||
setFormData({
|
setFormData({
|
||||||
...formData,
|
...formData,
|
||||||
[formField || target.name]: setValue ?? target.checked ?? target.value,
|
[formField || target.name]: setValue ?? checkedOrValue,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
504
package-lock.json
generated
504
package-lock.json
generated
@ -13,6 +13,7 @@
|
|||||||
"next": "12.2.5",
|
"next": "12.2.5",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
|
"react-hot-toast": "^2.3.0",
|
||||||
"swr": "^1.3.0"
|
"swr": "^1.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -164,6 +165,126 @@
|
|||||||
"url": "https://github.com/sponsors/isaacs"
|
"url": "https://github.com/sponsors/isaacs"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@next/swc-android-arm-eabi": {
|
||||||
|
"version": "12.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.2.5.tgz",
|
||||||
|
"integrity": "sha512-cPWClKxGhgn2dLWnspW+7psl3MoLQUcNqJqOHk2BhNcou9ARDtC0IjQkKe5qcn9qg7I7U83Gp1yh2aesZfZJMA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@next/swc-android-arm64": {
|
||||||
|
"version": "12.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.2.5.tgz",
|
||||||
|
"integrity": "sha512-vMj0efliXmC5b7p+wfcQCX0AfU8IypjkzT64GiKJD9PgiA3IILNiGJr1fw2lyUDHkjeWx/5HMlMEpLnTsQslwg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"android"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@next/swc-darwin-arm64": {
|
||||||
|
"version": "12.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.2.5.tgz",
|
||||||
|
"integrity": "sha512-VOPWbO5EFr6snla/WcxUKtvzGVShfs302TEMOtzYyWni6f9zuOetijJvVh9CCTzInnXAZMtHyNhefijA4HMYLg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@next/swc-darwin-x64": {
|
||||||
|
"version": "12.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.2.5.tgz",
|
||||||
|
"integrity": "sha512-5o8bTCgAmtYOgauO/Xd27vW52G2/m3i5PX7MUYePquxXAnX73AAtqA3WgPXBRitEB60plSKZgOTkcpqrsh546A==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@next/swc-freebsd-x64": {
|
||||||
|
"version": "12.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-12.2.5.tgz",
|
||||||
|
"integrity": "sha512-yYUbyup1JnznMtEBRkK4LT56N0lfK5qNTzr6/DEyDw5TbFVwnuy2hhLBzwCBkScFVjpFdfiC6SQAX3FrAZzuuw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"freebsd"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@next/swc-linux-arm-gnueabihf": {
|
||||||
|
"version": "12.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.2.5.tgz",
|
||||||
|
"integrity": "sha512-2ZE2/G921Acks7UopJZVMgKLdm4vN4U0yuzvAMJ6KBavPzqESA2yHJlm85TV/K9gIjKhSk5BVtauIUntFRP8cg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@next/swc-linux-arm64-gnu": {
|
||||||
|
"version": "12.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.2.5.tgz",
|
||||||
|
"integrity": "sha512-/I6+PWVlz2wkTdWqhlSYYJ1pWWgUVva6SgX353oqTh8njNQp1SdFQuWDqk8LnM6ulheVfSsgkDzxrDaAQZnzjQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@next/swc-linux-arm64-musl": {
|
||||||
|
"version": "12.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.2.5.tgz",
|
||||||
|
"integrity": "sha512-LPQRelfX6asXyVr59p5sTpx5l+0yh2Vjp/R8Wi4X9pnqcayqT4CUJLiHqCvZuLin3IsFdisJL0rKHMoaZLRfmg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@next/swc-linux-x64-gnu": {
|
"node_modules/@next/swc-linux-x64-gnu": {
|
||||||
"version": "12.2.5",
|
"version": "12.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.2.5.tgz",
|
||||||
@ -196,6 +317,51 @@
|
|||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@next/swc-win32-arm64-msvc": {
|
||||||
|
"version": "12.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.2.5.tgz",
|
||||||
|
"integrity": "sha512-3/90DRNSqeeSRMMEhj4gHHQlLhhKg5SCCoYfE3kBjGpE63EfnblYUqsszGGZ9ekpKL/R4/SGB40iCQr8tR5Jiw==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@next/swc-win32-ia32-msvc": {
|
||||||
|
"version": "12.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.2.5.tgz",
|
||||||
|
"integrity": "sha512-hGLc0ZRAwnaPL4ulwpp4D2RxmkHQLuI8CFOEEHdzZpS63/hMVzv81g8jzYA0UXbb9pus/iTc3VRbVbAM03SRrw==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@next/swc-win32-x64-msvc": {
|
||||||
|
"version": "12.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.2.5.tgz",
|
||||||
|
"integrity": "sha512-7h5/ahY7NeaO2xygqVrSG/Y8Vs4cdjxIjowTZ5W6CKoTKn7tmnuxlUc2h74x06FKmbhAd9agOjr/AOKyxYYm9Q==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@nodelib/fs.scandir": {
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
@ -917,7 +1083,6 @@
|
|||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
|
||||||
"integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==",
|
"integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/damerau-levenshtein": {
|
"node_modules/damerau-levenshtein": {
|
||||||
@ -1848,6 +2013,14 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/goober": {
|
||||||
|
"version": "2.1.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/goober/-/goober-2.1.11.tgz",
|
||||||
|
"integrity": "sha512-5SS2lmxbhqH0u9ABEWq7WPU69a4i2pYcHeCxqaNq6Cw3mnrF0ghWNM4tEGid4dKy8XNIAUbuThuozDHHKJVh3A==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"csstype": "^3.0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/grapheme-splitter": {
|
"node_modules/grapheme-splitter": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
|
||||||
@ -2899,6 +3072,21 @@
|
|||||||
"react": "^18.2.0"
|
"react": "^18.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-hot-toast": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-/RxV+bfjld7tSJR1SCLzMAXgFuNW7fCpK6+vbYqfmbGSWcqTMz2rizrvfWKvtcPH5HK0NqxmBaC5SrAy1F42zA==",
|
||||||
|
"dependencies": {
|
||||||
|
"goober": "^2.1.10"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16",
|
||||||
|
"react-dom": ">=16"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-is": {
|
"node_modules/react-is": {
|
||||||
"version": "16.13.1",
|
"version": "16.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
@ -3487,171 +3675,6 @@
|
|||||||
"funding": {
|
"funding": {
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"node_modules/@next/swc-android-arm-eabi": {
|
|
||||||
"version": "12.2.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.2.5.tgz",
|
|
||||||
"integrity": "sha512-cPWClKxGhgn2dLWnspW+7psl3MoLQUcNqJqOHk2BhNcou9ARDtC0IjQkKe5qcn9qg7I7U83Gp1yh2aesZfZJMA==",
|
|
||||||
"cpu": [
|
|
||||||
"arm"
|
|
||||||
],
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"android"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@next/swc-android-arm64": {
|
|
||||||
"version": "12.2.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.2.5.tgz",
|
|
||||||
"integrity": "sha512-vMj0efliXmC5b7p+wfcQCX0AfU8IypjkzT64GiKJD9PgiA3IILNiGJr1fw2lyUDHkjeWx/5HMlMEpLnTsQslwg==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"android"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@next/swc-darwin-arm64": {
|
|
||||||
"version": "12.2.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.2.5.tgz",
|
|
||||||
"integrity": "sha512-VOPWbO5EFr6snla/WcxUKtvzGVShfs302TEMOtzYyWni6f9zuOetijJvVh9CCTzInnXAZMtHyNhefijA4HMYLg==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@next/swc-darwin-x64": {
|
|
||||||
"version": "12.2.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.2.5.tgz",
|
|
||||||
"integrity": "sha512-5o8bTCgAmtYOgauO/Xd27vW52G2/m3i5PX7MUYePquxXAnX73AAtqA3WgPXBRitEB60plSKZgOTkcpqrsh546A==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@next/swc-freebsd-x64": {
|
|
||||||
"version": "12.2.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-12.2.5.tgz",
|
|
||||||
"integrity": "sha512-yYUbyup1JnznMtEBRkK4LT56N0lfK5qNTzr6/DEyDw5TbFVwnuy2hhLBzwCBkScFVjpFdfiC6SQAX3FrAZzuuw==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"freebsd"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@next/swc-linux-arm-gnueabihf": {
|
|
||||||
"version": "12.2.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.2.5.tgz",
|
|
||||||
"integrity": "sha512-2ZE2/G921Acks7UopJZVMgKLdm4vN4U0yuzvAMJ6KBavPzqESA2yHJlm85TV/K9gIjKhSk5BVtauIUntFRP8cg==",
|
|
||||||
"cpu": [
|
|
||||||
"arm"
|
|
||||||
],
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@next/swc-linux-arm64-gnu": {
|
|
||||||
"version": "12.2.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.2.5.tgz",
|
|
||||||
"integrity": "sha512-/I6+PWVlz2wkTdWqhlSYYJ1pWWgUVva6SgX353oqTh8njNQp1SdFQuWDqk8LnM6ulheVfSsgkDzxrDaAQZnzjQ==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@next/swc-linux-arm64-musl": {
|
|
||||||
"version": "12.2.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.2.5.tgz",
|
|
||||||
"integrity": "sha512-LPQRelfX6asXyVr59p5sTpx5l+0yh2Vjp/R8Wi4X9pnqcayqT4CUJLiHqCvZuLin3IsFdisJL0rKHMoaZLRfmg==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@next/swc-win32-arm64-msvc": {
|
|
||||||
"version": "12.2.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.2.5.tgz",
|
|
||||||
"integrity": "sha512-3/90DRNSqeeSRMMEhj4gHHQlLhhKg5SCCoYfE3kBjGpE63EfnblYUqsszGGZ9ekpKL/R4/SGB40iCQr8tR5Jiw==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@next/swc-win32-ia32-msvc": {
|
|
||||||
"version": "12.2.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.2.5.tgz",
|
|
||||||
"integrity": "sha512-hGLc0ZRAwnaPL4ulwpp4D2RxmkHQLuI8CFOEEHdzZpS63/hMVzv81g8jzYA0UXbb9pus/iTc3VRbVbAM03SRrw==",
|
|
||||||
"cpu": [
|
|
||||||
"ia32"
|
|
||||||
],
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@next/swc-win32-x64-msvc": {
|
|
||||||
"version": "12.2.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.2.5.tgz",
|
|
||||||
"integrity": "sha512-7h5/ahY7NeaO2xygqVrSG/Y8Vs4cdjxIjowTZ5W6CKoTKn7tmnuxlUc2h74x06FKmbhAd9agOjr/AOKyxYYm9Q==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 10"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -3750,6 +3773,54 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@next/swc-android-arm-eabi": {
|
||||||
|
"version": "12.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.2.5.tgz",
|
||||||
|
"integrity": "sha512-cPWClKxGhgn2dLWnspW+7psl3MoLQUcNqJqOHk2BhNcou9ARDtC0IjQkKe5qcn9qg7I7U83Gp1yh2aesZfZJMA==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@next/swc-android-arm64": {
|
||||||
|
"version": "12.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.2.5.tgz",
|
||||||
|
"integrity": "sha512-vMj0efliXmC5b7p+wfcQCX0AfU8IypjkzT64GiKJD9PgiA3IILNiGJr1fw2lyUDHkjeWx/5HMlMEpLnTsQslwg==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@next/swc-darwin-arm64": {
|
||||||
|
"version": "12.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.2.5.tgz",
|
||||||
|
"integrity": "sha512-VOPWbO5EFr6snla/WcxUKtvzGVShfs302TEMOtzYyWni6f9zuOetijJvVh9CCTzInnXAZMtHyNhefijA4HMYLg==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@next/swc-darwin-x64": {
|
||||||
|
"version": "12.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.2.5.tgz",
|
||||||
|
"integrity": "sha512-5o8bTCgAmtYOgauO/Xd27vW52G2/m3i5PX7MUYePquxXAnX73AAtqA3WgPXBRitEB60plSKZgOTkcpqrsh546A==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@next/swc-freebsd-x64": {
|
||||||
|
"version": "12.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-12.2.5.tgz",
|
||||||
|
"integrity": "sha512-yYUbyup1JnznMtEBRkK4LT56N0lfK5qNTzr6/DEyDw5TbFVwnuy2hhLBzwCBkScFVjpFdfiC6SQAX3FrAZzuuw==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@next/swc-linux-arm-gnueabihf": {
|
||||||
|
"version": "12.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.2.5.tgz",
|
||||||
|
"integrity": "sha512-2ZE2/G921Acks7UopJZVMgKLdm4vN4U0yuzvAMJ6KBavPzqESA2yHJlm85TV/K9gIjKhSk5BVtauIUntFRP8cg==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@next/swc-linux-arm64-gnu": {
|
||||||
|
"version": "12.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.2.5.tgz",
|
||||||
|
"integrity": "sha512-/I6+PWVlz2wkTdWqhlSYYJ1pWWgUVva6SgX353oqTh8njNQp1SdFQuWDqk8LnM6ulheVfSsgkDzxrDaAQZnzjQ==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@next/swc-linux-arm64-musl": {
|
||||||
|
"version": "12.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.2.5.tgz",
|
||||||
|
"integrity": "sha512-LPQRelfX6asXyVr59p5sTpx5l+0yh2Vjp/R8Wi4X9pnqcayqT4CUJLiHqCvZuLin3IsFdisJL0rKHMoaZLRfmg==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"@next/swc-linux-x64-gnu": {
|
"@next/swc-linux-x64-gnu": {
|
||||||
"version": "12.2.5",
|
"version": "12.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-12.2.5.tgz",
|
||||||
@ -3762,6 +3833,24 @@
|
|||||||
"integrity": "sha512-zg/Y6oBar1yVnW6Il1I/08/2ukWtOG6s3acdJdEyIdsCzyQi4RLxbbhkD/EGQyhqBvd3QrC6ZXQEXighQUAZ0g==",
|
"integrity": "sha512-zg/Y6oBar1yVnW6Il1I/08/2ukWtOG6s3acdJdEyIdsCzyQi4RLxbbhkD/EGQyhqBvd3QrC6ZXQEXighQUAZ0g==",
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"@next/swc-win32-arm64-msvc": {
|
||||||
|
"version": "12.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.2.5.tgz",
|
||||||
|
"integrity": "sha512-3/90DRNSqeeSRMMEhj4gHHQlLhhKg5SCCoYfE3kBjGpE63EfnblYUqsszGGZ9ekpKL/R4/SGB40iCQr8tR5Jiw==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@next/swc-win32-ia32-msvc": {
|
||||||
|
"version": "12.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.2.5.tgz",
|
||||||
|
"integrity": "sha512-hGLc0ZRAwnaPL4ulwpp4D2RxmkHQLuI8CFOEEHdzZpS63/hMVzv81g8jzYA0UXbb9pus/iTc3VRbVbAM03SRrw==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@next/swc-win32-x64-msvc": {
|
||||||
|
"version": "12.2.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.2.5.tgz",
|
||||||
|
"integrity": "sha512-7h5/ahY7NeaO2xygqVrSG/Y8Vs4cdjxIjowTZ5W6CKoTKn7tmnuxlUc2h74x06FKmbhAd9agOjr/AOKyxYYm9Q==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
"@nodelib/fs.scandir": {
|
"@nodelib/fs.scandir": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
@ -4273,8 +4362,7 @@
|
|||||||
"csstype": {
|
"csstype": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
|
||||||
"integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA==",
|
"integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"damerau-levenshtein": {
|
"damerau-levenshtein": {
|
||||||
"version": "1.0.8",
|
"version": "1.0.8",
|
||||||
@ -4931,6 +5019,12 @@
|
|||||||
"slash": "^3.0.0"
|
"slash": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"goober": {
|
||||||
|
"version": "2.1.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/goober/-/goober-2.1.11.tgz",
|
||||||
|
"integrity": "sha512-5SS2lmxbhqH0u9ABEWq7WPU69a4i2pYcHeCxqaNq6Cw3mnrF0ghWNM4tEGid4dKy8XNIAUbuThuozDHHKJVh3A==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
"grapheme-splitter": {
|
"grapheme-splitter": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
|
||||||
@ -5603,6 +5697,14 @@
|
|||||||
"scheduler": "^0.23.0"
|
"scheduler": "^0.23.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-hot-toast": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-/RxV+bfjld7tSJR1SCLzMAXgFuNW7fCpK6+vbYqfmbGSWcqTMz2rizrvfWKvtcPH5HK0NqxmBaC5SrAy1F42zA==",
|
||||||
|
"requires": {
|
||||||
|
"goober": "^2.1.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-is": {
|
"react-is": {
|
||||||
"version": "16.13.1",
|
"version": "16.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
@ -5982,72 +6084,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
||||||
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
|
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
|
||||||
"@next/swc-android-arm-eabi": {
|
|
||||||
"version": "12.2.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-12.2.5.tgz",
|
|
||||||
"integrity": "sha512-cPWClKxGhgn2dLWnspW+7psl3MoLQUcNqJqOHk2BhNcou9ARDtC0IjQkKe5qcn9qg7I7U83Gp1yh2aesZfZJMA==",
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"@next/swc-android-arm64": {
|
|
||||||
"version": "12.2.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-12.2.5.tgz",
|
|
||||||
"integrity": "sha512-vMj0efliXmC5b7p+wfcQCX0AfU8IypjkzT64GiKJD9PgiA3IILNiGJr1fw2lyUDHkjeWx/5HMlMEpLnTsQslwg==",
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"@next/swc-darwin-arm64": {
|
|
||||||
"version": "12.2.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-12.2.5.tgz",
|
|
||||||
"integrity": "sha512-VOPWbO5EFr6snla/WcxUKtvzGVShfs302TEMOtzYyWni6f9zuOetijJvVh9CCTzInnXAZMtHyNhefijA4HMYLg==",
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"@next/swc-darwin-x64": {
|
|
||||||
"version": "12.2.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-12.2.5.tgz",
|
|
||||||
"integrity": "sha512-5o8bTCgAmtYOgauO/Xd27vW52G2/m3i5PX7MUYePquxXAnX73AAtqA3WgPXBRitEB60plSKZgOTkcpqrsh546A==",
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"@next/swc-freebsd-x64": {
|
|
||||||
"version": "12.2.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-12.2.5.tgz",
|
|
||||||
"integrity": "sha512-yYUbyup1JnznMtEBRkK4LT56N0lfK5qNTzr6/DEyDw5TbFVwnuy2hhLBzwCBkScFVjpFdfiC6SQAX3FrAZzuuw==",
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"@next/swc-linux-arm-gnueabihf": {
|
|
||||||
"version": "12.2.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-12.2.5.tgz",
|
|
||||||
"integrity": "sha512-2ZE2/G921Acks7UopJZVMgKLdm4vN4U0yuzvAMJ6KBavPzqESA2yHJlm85TV/K9gIjKhSk5BVtauIUntFRP8cg==",
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"@next/swc-linux-arm64-gnu": {
|
|
||||||
"version": "12.2.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-12.2.5.tgz",
|
|
||||||
"integrity": "sha512-/I6+PWVlz2wkTdWqhlSYYJ1pWWgUVva6SgX353oqTh8njNQp1SdFQuWDqk8LnM6ulheVfSsgkDzxrDaAQZnzjQ==",
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"@next/swc-linux-arm64-musl": {
|
|
||||||
"version": "12.2.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-12.2.5.tgz",
|
|
||||||
"integrity": "sha512-LPQRelfX6asXyVr59p5sTpx5l+0yh2Vjp/R8Wi4X9pnqcayqT4CUJLiHqCvZuLin3IsFdisJL0rKHMoaZLRfmg==",
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"@next/swc-win32-arm64-msvc": {
|
|
||||||
"version": "12.2.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-12.2.5.tgz",
|
|
||||||
"integrity": "sha512-3/90DRNSqeeSRMMEhj4gHHQlLhhKg5SCCoYfE3kBjGpE63EfnblYUqsszGGZ9ekpKL/R4/SGB40iCQr8tR5Jiw==",
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"@next/swc-win32-ia32-msvc": {
|
|
||||||
"version": "12.2.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-12.2.5.tgz",
|
|
||||||
"integrity": "sha512-hGLc0ZRAwnaPL4ulwpp4D2RxmkHQLuI8CFOEEHdzZpS63/hMVzv81g8jzYA0UXbb9pus/iTc3VRbVbAM03SRrw==",
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"@next/swc-win32-x64-msvc": {
|
|
||||||
"version": "12.2.5",
|
|
||||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-12.2.5.tgz",
|
|
||||||
"integrity": "sha512-7h5/ahY7NeaO2xygqVrSG/Y8Vs4cdjxIjowTZ5W6CKoTKn7tmnuxlUc2h74x06FKmbhAd9agOjr/AOKyxYYm9Q==",
|
|
||||||
"optional": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
"next": "12.2.5",
|
"next": "12.2.5",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
|
"react-hot-toast": "^2.3.0",
|
||||||
"swr": "^1.3.0"
|
"swr": "^1.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -3,6 +3,7 @@ import type { AppProps } from 'next/app';
|
|||||||
import { SWRConfig } from 'swr';
|
import { SWRConfig } from 'swr';
|
||||||
import fetchJson from '../lib/utils/swr-fetcher';
|
import fetchJson from '../lib/utils/swr-fetcher';
|
||||||
import ModalRoot from '../components/common/Modal/ModalRoot/ModalRoot';
|
import ModalRoot from '../components/common/Modal/ModalRoot/ModalRoot';
|
||||||
|
import { Toaster } from 'react-hot-toast';
|
||||||
|
|
||||||
function MyApp({ Component, pageProps }: AppProps) {
|
function MyApp({ Component, pageProps }: AppProps) {
|
||||||
return (
|
return (
|
||||||
@ -14,6 +15,7 @@ function MyApp({ Component, pageProps }: AppProps) {
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<Toaster position="top-right" />
|
||||||
<Component {...pageProps} />
|
<Component {...pageProps} />
|
||||||
<ModalRoot />
|
<ModalRoot />
|
||||||
</SWRConfig>
|
</SWRConfig>
|
||||||
|
8
styles/_focus.scss
Normal file
8
styles/_focus.scss
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
input,
|
||||||
|
button,
|
||||||
|
textarea,
|
||||||
|
a {
|
||||||
|
&:focus {
|
||||||
|
outline: 4px solid #94cfff9c;
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
@import 'breakpoint';
|
@import 'breakpoint';
|
||||||
|
@import 'focus';
|
||||||
|
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
@ -10,8 +11,12 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: inherit;
|
color: #00aaff;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
|
18
yarn.lock
18
yarn.lock
@ -479,7 +479,7 @@
|
|||||||
"shebang-command" "^2.0.0"
|
"shebang-command" "^2.0.0"
|
||||||
"which" "^2.0.1"
|
"which" "^2.0.1"
|
||||||
|
|
||||||
"csstype@^3.0.2":
|
"csstype@^3.0.10", "csstype@^3.0.2":
|
||||||
"integrity" "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
|
"integrity" "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
|
||||||
"resolved" "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz"
|
"resolved" "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz"
|
||||||
"version" "3.1.0"
|
"version" "3.1.0"
|
||||||
@ -1005,6 +1005,11 @@
|
|||||||
"merge2" "^1.4.1"
|
"merge2" "^1.4.1"
|
||||||
"slash" "^3.0.0"
|
"slash" "^3.0.0"
|
||||||
|
|
||||||
|
"goober@^2.1.10":
|
||||||
|
"integrity" "sha512-5SS2lmxbhqH0u9ABEWq7WPU69a4i2pYcHeCxqaNq6Cw3mnrF0ghWNM4tEGid4dKy8XNIAUbuThuozDHHKJVh3A=="
|
||||||
|
"resolved" "https://registry.npmjs.org/goober/-/goober-2.1.11.tgz"
|
||||||
|
"version" "2.1.11"
|
||||||
|
|
||||||
"grapheme-splitter@^1.0.4":
|
"grapheme-splitter@^1.0.4":
|
||||||
"integrity" "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ=="
|
"integrity" "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ=="
|
||||||
"resolved" "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz"
|
"resolved" "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz"
|
||||||
@ -1555,7 +1560,7 @@
|
|||||||
"resolved" "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz"
|
"resolved" "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz"
|
||||||
"version" "1.2.3"
|
"version" "1.2.3"
|
||||||
|
|
||||||
"react-dom@^17.0.2 || ^18.0.0-0", "react-dom@18.2.0":
|
"react-dom@^17.0.2 || ^18.0.0-0", "react-dom@>=16", "react-dom@18.2.0":
|
||||||
"integrity" "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g=="
|
"integrity" "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g=="
|
||||||
"resolved" "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz"
|
"resolved" "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz"
|
||||||
"version" "18.2.0"
|
"version" "18.2.0"
|
||||||
@ -1563,12 +1568,19 @@
|
|||||||
"loose-envify" "^1.1.0"
|
"loose-envify" "^1.1.0"
|
||||||
"scheduler" "^0.23.0"
|
"scheduler" "^0.23.0"
|
||||||
|
|
||||||
|
"react-hot-toast@^2.3.0":
|
||||||
|
"integrity" "sha512-/RxV+bfjld7tSJR1SCLzMAXgFuNW7fCpK6+vbYqfmbGSWcqTMz2rizrvfWKvtcPH5HK0NqxmBaC5SrAy1F42zA=="
|
||||||
|
"resolved" "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.3.0.tgz"
|
||||||
|
"version" "2.3.0"
|
||||||
|
dependencies:
|
||||||
|
"goober" "^2.1.10"
|
||||||
|
|
||||||
"react-is@^16.13.1":
|
"react-is@^16.13.1":
|
||||||
"integrity" "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
"integrity" "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||||
"resolved" "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
|
"resolved" "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz"
|
||||||
"version" "16.13.1"
|
"version" "16.13.1"
|
||||||
|
|
||||||
"react@^16.11.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^17.0.2 || ^18.0.0-0", "react@^18.2.0", "react@>= 16.8.0 || 17.x.x || ^18.0.0-0", "react@18.2.0":
|
"react@^16.11.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^17.0.2 || ^18.0.0-0", "react@^18.2.0", "react@>= 16.8.0 || 17.x.x || ^18.0.0-0", "react@>=16", "react@18.2.0":
|
||||||
"integrity" "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ=="
|
"integrity" "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ=="
|
||||||
"resolved" "https://registry.npmjs.org/react/-/react-18.2.0.tgz"
|
"resolved" "https://registry.npmjs.org/react/-/react-18.2.0.tgz"
|
||||||
"version" "18.2.0"
|
"version" "18.2.0"
|
||||||
|
Reference in New Issue
Block a user