diff --git a/component/api/api.ts b/component/api/api.ts index cc8bc02b1..9022030f5 100644 --- a/component/api/api.ts +++ b/component/api/api.ts @@ -2,7 +2,7 @@ import axios from "axios"; const apiUrl = "https://bootcamp-api.codeit.kr/api"; -export default async function getApi(path: string) { +export async function getApi(path: string) { try { const res = await axios.get(`${apiUrl}${path}`); if (res.status === 200) { @@ -12,3 +12,44 @@ export default async function getApi(path: string) { throw new Error("Unknown error"); } } + +export async function postSignIn(email: string, password: string) { + try { + const res = await axios.post(`${apiUrl}/sign-in`, { + email, + password, + }); + if (res.status === 200) { + return res.data; + } + } catch (e) { + throw new Error("Wrong email, password"); + } +} + +export async function checkEmail(email: string) { + try { + const res = await axios.post(`${apiUrl}/check-email`, { + email, + }); + if (res.status === 200) { + return res.data; + } + } catch (e) { + throw new Error("Wrong email"); + } +} + +export async function postSignUp(email: string, password: string) { + try { + const res = await axios.post(`${apiUrl}/sign-up`, { + email, + password, + }); + if (res.status === 200) { + return res.data; + } + } catch (e) { + throw new Error("Wrong email, password"); + } +} diff --git a/component/card/card.tsx b/component/card/card.tsx index 03168978f..87fa8358e 100644 --- a/component/card/card.tsx +++ b/component/card/card.tsx @@ -5,9 +5,24 @@ import { useState } from "react"; import Image from "next/image"; import Link from "next/link"; import dateCalculator from "@/utils/dateCalculator"; -import { CardProps, CardsProps } from "../../types/type"; import filterItems from "./filterItems"; import { NoLink } from "@/pages/folder"; +import { CardItem } from "@/types/type"; + +interface CardProps { + item: CardItem; + setClose?: React.Dispatch>; + setTag?: React.Dispatch>; + close?: boolean; +} + +interface CardsProps { + items: CardItem[]; + setClose?: React.Dispatch>; + setTag?: React.Dispatch>; + close?: boolean; + search?: string; +} function Card({ item, setClose, setTag, close }: CardProps) { const [status, setStatus] = useState(true); diff --git a/component/common/header.tsx b/component/common/header.tsx index 131e44e9b..84dfeefe3 100644 --- a/component/common/header.tsx +++ b/component/common/header.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from "react"; import logo from "../../public/images/header/logo.png"; -import getApi from "../api/api"; +import { getApi } from "../api/api"; import styled from "styled-components"; import Image from "next/image"; import Link from "next/link"; diff --git a/component/common/input.tsx b/component/common/input.tsx index acf933f26..e57272da0 100644 --- a/component/common/input.tsx +++ b/component/common/input.tsx @@ -1,14 +1,28 @@ import styled from "styled-components"; -import eyeOnIcon from "../public/input/signin-eye-on.png"; -import eyeOffIcon from "../public/input/signin-eye-off.png"; +import eyeOnIcon from "../../public/images/input/signin-eye-on.png"; +import eyeOffIcon from "../../public/images/input/signin-eye-off.png"; import Image from "next/image"; import { useState } from "react"; -export default function Input({ passwd }: { passwd?: boolean }) { +interface InputPasswordProps { + signUp?: boolean; + passwordValue: string; + setPasswordValue: React.Dispatch>; + passwordError: string; + setPasswordError: React.Dispatch>; + postApi: () => Promise; +} + +export function InputPassword({ + signUp, + passwordValue, + setPasswordValue, + passwordError, + setPasswordError, + postApi, +}: InputPasswordProps) { const [eyeIcon, setEyeIcon] = useState(eyeOnIcon); const [passwdView, setPasswdView] = useState("password"); - const [error, setError] = useState(false); - const errorCase = "오류"; function handleIcon() { if (eyeIcon === eyeOnIcon) { @@ -19,42 +33,175 @@ export default function Input({ passwd }: { passwd?: boolean }) { setPasswdView("password"); } } - function valueError(event: React.FocusEvent) { - if (errorCase === event.target.value) { - setError(true); + function valueError() { + const regex = new RegExp("(?=.*[a-zA-Z])(?=.*[0-9])(?=.{8,})"); + if (passwordValue === "") { + setPasswordError("비밀번호를 입력해주세요"); + } else if ( + signUp && + (passwordValue.length < 8 || !regex.test(passwordValue)) + ) { + setPasswordError( + "비밀번호는 영문, 숫자 조합 8자 이상 입력해 주세요." + ); } else { - setError(false); + setPasswordError(""); + } + } + function enterCheck(event: React.KeyboardEvent) { + if (event.key === "Enter") { + postApi(); } } return ( - <> - - {passwd ? ( - <> - - - eyeIcon - - - ) : ( - - )} +
+ + setPasswordValue(event.target.value)} + onKeyDown={enterCheck} + /> + + eyeIcon + - - 내용을 다시 작성해주세요 + + {passwordError} - +
+ ); +} + +interface InputPasswordCheckProps { + passwordCheckValue: string; + setPasswordCheckValue: React.Dispatch>; + passwordCheckError: string; + setPasswordCheckError: React.Dispatch>; + passwordValue: string; + postApi: () => Promise; +} + +export function InputPasswordCheck({ + passwordCheckValue, + setPasswordCheckValue, + passwordCheckError, + setPasswordCheckError, + passwordValue, + postApi, +}: InputPasswordCheckProps) { + const [eyeIcon, setEyeIcon] = useState(eyeOnIcon); + const [passwdView, setPasswdView] = useState("password"); + + function handleIcon() { + if (eyeIcon === eyeOnIcon) { + setEyeIcon(eyeOffIcon); + setPasswdView("text"); + } else { + setEyeIcon(eyeOnIcon); + setPasswdView("password"); + } + } + function valueError() { + if (passwordCheckValue === "") { + setPasswordCheckError("비밀번호를 입력해주세요"); + } else if (passwordValue !== passwordCheckValue) { + console.log(passwordValue, passwordCheckValue); + setPasswordCheckError("비밀번호가 일치하지 않아요."); + } else { + setPasswordCheckError(""); + } + } + function enterCheck(event: React.KeyboardEvent) { + if (event.key === "Enter") { + postApi(); + } + } + + return ( +
+ + + setPasswordCheckValue(event.target.value) + } + onKeyDown={enterCheck} + /> + + eyeIcon + + + + {passwordCheckError} + +
+ ); +} + +interface InputEmailProps { + emailValue: string; + setEmailValue: React.Dispatch>; + emailError: string; + setEmailError: React.Dispatch>; + postApi: () => Promise; +} + +export function InputEmail({ + emailValue, + setEmailValue, + emailError, + setEmailError, + postApi, +}: InputEmailProps) { + const regex = new RegExp( + "([!#-'*+/-9=?A-Z^-~-]+(.[!#-'*+/-9=?A-Z^-~-]+)*|\"([]!#-[^-~ \t]|(\\[\t -~]))+\")@([!#-'*+/-9=?A-Z^-~-]+(.[!#-'*+/-9=?A-Z^-~-]+)*|[[\t -Z^-~]*])" + ); + + function valueCheck() { + if (emailValue === "") { + setEmailError("이메일을 입력해주세요."); + } else if (!regex.test(emailValue)) { + setEmailError("올바른 이메일 주소가 아닙니다."); + } else { + setEmailError(""); + } + } + function enterCheck(event: React.KeyboardEvent) { + if (event.key === "Enter") { + postApi(); + } + } + + return ( +
+ + setEmailValue(event.target.value)} + onKeyDown={enterCheck} + /> + + {emailError} +
); } -const StyledInputBox = styled.div<{ $error: boolean }>` - width: 350px; +const StyledInputBox = styled.div<{ $error: string }>` height: 60px; padding: 18px 15px; border-radius: 8px; border: 1px solid ${({ $error }) => - $error ? "var(--red);" : "var(--gray20);"} + $error !== "" ? "var(--red);" : "var(--gray20);"} background: var(--white); position: relative; display: flex; @@ -77,9 +224,9 @@ const StyledInputIcon = styled.div` cursor: pointer; `; -const StyledErrorMsg = styled.div<{ $error: boolean }>` +const StyledErrorMsg = styled.div<{ $error: string }>` color: var(--red); font-size: 14px; margin-top: 6px; - display: ${({ $error }) => ($error ? "" : "none;")}; + visibility: ${({ $error }) => ($error !== "" ? "" : "hidden;")}; `; diff --git a/component/common/searchbar.tsx b/component/common/searchbar.tsx index 0a18b804f..6b9ad0719 100644 --- a/component/common/searchbar.tsx +++ b/component/common/searchbar.tsx @@ -8,8 +8,8 @@ export default function SearchBar({ search, setSearch, }: { - search: string; - setSearch: React.Dispatch>; + search?: string; + setSearch?: React.Dispatch>; }) { return ( diff --git a/component/folder/modal.tsx b/component/folder/modal.tsx index 36d125437..a59b5c7c5 100644 --- a/component/folder/modal.tsx +++ b/component/folder/modal.tsx @@ -8,6 +8,7 @@ import { useState } from "react"; import CopyToClipboard from "react-copy-to-clipboard"; import { FacebookMessengerShareButton } from "react-share"; import Image from "next/image"; +import Script from "next/script"; function Add() { const [select, setSelect] = useState("0"); @@ -94,8 +95,18 @@ function DeleteFolder() { ); } +declare global { + interface Window { + Kakao: any; + } +} + function Share({ query }: { query: string }) { const currentUrl = window.location.href + "?" + query; + const kakaoInit = () => { + if (!window.Kakao.isInitialized()) + window.Kakao.init(process.env.NEXT_PUBLIC_KAKAO_API); + }; const shareKakao = async () => { await window.Kakao.Share.sendDefault({ @@ -121,6 +132,12 @@ function Share({ query }: { query: string }) { return ( <> +