diff --git a/components/commons/Cta.module.css b/components/commons/Cta.module.css new file mode 100644 index 000000000..216672028 --- /dev/null +++ b/components/commons/Cta.module.css @@ -0,0 +1,24 @@ +.shortButton { + background-image: var(--gradient-purpleblue-skyblue); + font-size: 1.8rem; + text-align: center; + text-decoration: none; + color: var(--white-color); + font-weight: 600; + border-radius: 0.8rem; + border: none; + padding: 1.6rem 2rem; + cursor: pointer; + width: 12.8rem; +} + +.longButton { + background-image: linear-gradient(90deg, #6d6afe, #6ae3fe); + font-size: 1.8rem; + text-align: center; + color: var(--white-color, #ffffff); + font-weight: 600; + border-radius: 0.8rem; + padding: 1.6rem 2rem; + width: 40rem; +} diff --git a/components/commons/Cta.tsx b/components/commons/Cta.tsx new file mode 100644 index 000000000..6c7845c16 --- /dev/null +++ b/components/commons/Cta.tsx @@ -0,0 +1,23 @@ +import React, { ReactNode } from "react"; +import styles from "./Cta.module.css"; + +interface CtaProps { + children: ReactNode; + onClick?: () => void; +} + +export function CtaShort({ children, onClick }: CtaProps) { + return ( + + ); +} + +export function CtaLong({ children, onClick }: CtaProps) { + return ( + + ); +} diff --git a/components/commons/Footer.module.css b/components/commons/Footer.module.css new file mode 100644 index 000000000..ba01df1be --- /dev/null +++ b/components/commons/Footer.module.css @@ -0,0 +1,55 @@ +.footer { + background-color: var(--black-color); + padding: 3.2rem 10.4rem 6.4rem 10.4rem; + font-size: 1.6rem; +} + +.info { + display: flex; + justify-content: space-between; + height: 6.4rem; + width: 100%; + + @media (min-width: 375px) and (max-width: 767px) { + display: grid; + row-gap: 6rem; + grid-template: + "policy sns" + "codeit ."; + } +} + +.codeit { + color: #676767; + font-size: 1.6rem; + + @media (min-width: 375px) and (max-width: 767px) { + grid-area: codeit; + } +} + +.policy { + display: flex; + gap: 3rem; + + @media (min-width: 375px) and (max-width: 767px) { + grid-area: policy; + } +} + +.link { + color: #676767; + text-decoration: none; + text-align: center; + font-size: 1.6rem; +} + +.sns { + display: flex; + gap: 1.2rem; + cursor: pointer; + + @media (min-width: 375px) and (max-width: 767px) { + grid-area: sns; + } +} diff --git a/components/commons/Footer.tsx b/components/commons/Footer.tsx new file mode 100644 index 000000000..6d946959b --- /dev/null +++ b/components/commons/Footer.tsx @@ -0,0 +1,36 @@ +import styles from "./Footer.module.css"; +import Image from "next/image"; + +function Footer() { + return ( + + ); +} + +export default Footer; diff --git a/components/commons/Gnb.module.css b/components/commons/Gnb.module.css new file mode 100644 index 000000000..db32357df --- /dev/null +++ b/components/commons/Gnb.module.css @@ -0,0 +1,43 @@ +.nav { + background-color: var(--gray-bg-color); + padding: 2rem 20rem; + position: sticky; + top: 0; + left: 0; + width: 100%; + z-index: 2; + + @media (min-width: 768px) and (max-width: 1199px) { + padding: 2rem 0; + } + + @media (min-width: 375px) and (max-width: 767px) { + padding: 2rem 3.2rem; + } +} + +.navContainer { + display: flex; + justify-content: space-between; + align-items: center; + max-width: 192rem; + margin: 0 auto; + + @media (min-width: 768px) and (max-width: 1199px) { + max-width: 80rem; + } +} + +.gnb { + display: flex; + gap: 0.6rem; + align-items: center; +} + +.gnbImg { + border-radius: 9999px; +} + +.email { + font-size: 1.4rem; +} diff --git a/components/commons/Gnb.tsx b/components/commons/Gnb.tsx new file mode 100644 index 000000000..dfcde06fe --- /dev/null +++ b/components/commons/Gnb.tsx @@ -0,0 +1,57 @@ +import React, { useEffect, useState } from "react"; +import Link from "next/link"; +import styles from "./Gnb.module.css"; +import { getProfiles } from "@/lib/api"; +import Image from "next/image"; +import { useRouter } from "next/router"; +import { CtaShort } from "./Cta"; + +interface Profile { + id: number; + email: string; + image_source: string; +} + +function Gnb() { + const [userData, setUserData] = useState([]); + const router = useRouter(); + + useEffect(() => { + const fetchUserData = async () => { + const data = await getProfiles(); + setUserData(data.data); + }; + fetchUserData(); + }, []); + + const onLogin = () => { + // setUserData((prev) => !prev); + router.push("/auth/signin"); + }; + + return ( + + ); +} + +export default Gnb; diff --git a/components/commons/SearchBar.module.css b/components/commons/SearchBar.module.css new file mode 100644 index 000000000..4c672d131 --- /dev/null +++ b/components/commons/SearchBar.module.css @@ -0,0 +1,42 @@ +.searchBarContainer { + position: relative; +} + +.searchBarInput { + background-color: #f5f5f5; + width: 100%; + height: 5.4rem; + border: none; + border-radius: 1rem; + padding-left: 4rem; + margin: 4rem auto; +} + +.searchIcon { + position: absolute; + top: 50%; + left: 1.6rem; + transform: translateY(-50%); +} + +.closeIcon { + width: 2.4rem; + height: 2.4rem; + position: absolute; + top: 50%; + right: 1.6rem; + transform: translateY(-50%); + background-image: url("/images/folder/_close.png"); + background-size: 100%; +} + +.searchResult { + font-size: 3.2rem; + margin-bottom: 4rem; + color: rgba(159, 166, 178, 1); +} + +.searchWord { + font-size: 3.2rem; + color: rgba(55, 55, 64, 1); +} diff --git a/components/commons/SearchBar.tsx b/components/commons/SearchBar.tsx new file mode 100644 index 000000000..60c7d4e06 --- /dev/null +++ b/components/commons/SearchBar.tsx @@ -0,0 +1,34 @@ +import React, { useState } from "react"; +import Image from "next/image"; +import styles from "./SearchBar.module.css"; +function SearchBar({ onSearchChange }: { onSearchChange: (value: string) => void }) { + const [value, setValue] = useState(""); + + const handleChange = (e: React.ChangeEvent) => { + setValue(e.target.value); + onSearchChange(e.target.value); + }; + + return ( +
+
+ 검색 아이콘 + + {value &&
+ {value !== "" ? ( +
+ {value}로 검색한 결과입니다. +
+ ) : null} +
+ ); +} + +export default SearchBar; diff --git a/components/domains/auth/AuthInput.module.css b/components/domains/auth/AuthInput.module.css new file mode 100644 index 000000000..abdeb8fd1 --- /dev/null +++ b/components/domains/auth/AuthInput.module.css @@ -0,0 +1,48 @@ +.signInputBox { + display: flex; + flex-direction: column; + row-gap: 1.2rem; + position: relative; +} + +.input { + padding: 1.8rem 1.5rem; + border-radius: 0.8rem; + font-size: 1.6rem; + line-height: 150%; + border: 0.1rem solid var(--gray-20-color); +} + +.input.isError { + border: 0.1rem solid var(--red-color); +} + +.input.isError:focus { + outline: var(--red-color); +} + +.input:focus { + border-color: var(--primary-color); +} + +.signInputLabel { + font-size: 1.4rem; + font-weight: 400; +} + +.eyeButton { + position: absolute; + top: 5.1rem; + right: 1.5rem; + background: transparent; + border: none; + cursor: pointer; +} + +.errorMessage { + margin-top: -0.4rem; + font-size: 1.4rem; + font-weight: 400; + color: var(--red-color); + display: inline-block; +} diff --git a/components/domains/auth/AuthInput.tsx b/components/domains/auth/AuthInput.tsx new file mode 100644 index 000000000..549bcc31f --- /dev/null +++ b/components/domains/auth/AuthInput.tsx @@ -0,0 +1,54 @@ +import React, { useState } from "react"; +import Image from "next/image"; +import styles from "./AuthInput.module.css"; + +interface AuthInputProps { + label: string; + type: "text" | "email" | "password"; + placeholder?: string; + onValid?: () => boolean; + errorMessage?: string; +} + +function AuthInput({ label, type, placeholder, onValid, errorMessage }: AuthInputProps) { + const [inputType, setInputType] = useState(type); + const [isError, setIsError] = useState(false); + + const togglePasswordVisibility = (e: React.MouseEvent) => { + e.preventDefault(); + setInputType(inputType === "password" ? "text" : "password"); + }; + + const handleBlur = () => { + if (onValid) { + const error = onValid(); + setIsError(error); + } + }; + + return ( +
+ + + {type === "password" && ( + + )} + {isError &&

{errorMessage}

} +
+ ); +} + +export default AuthInput; diff --git a/components/domains/folder/AddLink.module.css b/components/domains/folder/AddLink.module.css new file mode 100644 index 000000000..58f9130b0 --- /dev/null +++ b/components/domains/folder/AddLink.module.css @@ -0,0 +1,98 @@ +.addLinkSection { + background-color: var(--gray-bg-color); + padding: 3.2rem; +} + +.addLinkBar { + background-color: var(--gray-bg-color); + padding: 2.4rem; + z-index: 9; + position: static; + width: 100%; + + @media (max-width: 1200px) { + padding-left: 3.2rem; + padding-right: 3.2rem; + } +} + +.addLinkBar .sticky { + position: fixed; + bottom: 0px; + left: 0px; +} + +.addLinkContainer { + position: relative; + max-width: 80rem; + margin: auto; +} + +.addLinkInput { + width: 100%; + padding-left: 5rem; + height: 6.9rem; + border-radius: 1.5rem; + border: 1px solid var(--primary-color); + color: var(--gray-60-color); + font-size: 1.6rem; +} + +.addLinkButton { + background-image: var(--gradient-purpleblue-skyblue); + color: var(--white-color); + border-radius: 0.8rem; + position: absolute; + top: 50%; + right: 2rem; + transform: translateY(-50%); + width: 8.1rem; + font-size: 1.4rem; + font-weight: 600; + padding: 1rem 1.6rem; +} + +.addLinkImg { + position: absolute; + top: 50%; + left: 2rem; + transform: translateY(-50%); +} + +.linkButton { + display: flex; + align-items: center; + gap: 0.8rem; + font-size: 1.6rem; + font-weight: 400; + text-align: start; + border-radius: 0.8rem; + padding: 0.8rem; + width: 26.4rem; + height: 4rem; +} + +.linkButton:hover, +.linkButton:focus { + background-color: var(--gray-bg-color); +} + +.linkButton div { + font-size: 1.4rem; +} + +.linkButton span { + flex: 1; + color: var(--gray-60-color); + font-size: 1.4rem; +} + +.linkButton img { + width: 1.4rem; + height: 1.4rem; +} + +.folderLinkList { + display: flex; + flex-direction: column; +} diff --git a/components/domains/folder/AddLink.tsx b/components/domains/folder/AddLink.tsx new file mode 100644 index 000000000..28893bfa8 --- /dev/null +++ b/components/domains/folder/AddLink.tsx @@ -0,0 +1,115 @@ +import React, { useState, useEffect, FC, forwardRef } from "react"; +import Image from "next/image"; +import styles from "./AddLink.module.css"; +import { getLinks, getFolders } from "@/lib/api"; +import { LinkData, FolderData } from "@/lib/utils/type"; +import useModal, { ModalProps } from "@/lib/useModal"; +import ModalPotal from "@/lib/Portal"; + +interface FolderLinkButtonProps { + folderId?: number; + folderName?: string; + onClick?: () => void; +} + +function FolderLinkButton({ folderId, folderName, onClick }: FolderLinkButtonProps) { + const [isActive, setIsActive] = useState(false); + const [links, setLinks] = useState([]); + + useEffect(() => { + const fetchLinks = async () => { + const apiEndpoint: any = folderId; + + const link = await getLinks(apiEndpoint); + setLinks(link.data); + }; + + fetchLinks(); + }, [folderId]); + + const handleFolderClick = () => { + setIsActive(!isActive); + if (onClick) { + onClick(); + } + }; + + return ( + + ); +} + +function FolderLinks() { + const [folders, setFolders] = useState([]); + + useEffect(() => { + const fetchFolders = async () => { + const folder = await getFolders(); + + setFolders(folder.data); + }; + + fetchFolders(); + }, []); + + return ( +
+ {folders.map((folder) => ( + + ))} +
+ ); +} + +export interface AddLinkProps { + $isSticky: boolean; +} + +const AddLink = forwardRef(function AddLink({ $isSticky }, ref) { + const { Modal, openModal } = useModal(); + const [linkInput, setLinkInput] = useState(""); + + const handleInputChange = (e: React.ChangeEvent) => { + setLinkInput(e.target.value); + }; + + return ( +
+
+
+ + 링크 아이콘 + + + {(Modal as FC)({ + title: "폴더에 추가", + link: linkInput, + list: , + button: "추가하기", + color: "blue", + })} + +
+
+
+ ); +}); + +export default AddLink; diff --git a/components/domains/folder/FolderAddButton.module.css b/components/domains/folder/FolderAddButton.module.css new file mode 100644 index 000000000..094b014e6 --- /dev/null +++ b/components/domains/folder/FolderAddButton.module.css @@ -0,0 +1,13 @@ +.folderAddButton { + display: none; + width: 13.5rem; + height: 3.6rem; + cursor: pointer; + + @media (max-width: 767px) { + display: block; + position: sticky; + bottom: 101px; + margin: auto; + } +} diff --git a/components/domains/folder/FolderAddButton.tsx b/components/domains/folder/FolderAddButton.tsx new file mode 100644 index 000000000..a8022adfa --- /dev/null +++ b/components/domains/folder/FolderAddButton.tsx @@ -0,0 +1,13 @@ +import Image from "next/image"; +import styles from "./FolderAddButton.module.css"; +import React from "react"; + +function FolderAddButton() { + return ( + + ); +} + +export default FolderAddButton; diff --git a/components/domains/folder/FolderButton.module.css b/components/domains/folder/FolderButton.module.css new file mode 100644 index 000000000..2e769377b --- /dev/null +++ b/components/domains/folder/FolderButton.module.css @@ -0,0 +1,13 @@ +.folderButton { + padding: 8px 12px; + border-radius: 5px; + border: 1px solid var(--primary-color); + background: var(--white-color); + height: 4rem; +} + +.folderButton:hover, +.folderButton:focus { + background-color: var(--primary-color); + color: var(--white-color); +} diff --git a/components/domains/folder/FolderButton.tsx b/components/domains/folder/FolderButton.tsx new file mode 100644 index 000000000..1bb38921c --- /dev/null +++ b/components/domains/folder/FolderButton.tsx @@ -0,0 +1,26 @@ +import styles from "./FolderButton.module.css"; +// import { Link } from "react-router-dom"; +import Link from "next/link"; + +interface FolderButtonProps { + folderName: string; + folderId?: any; + onFolderClick: (folder: { folderId: number | null; folderName: string }) => void; + isActive?: boolean; +} + +function FolderButton({ folderId, folderName, onFolderClick }: FolderButtonProps) { + const handleFolderClick = () => { + onFolderClick({ folderId, folderName }); + }; + + return ( + + + + ); +} + +export default FolderButton; diff --git a/components/domains/folder/FolderCardList.module.css b/components/domains/folder/FolderCardList.module.css new file mode 100644 index 000000000..3f4174296 --- /dev/null +++ b/components/domains/folder/FolderCardList.module.css @@ -0,0 +1,86 @@ +.card { + background-color: var(--white-color); + position: relative; + display: flex; + flex-wrap: wrap; + justify-content: flex-start; + width: 34rem; + height: 33.4rem; + box-shadow: 0px 5px 25px 0px rgba(0, 0, 0, 0.08); +} + +.imgContainer { + overflow: hidden; +} + +.cardImg { + object-fit: cover; +} + +.cardImg:hover { + transform: scale(1.3); + transition: all 1s; +} + +.starImg { + position: absolute; + right: 1.5rem; + top: 1.5rem; +} + +.cardInfo { + display: flex; + flex-wrap: wrap; + gap: 1rem; + width: 100%; + padding: 1.5rem 2rem; +} + +.cardInfoTop { + display: flex; + justify-content: space-between; + width: 100%; +} + +.timeAgo { + color: #666; + font-size: 13px; + font-weight: 400; +} + +.linksDescription { + overflow: hidden; + color: #000; + text-overflow: ellipsis; + font-size: 1.6rem; + font-weight: 400; + line-height: 24px; + height: 4.9rem; + width: 100%; +} + +.createdAt { + color: #333; + white-space: nowrap; + font-size: 1.4rem; + font-weight: 400; +} + +.cards { + display: flex; + width: 100%; + justify-content: center; + flex-wrap: wrap; + column-gap: 2rem; + row-gap: 2.5rem; +} + +.noLink { + color: #000; + font-weight: 400; + font-size: 1.6rem; + text-align: center; + width: 100%; + padding-top: 4.1rem; + padding-bottom: 3.5rem; +} diff --git a/components/domains/folder/FolderCardList.tsx b/components/domains/folder/FolderCardList.tsx new file mode 100644 index 000000000..e424c6ecc --- /dev/null +++ b/components/domains/folder/FolderCardList.tsx @@ -0,0 +1,87 @@ +import { useEffect, useState } from "react"; +import { getLinks } from "@/lib/api"; +import { LinkData } from "@/lib/utils/type"; +import Kebab from "./Kebab"; +import formatTimeAgo from "@/lib/utils/formatTimeAgo"; +import formatDate from "@/lib/utils/formatDate"; +import Link from "next/link"; +import Image from "next/image"; +import styles from "./FolderCardList.module.css"; + +// interface StyledCardProps { +// href: string; +// } + +interface FolderCardProps { + card: LinkData; +} + +interface FolderCardListProps { + folderId: any; + link?: LinkData[]; +} + +function FolderCard({ card }: FolderCardProps) { + const timeAgo = formatTimeAgo(card.created_at); + const date = formatDate(card.created_at); + + return ( + +
+ {card.image_source ? ( + {card.title} + ) : ( + no Image + )} + bookmark icon +
+
+
+

{timeAgo}

+ +
+

{card.description}

+

{date}

+
+ + ); +} + +function FolderCardList({ folderId }: FolderCardListProps) { + const [links, setLinks] = useState([]); + + useEffect(() => { + const fetchLinks = async () => { + const apiEndpoint: any = folderId ? `${folderId}` : ``; + + const link = await getLinks(apiEndpoint); + setLinks(link.data); + }; + + fetchLinks(); + }, [folderId]); + + return ( + <> + {links && links.length > 0 ? ( +
+ {links.map((card) => { + return ; + })} +
+ ) : ( +
+
저장된 링크가 없습니다
+
+ )} + + ); +} + +export default FolderCardList; diff --git a/components/domains/folder/FolderLists.module.css b/components/domains/folder/FolderLists.module.css new file mode 100644 index 000000000..45638f738 --- /dev/null +++ b/components/domains/folder/FolderLists.module.css @@ -0,0 +1,31 @@ +.folders { + display: flex; + flex-direction: column; + gap: 2.4rem; +} + +.foldersTop { + display: flex; + justify-content: space-between; + align-items: center; +} + +.folderButtons { + display: flex; + justify-content: flex-start; + align-items: center; + gap: 1.2rem; + flex-wrap: wrap; +} + +.folderName { + font-size: 2.4rem; + font-weight: 600; +} + +.nameAndOption { + display: flex; + justify-content: space-between; + flex-wrap: wrap; + row-gap: 1.2rem; +} diff --git a/components/domains/folder/FolderLists.tsx b/components/domains/folder/FolderLists.tsx new file mode 100644 index 000000000..4b79b9406 --- /dev/null +++ b/components/domains/folder/FolderLists.tsx @@ -0,0 +1,97 @@ +import { useEffect, useState } from "react"; +import { getFolders } from "@/lib/api"; +import FolderButton from "./FolderButton"; +import FolderCardList from "./FolderCardList"; +import Option from "./Option"; +import { FolderData, LinkData } from "@/lib/utils/type"; +import SearchBar from "../../commons/SearchBar"; +import styles from "./FolderLists.module.css"; +import Image from "next/image"; + +interface SelectedFolder { + id: number | null; + name: string; + link?: LinkData[]; +} + +function FolderLists() { + const [folders, setFolders] = useState([]); + const [selected, setSelected] = useState({ + id: null, + name: "전체", + link: [], + }); + const [search, setSearch] = useState(""); + const [filteredLinks, setFilteredLinks] = useState([]); + + useEffect(() => { + const fetchFolders = async () => { + const folder = await getFolders(); + + setFolders(folder.data); + }; + + fetchFolders(); + }, []); + + const isFolderSelected = selected.id !== null && selected.name !== "전체"; + + const handleSearchChange = (searchValue: string) => { + setSearch(searchValue); + }; + + useEffect(() => { + console.log("useEffect is being called"); + console.log("Search:", search); + console.log("Selected Links:", selected.link); + + const lowerCasedValue = search.toLowerCase(); + if (search) { + const filtered = (selected.link ?? []).filter((link) => { + const shouldInclude = + (link.url && link.url.includes(lowerCasedValue)) || + (link.title && link.title.includes(lowerCasedValue)) || + (link.description && link.description.includes(lowerCasedValue)); + + return shouldInclude; + }); + setFilteredLinks(filtered); + } else { + // 검색어가 없을 경우, 선택한 폴더의 링크. selected.link 대신에 selected.link를 사용. + setFilteredLinks(selected.link ?? []); + } + }, [search, selected.link]); + + const handleFolderClick = ({ folderId, folderName }: { folderId: number | null; folderName: string }) => { + setSelected({ id: folderId, name: folderName, link: [] }); + }; + + return ( + <> + +
+
+
+ + {folders.map((folder) => ( + + ))} +
+ add icon +
+
+
{selected.name}
+ {isFolderSelected ?
+ +
+ + ); +} + +export default FolderLists; diff --git a/components/domains/folder/Kebab.module.css b/components/domains/folder/Kebab.module.css new file mode 100644 index 000000000..f6eea41bc --- /dev/null +++ b/components/domains/folder/Kebab.module.css @@ -0,0 +1,34 @@ +.kebab { + position: relative; + width: 2.1rem; + height: 1.7rem; + cursor: pointer; +} + +.popup { + padding: 0; + margin: 0; + width: 10rem; + list-style: none; + background-color: #fff; + position: absolute; + top: 1rem; + left: 1rem; + border: 1px solid #ebebeb; + border-radius: 5px 0 5px 5px; + z-index: 1; +} + +.kebobList { + padding: 12px 7px; + text-align: center; + font-size: 1.4rem; + font-weight: 400; + color: var(--gray-100-color); +} + +.kebobList:hover { + background-color: var(--gray-10-color); + color: var(--primary-color); + cursor: pointer; +} diff --git a/components/domains/folder/Kebab.tsx b/components/domains/folder/Kebab.tsx new file mode 100644 index 000000000..060d19c4a --- /dev/null +++ b/components/domains/folder/Kebab.tsx @@ -0,0 +1,52 @@ +import React, { useState, useCallback, useEffect } from "react"; +import styles from "./Kebab.module.css"; +import Image from "next/image"; + +function Kebab() { + const [isOpen, setIsOpen] = useState(false); + + const handleButton = useCallback((e: React.MouseEvent) => { + e.stopPropagation(); + setIsOpen((prev) => !prev); + }, []); + + useEffect(() => { + if (!isOpen) return; + + const handleClickOutside = () => setIsOpen(false); + window.addEventListener("click", handleClickOutside); + + return () => { + window.removeEventListener("click", handleClickOutside); + }; + }, [isOpen]); + + return ( +
{ + e.preventDefault(); + handleButton(e); + }} + > + kebab icon + {isOpen && ( +
    { + e.preventDefault(); + }} + > +
  • + 삭제하기 +
  • +
  • + 폴더에 추가 +
  • +
+ )} +
+ ); +} + +export default Kebab; diff --git a/components/domains/folder/Option.module.css b/components/domains/folder/Option.module.css new file mode 100644 index 000000000..02ee193df --- /dev/null +++ b/components/domains/folder/Option.module.css @@ -0,0 +1,17 @@ +.option { + display: flex; + gap: 1.2rem; +} + +.optionButton { + display: flex; + align-items: center; + gap: 0.4rem; +} + +.optionText { + display: flex; + font-size: 1.4rem; + color: var(--gray-60-color); + align-items: center; +} diff --git a/components/domains/folder/Option.tsx b/components/domains/folder/Option.tsx new file mode 100644 index 000000000..22e28a531 --- /dev/null +++ b/components/domains/folder/Option.tsx @@ -0,0 +1,56 @@ +import useModal from "@/lib/useModal"; +import ModalPotal from "@/lib/Portal"; +import styles from "./Option.module.css"; +import Image from "next/image"; + +interface OptionButtonProps { + onClick?: (folderId?: number) => void; + icon: string; + altText: string; + text: string; + folderId?: number; +} + +function OptionButton({ onClick, icon, altText, text, folderId }: OptionButtonProps) { + return ( + + ); +} + +function Option({ folderName, folderId }: { folderName: string; folderId: any }) { + const { Modal: ShareModal, openModal: openShareModal } = useModal(); + const { Modal: RenameModal, openModal: openRenameModal } = useModal(); + const { Modal: DeleteModal, openModal: openDeleteModal } = useModal(); + + return ( +
+ + + {ShareModal && } + + + + + {RenameModal && ( + + )} + + + + + {DeleteModal && } + +
+ ); +} + +export default Option; diff --git a/components/domains/folder/ShareButtons.module.css b/components/domains/folder/ShareButtons.module.css new file mode 100644 index 000000000..52325c35a --- /dev/null +++ b/components/domains/folder/ShareButtons.module.css @@ -0,0 +1,24 @@ +.modalShareSNS { + display: flex; + gap: 3.2rem; +} + +.shareSNS { + display: flex; + flex-direction: column; + gap: 1rem; + align-items: center; + text-decoration: none; + color: var(--gray-80-color); + font-size: 1.3rem; +} + +.shareLinkButton { + display: flex; + flex-direction: column; + gap: 1rem; + align-items: center; + text-decoration: none; + color: var(--gray-80-color); + font-size: 1.3rem; +} diff --git a/components/domains/folder/ShareButtons.tsx b/components/domains/folder/ShareButtons.tsx new file mode 100644 index 000000000..d723e9ccc --- /dev/null +++ b/components/domains/folder/ShareButtons.tsx @@ -0,0 +1,52 @@ +import { useState } from "react"; +import styles from "./ShareButtons.module.css"; +import Link from "next/link"; +import Image from "next/image"; + +interface ShareButtonsProps { + folderId?: number; +} + +function ShareButtons({ folderId }: ShareButtonsProps) { + const [copied, setCopied] = useState(false); + + const shareLink = `${window.location.origin}/shared?user=1&folder=${folderId}`; + + const copyToClipboard = () => { + navigator.clipboard.writeText(shareLink); + setCopied(true); + }; + + const resetCopiedStatus = () => { + setCopied(false); + }; + + return ( +
+ + kakao share + 카카오톡 + + + facebook share + 페이스북 + + +
+ ); +} + +export default ShareButtons; diff --git a/components/domains/shared/Profile.module.css b/components/domains/shared/Profile.module.css new file mode 100644 index 000000000..2c44b9748 --- /dev/null +++ b/components/domains/shared/Profile.module.css @@ -0,0 +1,19 @@ +.profile { + background-color: var(--gray-bg-color); + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding-top: 2rem; + padding-bottom: 6rem; + gap: 2rem; +} + +.profile img { + width: 6rem; + height: 6rem; +} + +.profileNickname { + font-size: 16px; +} diff --git a/components/domains/shared/Profile.tsx b/components/domains/shared/Profile.tsx new file mode 100644 index 000000000..e3ba92dc0 --- /dev/null +++ b/components/domains/shared/Profile.tsx @@ -0,0 +1,46 @@ +import Image from "next/image"; +import { getSharedFolders } from "@/lib/api"; +import { useEffect, useState } from "react"; +import styles from "./Profile.module.css"; + +interface Owner { + profileImageSource: string; + name: string; +} + +interface Folder { + owner: Owner; + name: string; +} + +interface ProfileInfoProps { + owner: Owner; + name: string; +} + +function ProfileInfo({ owner, name }: ProfileInfoProps) { + if (!owner) return

로그인 정보가 없습니다.

; + return ( +
+ 해당 프로필 이미지 +
{`@${owner.name}`}
+

{name}

+
+ ); +} + +function Profile() { + const [folder, setFolder] = useState(null); + + useEffect(() => { + const handleFolder = async () => { + const { folder } = await getSharedFolders(); + setFolder(folder); + }; + handleFolder(); + }, []); + + return folder && ; +} + +export default Profile; diff --git a/components/domains/shared/SharedCardList.module.css b/components/domains/shared/SharedCardList.module.css new file mode 100644 index 000000000..a614c2118 --- /dev/null +++ b/components/domains/shared/SharedCardList.module.css @@ -0,0 +1,66 @@ +.imgContainer { + overflow: hidden; +} + +.card { + background-color: var(--white-color); + position: relative; + display: flex; + flex-wrap: wrap; + justify-content: flex-start; + width: 34rem; + height: 33.4rem; + box-shadow: 0px 5px 25px 0px rgba(0, 0, 0, 0.08); +} + +.cardImg { + width: 34rem; + height: 20rem; + object-fit: cover; +} + +.cardImg:hover { + transform: scale(1.3); + transition: all 1s; +} + +.cardInfo { + display: flex; + flex-wrap: wrap; + gap: 1rem; + width: 100%; + padding: 1.5rem 2rem; +} + +.timeAgo { + color: #666; + font-size: 13px; + font-weight: 400; +} + +.linksDescription { + overflow: hidden; + color: #000; + text-overflow: ellipsis; + font-size: 1.6rem; + font-weight: 400; + line-height: 24px; + height: 4.9rem; + width: 100%; +} + +.createdAt { + color: #333; + white-space: nowrap; + font-size: 1.4rem; + font-weight: 400; +} + +.cards { + display: flex; + width: 100%; + justify-content: center; + flex-wrap: wrap; + column-gap: 2rem; + row-gap: 2.5rem; +} diff --git a/components/domains/shared/SharedCardList.tsx b/components/domains/shared/SharedCardList.tsx new file mode 100644 index 000000000..09b5d23ea --- /dev/null +++ b/components/domains/shared/SharedCardList.tsx @@ -0,0 +1,68 @@ +import Image from "next/image"; +import { getSharedFolders } from "@/lib/api"; +import { useEffect, useState } from "react"; +import styles from "./SharedCardList.module.css"; +import formatTimeAgo from "@/lib/utils/formatTimeAgo"; +import formatDate from "@/lib/utils/formatDate"; +import Link from "next/link"; + +// interface StyledCardProps { +// href: string; +// } + +interface Link { + id: number; + url: string; + imageSource: string; + title: string; + description: string; + createdAt: number; +} + +interface SharedCardProps { + card: Link; +} + +const noImage = "/noimage.svg"; + +function SharedCard({ card }: SharedCardProps) { + const timeAgo = formatTimeAgo(card.createdAt); + const date = formatDate(card.createdAt); + + return ( +
+ +
+ {card.title} +
+
+

{timeAgo}

+

{card.description}

+

{date}

+
+ +
+ ); +} + +function SharedCardList() { + const [folder, setFolder] = useState([]); + + useEffect(() => { + const handleFolder = async () => { + const { folder } = await getSharedFolders(); + setFolder(folder.links); + }; + handleFolder(); + }, []); + + return ( +
+ {folder.map((card) => { + return ; + })} +
+ ); +} + +export default SharedCardList; diff --git a/image.d.ts b/image.d.ts new file mode 100644 index 000000000..3c1c88322 --- /dev/null +++ b/image.d.ts @@ -0,0 +1,11 @@ +declare module "*.svg" { + import React = require("react"); + export const ReactComponent: React.FunctionComponent>; + const src: string; + export default src; +} + +declare module "*.png" { + const content: string; + export default content; +} diff --git a/lib/Portal.tsx b/lib/Portal.tsx new file mode 100644 index 000000000..cfd3eb3ae --- /dev/null +++ b/lib/Portal.tsx @@ -0,0 +1,20 @@ +// lib/Portal.js + +import { ReactNode } from "react"; +import ReactDOM from "react-dom"; + +interface ModalPortalProps { + children: ReactNode; +} + +function ModalPortal({ children }: ModalPortalProps) { + const el = typeof document !== "undefined" ? document.getElementById("modal-root") : null; + + if (el) { + return ReactDOM.createPortal(children, el); + } else { + return null; + } +} + +export default ModalPortal; diff --git a/lib/api.tsx b/lib/api.tsx new file mode 100644 index 000000000..89b637b76 --- /dev/null +++ b/lib/api.tsx @@ -0,0 +1,8 @@ +import fetcher from "@/lib/utils/fetcher"; + +const API_URL = "https://bootcamp-api.codeit.kr/api"; + +export const getProfiles = () => fetcher(`${API_URL}/users/1`); +export const getSharedFolders = () => fetcher(`${API_URL}/sample/folder`); +export const getFolders = () => fetcher(`${API_URL}/users/1/folders`); +export const getLinks = (folderId: number) => fetcher(`${API_URL}/users/1/links?folderId=${folderId}`); diff --git a/lib/useModal/index.tsx b/lib/useModal/index.tsx new file mode 100644 index 000000000..eebff0606 --- /dev/null +++ b/lib/useModal/index.tsx @@ -0,0 +1,59 @@ +import React, { useState, ReactNode } from "react"; +import Image from "next/image"; +import styles from "./useModal.module.css"; +import ShareButtons from "../../components/domains/folder/ShareButtons"; + +export interface ModalProps { + title?: string; + link?: string; + list?: ReactNode; + input?: string; + button?: string; + color?: "blue" | "red"; + shareSNS?: boolean; + folderId?: number; + placeholder?: string; +} + +const useModal = () => { + const [modalOpen, setModalOpen] = useState(false); + + const openModal = () => { + setModalOpen(true); + }; + const closeModal = () => { + setModalOpen(false); + }; + + const Modal = ({ title, link, list, input, button, color, shareSNS, folderId, placeholder }: ModalProps) => + modalOpen ? ( +
+
+
+
+ +

{title}

+

{link}

+
+ {list &&
{list}
} + {input && } + {button && ( + + )} + {shareSNS && } +
+
+
+ ) : null; + return { Modal, openModal, closeModal }; +}; + +export default useModal; diff --git a/lib/useModal/useModal.module.css b/lib/useModal/useModal.module.css new file mode 100644 index 000000000..790c21f5f --- /dev/null +++ b/lib/useModal/useModal.module.css @@ -0,0 +1,90 @@ +.modalBackground { + position: fixed; + background-color: rgba(0, 0, 0, 0.4); + left: 0; + top: 0; + width: 100%; + height: 100%; +} + +.modalBox { + background-color: var(--white-color); + position: fixed; + left: 50%; + top: 50%; + border-radius: 1.5rem; + border: 1px solid #dee2e6; + padding: 3.2rem 4rem; +} + +.modalContainer { + display: flex; + flex-direction: column; + align-items: center; + gap: 2.4rem; +} + +.modalTop { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + gap: 0.8rem; + width: 28rem; +} + +.modalExit { + position: absolute; + top: 1rem; + right: 1rem; + width: 2.4rem; + height: 2.4rem; +} + +.modalTitle { + font-size: 2rem; + font-weight: 700; +} + +.modalLink { + color: var(--gray-60-color); + font-size: 14px; + font-weight: 400; +} + +.modalList { + height: 17.2rem; + overflow: auto; +} + +.modalList::-webkit-scrollbar { + display: none; +} + +.modalInput { + width: 28rem; + padding: 1.8rem 1.5rem; + font-size: 1.6rem; + font-weight: 400; + border-radius: 8px; + border: 1px solid var(--gray-20-color); +} + +.modalInput:focus { + outline: 1px solid var(--primary-color); +} + +.modalButton { + color: var(--white-color); + border-radius: 0.8rem; + padding: 1.6rem 2rem; + width: 28rem; +} + +.modalButton.blue { + background: var(--gradient-purpleblue-skyblue); +} + +.modalButton.red { + background: red; +} diff --git a/lib/utils/fetcher.tsx b/lib/utils/fetcher.tsx new file mode 100644 index 000000000..2d78cb1e0 --- /dev/null +++ b/lib/utils/fetcher.tsx @@ -0,0 +1,9 @@ +const fetcher = async (url: string) => { + const response = await fetch(url); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + return response.json(); +}; + +export default fetcher; diff --git a/lib/utils/formatDate.ts b/lib/utils/formatDate.ts new file mode 100644 index 000000000..07c9da5ba --- /dev/null +++ b/lib/utils/formatDate.ts @@ -0,0 +1,6 @@ +function formatDate(value: number) { + const date = new Date(value); + return `${date.getFullYear()}. ${date.getMonth()}. ${date.getDate()}`; +} + +export default formatDate; diff --git a/lib/utils/formatTimeAgo.tsx b/lib/utils/formatTimeAgo.tsx new file mode 100644 index 000000000..fabb1c778 --- /dev/null +++ b/lib/utils/formatTimeAgo.tsx @@ -0,0 +1,35 @@ +function formatTimeAgo(value: number) { + const now = new Date(); + const createdDate = new Date(value); + const timeDiff = now.getTime() - createdDate.getTime(); + const seconds = Math.floor(timeDiff / 1000); + const minutes = Math.floor(seconds / 60); + const hours = Math.floor(minutes / 60); + const days = Math.floor(hours / 24); + const months = Math.floor(days / 31); + const years = Math.floor(months / 12); + + if (seconds < 120) { + return `1 minute ago`; + } else if (minutes <= 59) { + return `${minutes} minutes ago`; + } else if (hours === 1) { + return `1 hour ago`; + } else if (hours <= 23) { + return `${hours} hours ago`; + } else if (days === 1) { + return `1 days ago`; + } else if (days <= 30) { + return `${days} days ago`; + } else if (months === 1) { + return `1 months ago`; + } else if (months <= 11) { + return `${months} months ago`; + } else if (years === 1) { + return `1 year ago`; + } else if (years > 1) { + return `${years} years ago`; + } +} + +export default formatTimeAgo; diff --git a/lib/utils/type.tsx b/lib/utils/type.tsx new file mode 100644 index 000000000..83fbf131c --- /dev/null +++ b/lib/utils/type.tsx @@ -0,0 +1,20 @@ +export interface LinkData { + id: number; + created_at: number; + update_at: string; + url: string; + title: string; + description: string; + image_source: string; + folder_id: number; +} + +export interface FolderData { + id: number; + created_at: string; + name: string; + user_id: number; + link: { + count: number; + }; +} diff --git a/next.config.js b/next.config.js index a843cbee0..459efe15a 100644 --- a/next.config.js +++ b/next.config.js @@ -1,6 +1,21 @@ /** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, -} + compiler: { + styledComponents: true, + }, + images: { + domains: ["codeit-images.codeit.com"], + }, + webpack(config) { + config.module.rules.push({ + test: /\.svg$/, + issuer: /\.[jt]sx?$/, + use: ["@svgr/webpack"], + }); -module.exports = nextConfig + return config; + }, +}; + +module.exports = nextConfig; diff --git a/package-lock.json b/package-lock.json index baa2b6655..5d2780088 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,10 @@ "version": "0.1.0", "dependencies": { "next": "13.5.6", + "next-images": "^1.8.5", "react": "^18", - "react-dom": "^18" + "react-dom": "^18", + "react-router-dom": "^6.21.1" }, "devDependencies": { "@types/node": "^20", @@ -131,6 +133,64 @@ "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", "dev": true }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "peer": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "peer": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "peer": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", + "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "peer": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "peer": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/@next/env": { "version": "13.5.6", "resolved": "https://registry.npmjs.org/@next/env/-/env-13.5.6.tgz", @@ -315,6 +375,14 @@ "node": ">= 8" } }, + "node_modules/@remix-run/router": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.14.1.tgz", + "integrity": "sha512-Qg4DMQsfPNAs88rb2xkdk03N3bjK4jgX5fR24eHCTR9q6PrhZQZ4UJBPzCHJkIpTRN1UKxx2DzjZmnC+7Lj0Ow==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@rushstack/eslint-patch": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.6.0.tgz", @@ -329,6 +397,37 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/eslint": { + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.1.tgz", + "integrity": "sha512-18PLWRzhy9glDQp3+wOgfLYRWlhgX0azxgJ63rdpoUHyrC9z0f5CkFburjQx4uD7ZCruw85ZtMt6K+L+R8fLJQ==", + "peer": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", + "peer": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "peer": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -339,7 +438,6 @@ "version": "20.9.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.4.tgz", "integrity": "sha512-wmyg8HUhcn6ACjsn8oKYjkN/zUzQeNtMy44weTJSM6p4MMzEOuKbA3OjJ267uPCOW7Xex9dyrNTful8XTQYoDA==", - "dev": true, "dependencies": { "undici-types": "~5.26.4" } @@ -484,11 +582,168 @@ "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", "dev": true }, + "node_modules/@webassemblyjs/ast": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.6.tgz", + "integrity": "sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==", + "peer": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", + "peer": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", + "peer": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz", + "integrity": "sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==", + "peer": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "peer": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", + "peer": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz", + "integrity": "sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "peer": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "peer": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "peer": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz", + "integrity": "sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-opt": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6", + "@webassemblyjs/wast-printer": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz", + "integrity": "sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz", + "integrity": "sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-buffer": "1.11.6", + "@webassemblyjs/wasm-gen": "1.11.6", + "@webassemblyjs/wasm-parser": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz", + "integrity": "sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz", + "integrity": "sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==", + "peer": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "peer": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "peer": true + }, "node_modules/acorn": { "version": "8.11.2", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", - "dev": true, "bin": { "acorn": "bin/acorn" }, @@ -496,6 +751,15 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "peer": true, + "peerDependencies": { + "acorn": "^8" + } + }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -509,7 +773,6 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -521,6 +784,14 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -741,6 +1012,14 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "engines": { + "node": "*" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -763,6 +1042,44 @@ "node": ">=8" } }, + "node_modules/browserslist": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", + "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "peer": true, + "dependencies": { + "caniuse-lite": "^1.0.30001565", + "electron-to-chromium": "^1.4.601", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "peer": true + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -798,9 +1115,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001564", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001564.tgz", - "integrity": "sha512-DqAOf+rhof+6GVx1y+xzbFPeOumfQnhYzVnZD6LAXijR77yPtm9mfOcqOnT3mpnJiZVT+kwLAFnRlZcIz+c6bg==", + "version": "1.0.30001576", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001576.tgz", + "integrity": "sha512-ff5BdakGe2P3SQsMsiqmt1Lc8221NR1VzHj5jXN5vBny9A6fpze94HiVV/n7XRosOlsShJcvMv5mdnpjOGCEgg==", "funding": [ { "type": "opencollective", @@ -832,6 +1149,15 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "peer": true, + "engines": { + "node": ">=6.0" + } + }, "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", @@ -855,6 +1181,12 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "peer": true + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -974,17 +1306,30 @@ "node": ">=6.0.0" } }, + "node_modules/electron-to-chromium": { + "version": "1.4.625", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.625.tgz", + "integrity": "sha512-DENMhh3MFgaPDoXWrVIqSPInQoLImywfCwrSmVl3cf9QHzoZSiutHwGaB/Ql3VkqcQV30rzgdM+BjKqBAJxo5Q==", + "peer": true + }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "dev": true }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "engines": { + "node": ">= 4" + } + }, "node_modules/enhanced-resolve": { "version": "5.15.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz", "integrity": "sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==", - "dev": true, "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -1068,6 +1413,12 @@ "safe-array-concat": "^1.0.1" } }, + "node_modules/es-module-lexer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.1.tgz", + "integrity": "sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==", + "peer": true + }, "node_modules/es-set-tostringtag": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", @@ -1108,6 +1459,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "peer": true, + "engines": { + "node": ">=6" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -1504,7 +1864,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, "dependencies": { "estraverse": "^5.2.0" }, @@ -1516,7 +1875,6 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, "engines": { "node": ">=4.0" } @@ -1530,11 +1888,19 @@ "node": ">=0.10.0" } }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "peer": true, + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-glob": { "version": "3.3.2", @@ -1567,8 +1933,7 @@ "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, "node_modules/fast-levenshtein": { "version": "2.0.6", @@ -1597,6 +1962,25 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -1862,7 +2246,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } @@ -2333,6 +2716,35 @@ "set-function-name": "^2.0.1" } }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "peer": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -2356,11 +2768,16 @@ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "peer": true + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -2435,6 +2852,39 @@ "node": ">= 0.8.0" } }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "peer": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/loader-utils/node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -2479,6 +2929,12 @@ "node": ">=10" } }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "peer": true + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -2501,6 +2957,25 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -2551,6 +3026,12 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "peer": true + }, "node_modules/next": { "version": "13.5.6", "resolved": "https://registry.npmjs.org/next/-/next-13.5.6.tgz", @@ -2596,6 +3077,24 @@ } } }, + "node_modules/next-images": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/next-images/-/next-images-1.8.5.tgz", + "integrity": "sha512-YLBERp92v+Nu2EVxI9+wa32KRuxyxTC8ItbiHUWVPlatUoTl0yRqsNtP39c2vYv27VRvY4LlYcUGjNRBSMUIZA==", + "dependencies": { + "file-loader": "^6.2.0", + "url-loader": "^4.1.0" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "peer": true + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -2892,7 +3391,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, "engines": { "node": ">=6" } @@ -2917,6 +3415,15 @@ } ] }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "peer": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, "node_modules/react": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", @@ -2946,6 +3453,36 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, + "node_modules/react-router": { + "version": "6.21.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.21.1.tgz", + "integrity": "sha512-W0l13YlMTm1YrpVIOpjCADJqEUpz1vm+CMo47RuFX4Ftegwm6KOYsL5G3eiE52jnJpKvzm6uB/vTKTPKM8dmkA==", + "dependencies": { + "@remix-run/router": "1.14.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.21.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.21.1.tgz", + "integrity": "sha512-QCNrtjtDPwHDO+AO21MJd7yIcr41UetYt5jzaB9Y1UYaPTCnVuJq6S748g1dE11OQlCFIQg+RtAA1SEZIyiBeA==", + "dependencies": { + "@remix-run/router": "1.14.1", + "react-router": "6.21.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz", @@ -3090,6 +3627,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "peer": true + }, "node_modules/safe-regex-test": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", @@ -3112,6 +3669,23 @@ "loose-envify": "^1.1.0" } }, + "node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -3127,6 +3701,15 @@ "node": ">=10" } }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "peer": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, "node_modules/set-function-length": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", @@ -3200,6 +3783,15 @@ "node": ">=8" } }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", @@ -3208,6 +3800,16 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "peer": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -3364,11 +3966,62 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "dev": true, "engines": { "node": ">=6" } }, + "node_modules/terser": { + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.26.0.tgz", + "integrity": "sha512-dytTGoE2oHgbNV9nTzgBEPaqAWvcJNl66VZ0BkJqlvp71IjO8CxdBx/ykCNb47cLnCmCvRZ6ZR0tLkqvZCdVBQ==", + "peer": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.20", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.26.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -3536,18 +4189,72 @@ "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "peer": true, + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, "dependencies": { "punycode": "^2.1.0" } }, + "node_modules/url-loader": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/url-loader/-/url-loader-4.1.1.tgz", + "integrity": "sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==", + "dependencies": { + "loader-utils": "^2.0.0", + "mime-types": "^2.1.27", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "file-loader": "*", + "webpack": "^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "file-loader": { + "optional": true + } + } + }, "node_modules/watchpack": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", @@ -3560,6 +4267,84 @@ "node": ">=10.13.0" } }, + "node_modules/webpack": { + "version": "5.89.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz", + "integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==", + "peer": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^1.0.0", + "@webassemblyjs/ast": "^1.11.5", + "@webassemblyjs/wasm-edit": "^1.11.5", + "@webassemblyjs/wasm-parser": "^1.11.5", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.9.0", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.15.0", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.7", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "peer": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "peer": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/webpack/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "peer": true, + "engines": { + "node": ">=4.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 1ce24924f..041dee306 100644 --- a/package.json +++ b/package.json @@ -9,16 +9,18 @@ "lint": "next lint" }, "dependencies": { + "next": "13.5.6", + "next-images": "^1.8.5", "react": "^18", "react-dom": "^18", - "next": "13.5.6" + "react-router-dom": "^6.21.1" }, "devDependencies": { - "typescript": "^5", "@types/node": "^20", "@types/react": "^18", "@types/react-dom": "^18", "eslint": "^8", - "eslint-config-next": "13.5.6" + "eslint-config-next": "13.5.6", + "typescript": "^5" } } diff --git a/pages/_app.tsx b/pages/_app.tsx index 021681f4d..b34257b2d 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -1,6 +1,22 @@ -import '@/styles/globals.css' -import type { AppProps } from 'next/app' +import Gnb from "@/components/commons/Gnb"; +import "../styles/global.css"; +import type { AppProps } from "next/app"; +import Footer from "@/components/commons/Footer"; +import ModalPortal from "@/lib/Portal"; +import useModal from "@/lib/useModal"; export default function App({ Component, pageProps }: AppProps) { - return + const { Modal } = useModal(); + return ( + <> + + + ); } diff --git a/pages/_document.tsx b/pages/_document.tsx index 54e8bf3e2..1b74daf64 100644 --- a/pages/_document.tsx +++ b/pages/_document.tsx @@ -1,4 +1,4 @@ -import { Html, Head, Main, NextScript } from 'next/document' +import { Html, Head, Main, NextScript } from "next/document"; export default function Document() { return ( @@ -6,8 +6,9 @@ export default function Document() {
+
- ) + ); } diff --git a/pages/api/hello.ts b/pages/api/hello.ts deleted file mode 100644 index f8bcc7e5c..000000000 --- a/pages/api/hello.ts +++ /dev/null @@ -1,13 +0,0 @@ -// Next.js API route support: https://nextjs.org/docs/api-routes/introduction -import type { NextApiRequest, NextApiResponse } from 'next' - -type Data = { - name: string -} - -export default function handler( - req: NextApiRequest, - res: NextApiResponse -) { - res.status(200).json({ name: 'John Doe' }) -} diff --git a/pages/auth/signin.tsx b/pages/auth/signin.tsx new file mode 100644 index 000000000..ad2e027b4 --- /dev/null +++ b/pages/auth/signin.tsx @@ -0,0 +1,54 @@ +import React from "react"; +import styles from "@/styles/Sign.module.css"; +import Link from "next/link"; +import Image from "next/image"; +import { CtaLong } from "@/components/commons/Cta"; +import { useRouter } from "next/router"; +import AuthInput from "@/components/domains/auth/AuthInput"; + +export default function Signin() { + const router = useRouter(); + + const onLogin = () => { + router.push("/folder"); + }; + + return ( +
+
+
+ + logo + +
+
회원이 아니신가요?
+ 회원 가입하기 +
+
+ +
+ + + + + 로그인 + +
+
소셜 로그인
+ + +
+
+
+ ); +} diff --git a/pages/auth/signup.tsx b/pages/auth/signup.tsx new file mode 100644 index 000000000..8dd21dbc2 --- /dev/null +++ b/pages/auth/signup.tsx @@ -0,0 +1,71 @@ +import React from "react"; +import styles from "@/styles/Sign.module.css"; +import Link from "next/link"; +import Image from "next/image"; +import { CtaLong } from "@/components/commons/Cta"; +import { useRouter } from "next/router"; +import AuthInput from "@/components/domains/auth/AuthInput"; + +export default function Signin() { + const router = useRouter(); + + const onLogin = () => { + router.push("/auth/signin"); + }; + + return ( +
+
+
+ + logo + +
+
이미 회원이신가요?
+ 로그인 하기 +
+
+ +
+ true} + placeholder="이메일을 입력해 주세요." + errorMessage="이메일 형식이 아닙니다." + /> + + + + + 회원가입 + +
+
다른 방식으로 가입하기
+ + +
+
+
+ ); +} diff --git a/pages/folder/[id]/index.tsx b/pages/folder/[id]/index.tsx new file mode 100644 index 000000000..efa30b4ed --- /dev/null +++ b/pages/folder/[id]/index.tsx @@ -0,0 +1,65 @@ +import React, { useRef, useEffect, useState } from "react"; +import styles from "@/styles/Folder.module.css"; +import AddLink from "@/components/domains/folder/AddLink"; +import FolderLists from "@/components/domains/folder/FolderLists"; +import FolderAddButton from "@/components/domains/folder/FolderAddButton"; + +function FolderPage() { + const addLinkRef = useRef(null); + const footerRef = useRef(null); + const [isSticky, setIsSticky] = useState(true); + + useEffect(() => { + const addLinkCurrentRef = addLinkRef.current; + const footerCurrentRef = footerRef.current; + if (!addLinkCurrentRef || !footerCurrentRef) return; + + let isAddLinkIntersecting = false; + let isFooterIntersecting = false; + + const handler: IntersectionObserverCallback = (entries) => { + entries.forEach((entry) => { + if (entry.target === addLinkCurrentRef) { + isAddLinkIntersecting = entry.isIntersecting; + } else if (entry.target === footerCurrentRef) { + isFooterIntersecting = entry.isIntersecting; + } + }); + + const shouldBeIntersecting = + (isAddLinkIntersecting && isFooterIntersecting) || + isAddLinkIntersecting || + (!isAddLinkIntersecting && isFooterIntersecting); + setIsSticky(shouldBeIntersecting); + }; + + const options = { + threshold: 0.1, + }; + + const observer = new IntersectionObserver(handler, options); + + observer.observe(addLinkCurrentRef); + observer.observe(footerCurrentRef); + + return () => { + observer.unobserve(addLinkCurrentRef); + observer.unobserve(footerCurrentRef); + }; + }, []); + + return ( + <> + +
+
+ + +
+
+
+ + ); +} + +export default FolderPage; diff --git a/pages/folder/index.tsx b/pages/folder/index.tsx new file mode 100644 index 000000000..efa30b4ed --- /dev/null +++ b/pages/folder/index.tsx @@ -0,0 +1,65 @@ +import React, { useRef, useEffect, useState } from "react"; +import styles from "@/styles/Folder.module.css"; +import AddLink from "@/components/domains/folder/AddLink"; +import FolderLists from "@/components/domains/folder/FolderLists"; +import FolderAddButton from "@/components/domains/folder/FolderAddButton"; + +function FolderPage() { + const addLinkRef = useRef(null); + const footerRef = useRef(null); + const [isSticky, setIsSticky] = useState(true); + + useEffect(() => { + const addLinkCurrentRef = addLinkRef.current; + const footerCurrentRef = footerRef.current; + if (!addLinkCurrentRef || !footerCurrentRef) return; + + let isAddLinkIntersecting = false; + let isFooterIntersecting = false; + + const handler: IntersectionObserverCallback = (entries) => { + entries.forEach((entry) => { + if (entry.target === addLinkCurrentRef) { + isAddLinkIntersecting = entry.isIntersecting; + } else if (entry.target === footerCurrentRef) { + isFooterIntersecting = entry.isIntersecting; + } + }); + + const shouldBeIntersecting = + (isAddLinkIntersecting && isFooterIntersecting) || + isAddLinkIntersecting || + (!isAddLinkIntersecting && isFooterIntersecting); + setIsSticky(shouldBeIntersecting); + }; + + const options = { + threshold: 0.1, + }; + + const observer = new IntersectionObserver(handler, options); + + observer.observe(addLinkCurrentRef); + observer.observe(footerCurrentRef); + + return () => { + observer.unobserve(addLinkCurrentRef); + observer.unobserve(footerCurrentRef); + }; + }, []); + + return ( + <> + +
+
+ + +
+
+
+ + ); +} + +export default FolderPage; diff --git a/pages/index.tsx b/pages/index.tsx index 02c4dee04..83dc2698d 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -1,114 +1,90 @@ -import Head from 'next/head' -import Image from 'next/image' -import { Inter } from 'next/font/google' -import styles from '@/styles/Home.module.css' +import React from "react"; +import Image from "next/image"; +import styles from "@/styles/Home.module.css"; +import { CtaLong } from "@/components/commons/Cta"; +import { useRouter } from "next/router"; -const inter = Inter({ subsets: ['latin'] }) +export default function HomePage() { + const router = useRouter(); + + const onSignup = () => { + router.push("/auth/signup"); + }; -export default function Home() { return ( <> - - Create Next App - - - - -
-
-

- Get started by editing  - pages/index.tsx -

- -
- -
+
+
+

+ 세상의 모든 정보
+ 쉽게 저장하고 관리해 보세요 +

+ 링크 추가하기 Next.js Logo
+
-
- -

- Docs -> -

-

- Find in-depth information about Next.js features and API. -

-
+
+
+

+ 원하는 링크
+ 저장하세요 +

+

+ 나중에 읽고 싶은 글, 다시 보고 싶은 영상, +
+ 사고 싶은 옷, 기억하고 싶은 모든 것을 +
한 공간에 저장하세요. +

+ image 1 +
- -

- Learn -> -

-

- Learn about Next.js in an interactive course with quizzes! -

-
+
+ image 2 +

+ 링크를 폴더로 +
+ 관리하세요 +

+

+ 나만의 폴더를 무제한으로 만들고 +
+ 다양하게 활용할 수 있습니다. +

+
- -

- Templates -> -

-

- Discover and deploy boilerplate example Next.js projects. -

-
+
+

+ 저장한 링크를 +
+ 공유해 보세요. +

+

+ 여러 링크를 폴더에 담고 공유할 수 있습니다. +
+ 가족, 친구, 동료들에게 쉽고 빠르게 링크를 +
+ 공유해 보세요. +

+ image 3 +
- -

- Deploy -> -

-

- Instantly deploy your Next.js site to a shareable URL - with Vercel. -

-
+
+ image 4 +

+ 저장한 링크를 +
+ 검색해 보세요. +

+

중요한 정보들을 검색으로 쉽게 찾아보세요.

-
+ - ) + ); } diff --git a/pages/shared/index.tsx b/pages/shared/index.tsx new file mode 100644 index 000000000..408838366 --- /dev/null +++ b/pages/shared/index.tsx @@ -0,0 +1,21 @@ +import React from "react"; +import Profile from "../../components/domains/shared/Profile"; +// import SearchBar from "../../components/commons/SearchBar"; +import SharedCardList from "../../components/domains/shared/SharedCardList"; +import styles from "@/styles/Shared.module.css"; + +function SharedPage() { + return ( + <> +
+ +
+ {/* */} + +
+
+ + ); +} + +export default SharedPage; diff --git a/public/favicon.ico b/public/favicon.ico deleted file mode 100644 index 718d6fea4..000000000 Binary files a/public/favicon.ico and /dev/null differ diff --git a/public/images/auth/eye-off.svg b/public/images/auth/eye-off.svg new file mode 100644 index 000000000..bec50d66f --- /dev/null +++ b/public/images/auth/eye-off.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/auth/eye-on.svg b/public/images/auth/eye-on.svg new file mode 100644 index 000000000..61afee898 --- /dev/null +++ b/public/images/auth/eye-on.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/auth/google-background.svg b/public/images/auth/google-background.svg new file mode 100644 index 000000000..75c873ae7 --- /dev/null +++ b/public/images/auth/google-background.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/auth/google.png b/public/images/auth/google.png new file mode 100644 index 000000000..f047adcc5 Binary files /dev/null and b/public/images/auth/google.png differ diff --git a/public/images/auth/kakao-background.svg b/public/images/auth/kakao-background.svg new file mode 100644 index 000000000..f0da6389e --- /dev/null +++ b/public/images/auth/kakao-background.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/auth/kakao.svg b/public/images/auth/kakao.svg new file mode 100644 index 000000000..920fb6542 --- /dev/null +++ b/public/images/auth/kakao.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/public/images/auth/linkbrary-logo-sign.svg b/public/images/auth/linkbrary-logo-sign.svg new file mode 100644 index 000000000..d938ff2ad --- /dev/null +++ b/public/images/auth/linkbrary-logo-sign.svg @@ -0,0 +1,15 @@ + + + diff --git a/public/images/folder/FAB.png b/public/images/folder/FAB.png new file mode 100644 index 000000000..7fc89aba2 Binary files /dev/null and b/public/images/folder/FAB.png differ diff --git a/public/images/folder/Search.png b/public/images/folder/Search.png new file mode 100644 index 000000000..45b7ed78e Binary files /dev/null and b/public/images/folder/Search.png differ diff --git a/public/images/folder/_close.png b/public/images/folder/_close.png new file mode 100644 index 000000000..d44eb8e57 Binary files /dev/null and b/public/images/folder/_close.png differ diff --git a/public/images/folder/add-icon.svg b/public/images/folder/add-icon.svg new file mode 100644 index 000000000..0cfdde6ef --- /dev/null +++ b/public/images/folder/add-icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/folder/add-link.png b/public/images/folder/add-link.png new file mode 100644 index 000000000..462d79e02 Binary files /dev/null and b/public/images/folder/add-link.png differ diff --git a/public/images/folder/delete.png b/public/images/folder/delete.png new file mode 100644 index 000000000..eaf307871 Binary files /dev/null and b/public/images/folder/delete.png differ diff --git a/public/images/folder/kebab.png b/public/images/folder/kebab.png new file mode 100644 index 000000000..88cc9bec6 Binary files /dev/null and b/public/images/folder/kebab.png differ diff --git a/public/images/folder/noimage.svg b/public/images/folder/noimage.svg new file mode 100644 index 000000000..4085188d2 --- /dev/null +++ b/public/images/folder/noimage.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/public/images/folder/pen.png b/public/images/folder/pen.png new file mode 100644 index 000000000..9407367dd Binary files /dev/null and b/public/images/folder/pen.png differ diff --git a/public/images/folder/share.png b/public/images/folder/share.png new file mode 100644 index 000000000..c999ddb00 Binary files /dev/null and b/public/images/folder/share.png differ diff --git a/public/images/folder/star-default.png b/public/images/folder/star-default.png new file mode 100644 index 000000000..7f1ee079b Binary files /dev/null and b/public/images/folder/star-default.png differ diff --git a/public/images/footer/facebook.svg b/public/images/footer/facebook.svg new file mode 100644 index 000000000..af5130815 --- /dev/null +++ b/public/images/footer/facebook.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/footer/instagram.svg b/public/images/footer/instagram.svg new file mode 100644 index 000000000..acd5df20b --- /dev/null +++ b/public/images/footer/instagram.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/footer/twitter.svg b/public/images/footer/twitter.svg new file mode 100644 index 000000000..4586b559d --- /dev/null +++ b/public/images/footer/twitter.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/footer/youtube.svg b/public/images/footer/youtube.svg new file mode 100644 index 000000000..9e4a7b7b5 --- /dev/null +++ b/public/images/footer/youtube.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/public/images/home/download-icon.png b/public/images/home/download-icon.png new file mode 100644 index 000000000..0f622211a Binary files /dev/null and b/public/images/home/download-icon.png differ diff --git a/public/images/home/footer-facebook.svg b/public/images/home/footer-facebook.svg new file mode 100644 index 000000000..af5130815 --- /dev/null +++ b/public/images/home/footer-facebook.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/home/hero-image.png b/public/images/home/hero-image.png new file mode 100644 index 000000000..b5c751a93 Binary files /dev/null and b/public/images/home/hero-image.png differ diff --git a/public/images/home/image1.png b/public/images/home/image1.png new file mode 100644 index 000000000..7af9dc54d Binary files /dev/null and b/public/images/home/image1.png differ diff --git a/public/images/home/image2.png b/public/images/home/image2.png new file mode 100644 index 000000000..a19ac1465 Binary files /dev/null and b/public/images/home/image2.png differ diff --git a/public/images/home/image3.png b/public/images/home/image3.png new file mode 100644 index 000000000..9322bfac6 Binary files /dev/null and b/public/images/home/image3.png differ diff --git a/public/images/home/image4.png b/public/images/home/image4.png new file mode 100644 index 000000000..c1779da04 Binary files /dev/null and b/public/images/home/image4.png differ diff --git a/public/images/home/linkbrary-logo.png b/public/images/home/linkbrary-logo.png new file mode 100644 index 000000000..e26932d8d Binary files /dev/null and b/public/images/home/linkbrary-logo.png differ diff --git a/public/images/modal/Facebook-share.png b/public/images/modal/Facebook-share.png new file mode 100644 index 000000000..c107a50e2 Binary files /dev/null and b/public/images/modal/Facebook-share.png differ diff --git a/public/images/modal/Kakao-share.png b/public/images/modal/Kakao-share.png new file mode 100644 index 000000000..278add880 Binary files /dev/null and b/public/images/modal/Kakao-share.png differ diff --git a/public/images/modal/check.svg b/public/images/modal/check.svg new file mode 100644 index 000000000..2ab2949b7 --- /dev/null +++ b/public/images/modal/check.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/modal/exit-icon.png b/public/images/modal/exit-icon.png new file mode 100644 index 000000000..1ad15b223 Binary files /dev/null and b/public/images/modal/exit-icon.png differ diff --git a/public/images/modal/link-share.png b/public/images/modal/link-share.png new file mode 100644 index 000000000..9d7cf1997 Binary files /dev/null and b/public/images/modal/link-share.png differ diff --git a/public/next.svg b/public/next.svg deleted file mode 100644 index 5174b28c5..000000000 --- a/public/next.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/vercel.svg b/public/vercel.svg deleted file mode 100644 index d2f842227..000000000 --- a/public/vercel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/styles/Folder.module.css b/styles/Folder.module.css new file mode 100644 index 000000000..2ad7976c0 --- /dev/null +++ b/styles/Folder.module.css @@ -0,0 +1,13 @@ +.folder { + max-width: 106rem; + margin: auto; + display: flex; + flex-direction: column; + justify-content: center; + padding-top: 4rem; + padding-bottom: 4rem; + + @media (max-width: 1200px) { + padding: 4rem 3.2rem; + } +} diff --git a/styles/Home.module.css b/styles/Home.module.css index 6676d2c66..154479de7 100644 --- a/styles/Home.module.css +++ b/styles/Home.module.css @@ -1,229 +1,133 @@ -.main { +.section { + background-color: var(--white-color, #ffffff); + padding-top: 7rem; + padding-bottom: 12rem; +} + +.navbox { + background-color: var(--gray-bg-color, #edf7ff); + padding: 2rem 20rem; + position: sticky; + top: 0; + left: 0; + width: 100%; + z-index: 2; +} + +.nav { display: flex; - flex-direction: column; justify-content: space-between; align-items: center; - padding: 6rem; - min-height: 100vh; + max-width: 192rem; + margin: 0 auto; } -.description { - display: inherit; - justify-content: inherit; - align-items: inherit; - font-size: 0.85rem; - max-width: var(--max-width); - width: 100%; - z-index: 2; - font-family: var(--font-mono); +.button { + background-image: linear-gradient(90deg, #6d6afe, #6ae3fe); + font-size: 1.8rem; + text-align: center; + text-decoration: none; + color: var(--white-color, #ffffff); + font-weight: 600; + border-radius: 0.8rem; + border: none; + padding: 1.6rem 2rem; + cursor: pointer; } -.description a { +.login { + width: 12.8rem; +} + +.header { + padding-top: 7rem; + background-color: var(--gray-bg-color, #edf7ff); +} + +.hero { display: flex; - justify-content: center; + flex-direction: column; align-items: center; - gap: 0.5rem; + justify-content: center; + gap: 4rem; } -.description p { - position: relative; - margin: 0; - padding: 1rem; - background-color: rgba(var(--callout-rgb), 0.5); - border: 1px solid rgba(var(--callout-border-rgb), 0.3); - border-radius: var(--border-radius); +.title { + text-align: center; + font-size: 6.4rem; } -.code { - font-weight: 700; - font-family: var(--font-mono); +.backgroundClipText { + color: transparent; + -webkit-background-clip: text; + background-clip: text; } -.grid { - display: grid; - grid-template-columns: repeat(4, minmax(25%, auto)); - max-width: 100%; - width: var(--max-width); +.titleGradation1 { + background-image: linear-gradient(91deg, #6d6afe 17.28%, #ff9f9f 74.98%); +} +.titleGradation2 { + background-image: linear-gradient(96deg, #fe8a8a 1.72%, #a4ceff 74.97%); +} +.titleGradation3 { + background-image: linear-gradient(277deg, #6fbaff 59.22%, #ffd88b 93.66%); +} +.titleGradation4 { + background-image: linear-gradient(99deg, #6d7ccd 19.76%, rgba(82, 136, 133, 0.22) 52.69%); +} +.titleGradation5 { + background-image: linear-gradient(271deg, #fe578f -9.84%, #68e8f9 107.18%); } -.card { - padding: 1rem 1.2rem; - border-radius: var(--border-radius); - background: rgba(var(--card-rgb), 0); - border: 1px solid rgba(var(--card-border-rgb), 0); - transition: background 200ms, border 200ms; +.linkadd { + width: 35rem; } -.card span { - display: inline-block; - transition: transform 200ms; +.heroImage { + width: 120rem; + height: 59rem; } -.card h2 { - font-weight: 600; - margin-bottom: 0.7rem; +.subTitle { + font-size: 4.8rem; + text-align: left; + grid-area: title; } -.card p { - margin: 0; - opacity: 0.6; - font-size: 0.9rem; - line-height: 1.5; - max-width: 30ch; +.description { + font-size: 1.6rem; + color: #6b6b6b; + font-weight: 500; + line-height: 150%; + grid-area: description; } -.center { - display: flex; +.image { + grid-area: image; +} + +.content { + background-color: var(--white-color, #ffffff); + padding: 5rem 46.1rem; + display: grid; justify-content: center; - align-items: center; - position: relative; - padding: 4rem 0; -} - -.center::before { - background: var(--secondary-glow); - border-radius: 50%; - width: 480px; - height: 360px; - margin-left: -400px; -} - -.center::after { - background: var(--primary-glow); - width: 240px; - height: 180px; - z-index: -1; -} - -.center::before, -.center::after { - content: ''; - left: 50%; - position: absolute; - filter: blur(45px); - transform: translateZ(0); -} - -.logo { - position: relative; -} -/* Enable hover only on non-touch devices */ -@media (hover: hover) and (pointer: fine) { - .card:hover { - background: rgba(var(--card-rgb), 0.1); - border: 1px solid rgba(var(--card-border-rgb), 0.15); - } - - .card:hover span { - transform: translateX(4px); - } -} - -@media (prefers-reduced-motion) { - .card:hover span { - transform: none; - } -} - -/* Mobile */ -@media (max-width: 700px) { - .content { - padding: 4rem; - } - - .grid { - grid-template-columns: 1fr; - margin-bottom: 120px; - max-width: 320px; - text-align: center; - } - - .card { - padding: 1rem 2.5rem; - } - - .card h2 { - margin-bottom: 0.5rem; - } - - .center { - padding: 8rem 0 6rem; - } - - .center::before { - transform: none; - height: 300px; - } - - .description { - font-size: 0.8rem; - } - - .description a { - padding: 1rem; - } - - .description p, - .description div { - display: flex; - justify-content: center; - position: fixed; - width: 100%; - } - - .description p { - align-items: center; - inset: 0 0 auto; - padding: 2rem 1rem 1.4rem; - border-radius: 0; - border: none; - border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25); - background: linear-gradient( - to bottom, - rgba(var(--background-start-rgb), 1), - rgba(var(--callout-rgb), 0.5) - ); - background-clip: padding-box; - backdrop-filter: blur(24px); - } - - .description div { - align-items: flex-end; - pointer-events: none; - inset: auto 0 0; - padding: 2rem; - height: 200px; - background: linear-gradient( - to bottom, - transparent 0%, - rgb(var(--background-end-rgb)) 40% - ); - z-index: 1; - } -} - -/* Tablet and Smaller Desktop */ -@media (min-width: 701px) and (max-width: 1120px) { - .grid { - grid-template-columns: repeat(2, 50%); - } -} - -@media (prefers-color-scheme: dark) { - .vercelLogo { - filter: invert(1); - } - - .logo { - filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70); - } -} - -@keyframes rotate { - from { - transform: rotate(360deg); - } - to { - transform: rotate(0deg); - } + column-gap: 15.7rem; + row-gap: 1rem; + width: 100%; +} + +.content:nth-of-type(odd) { + grid-template: + ". image" + "title image" + "description image" + ". image" /291px 55rem; +} + +.content:nth-of-type(even) { + grid-template: + "image ." + "image title" + "image description" + "image ." /550px 29.1rem; } diff --git a/styles/Shared.module.css b/styles/Shared.module.css new file mode 100644 index 000000000..e5a20558c --- /dev/null +++ b/styles/Shared.module.css @@ -0,0 +1,13 @@ +.folder { + max-width: 106rem; + margin: auto; + display: flex; + flex-direction: column; + justify-content: center; + padding-top: 4rem; + padding-bottom: 4rem; + + @media (max-width: 1200px) { + padding: 0 3.2rem; + } +} diff --git a/styles/Sign.module.css b/styles/Sign.module.css new file mode 100644 index 000000000..ca9a840e6 --- /dev/null +++ b/styles/Sign.module.css @@ -0,0 +1,132 @@ +.body { + padding: 23.8rem; + display: flex; + justify-content: center; + background-color: var(--gray-bg-color); +} + +.main { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 3rem; + width: 40rem; +} + +.logo { + display: flex; + flex-direction: column; + align-items: center; + gap: 1.6rem; +} + +.sign { + display: flex; + align-items: center; + gap: 0.8rem; + font-size: 1.6rem; + font-weight: 400; + color: #373740; +} + +.sign a { + color: var(--primary-color); + text-decoration: underline; +} + +.emailContainer, +.passwordContainer, +.passwordCheck { + display: flex; + flex-direction: column; + gap: 1.2rem; +} + +.label { + font-size: 1.4rem; + font-weight: 400; +} + +.input { + padding: 1.8rem 1.5rem; + border-radius: 0.8rem; + border: 0.1rem solid var(--gray-20-color); + width: 100%; + font-size: 1.6rem; +} + +.input:focus { + outline: 0.1rem solid var(--primary-color); +} + +.error { + outline: 0.1rem solid var(--red-color); +} + +.emailErrorMessage, +.passwordErrorMessage, +.passwordCheckErrorMessage { + display: none; + color: var(--red-color); + font-size: 1.6rem; +} + +.password { + position: relative; +} + +.eyeButton { + position: absolute; + background-color: var(--white-color); + top: 50%; + right: 2rem; + transform: translateY(-50%); + width: 1.6rem; + height: 1.6rem; +} + +.form { + display: flex; + flex-direction: column; + gap: 2.4rem; + width: 100%; +} + +.signButton { + background-image: linear-gradient(90deg, #6d6afe, #6ae3fe); + font-size: 1.8rem; + text-align: center; + color: var(--white-color, #ffffff); + font-weight: 600; + border-radius: 0.8rem; + padding: 1.6rem 2rem; + width: 100%; +} + +.sns { + display: flex; + justify-content: flex-end; + align-items: center; + gap: 1.6rem; + width: 100%; + padding: 1.2rem 2.4rem; + border-radius: 0.8rem; + border: 0.1rem solid var(--gray-20-color); + background: var(--gray-10-color); +} + +.snsTitle { + flex: 1 1 auto; + font-size: 1.4rem; + color: #373740; +} + +.snsButton { + position: relative; +} + +.snsIcon { + position: absolute; + inset: 1rem; +} diff --git a/styles/global.css b/styles/global.css new file mode 100644 index 000000000..4b730752a --- /dev/null +++ b/styles/global.css @@ -0,0 +1,42 @@ +* { + box-sizing: border-box; + margin: 0; + padding: 0; + text-decoration: none; +} + +html { + font-family: Pretendard, Arial, sans-serif; + font-size: 62.5%; + background-color: var(--white-color); + + --primary-color: #6d6afe; + --red-color: #ff5b56; + --black-color: #111322; + --white-color: #ffffff; + --gray-100-color: #3e3e43; + --gray-80-color: #444; + --gray-60-color: #9fa6b2; + --gray-20-color: #ccd5e3; + --gray-10-color: #e7effb; + --gray-bg-color: #f0f6ff; + --gradient-purple-orange: linear-gradient(91deg, #6d6afe 17.28%, #ff9f9f 74.98%); + --gradient-orange-skyblue: linear-gradient(96deg, #fe8a8a 1.72%, #a4ceff 74.97%); + --gradient-skyblue-yellow: linear-gradient(277deg, #6fbaff 59.22%, #ffd88b 93.66%); + --gradient-purple-gray: linear-gradient(99deg, #6d7ccd 19.76%, rgba(82, 136, 133, 0.22) 52.69%); + --gradient-red-blue: linear-gradient(271deg, #fe578f -9.84%, #68e8f9 107.18%); + --gradient-purpleblue-skyblue: linear-gradient(91deg, #6d6afe 0.12%, #6ae3fe 101.84%); +} +br { + display: block; +} + +button { + border-style: none; + background-color: transparent; + cursor: pointer; +} + +input { + outline: none; +} diff --git a/styles/globals.css b/styles/globals.css deleted file mode 100644 index d4f491e15..000000000 --- a/styles/globals.css +++ /dev/null @@ -1,107 +0,0 @@ -:root { - --max-width: 1100px; - --border-radius: 12px; - --font-mono: ui-monospace, Menlo, Monaco, 'Cascadia Mono', 'Segoe UI Mono', - 'Roboto Mono', 'Oxygen Mono', 'Ubuntu Monospace', 'Source Code Pro', - 'Fira Mono', 'Droid Sans Mono', 'Courier New', monospace; - - --foreground-rgb: 0, 0, 0; - --background-start-rgb: 214, 219, 220; - --background-end-rgb: 255, 255, 255; - - --primary-glow: conic-gradient( - from 180deg at 50% 50%, - #16abff33 0deg, - #0885ff33 55deg, - #54d6ff33 120deg, - #0071ff33 160deg, - transparent 360deg - ); - --secondary-glow: radial-gradient( - rgba(255, 255, 255, 1), - rgba(255, 255, 255, 0) - ); - - --tile-start-rgb: 239, 245, 249; - --tile-end-rgb: 228, 232, 233; - --tile-border: conic-gradient( - #00000080, - #00000040, - #00000030, - #00000020, - #00000010, - #00000010, - #00000080 - ); - - --callout-rgb: 238, 240, 241; - --callout-border-rgb: 172, 175, 176; - --card-rgb: 180, 185, 188; - --card-border-rgb: 131, 134, 135; -} - -@media (prefers-color-scheme: dark) { - :root { - --foreground-rgb: 255, 255, 255; - --background-start-rgb: 0, 0, 0; - --background-end-rgb: 0, 0, 0; - - --primary-glow: radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0)); - --secondary-glow: linear-gradient( - to bottom right, - rgba(1, 65, 255, 0), - rgba(1, 65, 255, 0), - rgba(1, 65, 255, 0.3) - ); - - --tile-start-rgb: 2, 13, 46; - --tile-end-rgb: 2, 5, 19; - --tile-border: conic-gradient( - #ffffff80, - #ffffff40, - #ffffff30, - #ffffff20, - #ffffff10, - #ffffff10, - #ffffff80 - ); - - --callout-rgb: 20, 20, 20; - --callout-border-rgb: 108, 108, 108; - --card-rgb: 100, 100, 100; - --card-border-rgb: 200, 200, 200; - } -} - -* { - box-sizing: border-box; - padding: 0; - margin: 0; -} - -html, -body { - max-width: 100vw; - overflow-x: hidden; -} - -body { - color: rgb(var(--foreground-rgb)); - background: linear-gradient( - to bottom, - transparent, - rgb(var(--background-end-rgb)) - ) - rgb(var(--background-start-rgb)); -} - -a { - color: inherit; - text-decoration: none; -} - -@media (prefers-color-scheme: dark) { - html { - color-scheme: dark; - } -} diff --git a/tsconfig.json b/tsconfig.json index 670224f3e..bd4b555a1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,7 +15,8 @@ "incremental": true, "paths": { "@/*": ["./*"] - } + }, + "typeRoots": ["./node_modules/@types", "./@types"] }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], "exclude": ["node_modules"]