diff --git a/.gitignore b/.gitignore index f2a8f77..22e6ddc 100644 --- a/.gitignore +++ b/.gitignore @@ -11,5 +11,4 @@ #!.yarn/cache .pnp.* -node_modules -.env \ No newline at end of file +node_modules \ No newline at end of file diff --git a/packages/design-system/src/style/GlobalStyle.tsx b/packages/design-system/src/style/GlobalStyle.tsx index 7f9776c..9c739e5 100644 --- a/packages/design-system/src/style/GlobalStyle.tsx +++ b/packages/design-system/src/style/GlobalStyle.tsx @@ -19,7 +19,7 @@ const style = css` font-family: 'Pretendard'; font-weight: 500; font-style: normal; - src: url('https://cdn.jsdelivr.net/gh/Project-Noonnu/noonfonts_2107@1.1/Pretendard-Medeum.woff') format('woff'); + src: url('https://cdn.jsdelivr.net/gh/Project-Noonnu/noonfonts_2107@1.1/Pretendard-Medium.woff') format('woff'); } @font-face { diff --git a/packages/user/.env b/packages/user/.env new file mode 100644 index 0000000..9301709 --- /dev/null +++ b/packages/user/.env @@ -0,0 +1 @@ +VITE_SERVER_BASE_URL='https://prod-server.xquare.app/merge' \ No newline at end of file diff --git a/packages/user/src/apis/axios.ts b/packages/user/src/apis/axios.ts index 923be9b..20e0b3b 100644 --- a/packages/user/src/apis/axios.ts +++ b/packages/user/src/apis/axios.ts @@ -30,6 +30,9 @@ instance.interceptors.response.use( } = err; if (status === 403) { //const token = Cookie.get('refreshToken'); + Cookie.remove('accessToken'); + Cookie.remove('refreshToken'); + window.location.href = '/login'; } else { toast.error('오류가 발생헀습니다'); return Promise.reject(err); diff --git a/packages/user/src/apis/project.ts b/packages/user/src/apis/project.ts new file mode 100644 index 0000000..45f4a23 --- /dev/null +++ b/packages/user/src/apis/project.ts @@ -0,0 +1,16 @@ +import { instance } from './axios'; +import { projectType } from '../types/projectType'; + +type createProjectType = { + project: projectType; + logo: File; + projectImage: File[]; +}; + +export const getMyProject = async (email: string) => { + return await instance.get(`/project/user?email=${email}`); +}; + +export const createProject = async (data: createProjectType) => { + return await instance.post('/project', data); +}; diff --git a/packages/user/src/components/Header/Menu.tsx b/packages/user/src/components/Header/Menu.tsx index 17468ac..6be88af 100644 --- a/packages/user/src/components/Header/Menu.tsx +++ b/packages/user/src/components/Header/Menu.tsx @@ -1,6 +1,6 @@ import styled from '@emotion/styled'; import { Button } from '@merge/design-system'; -import { useNavigate } from 'react-router-dom'; +import { Link, useNavigate } from 'react-router-dom'; type menuProps = { isLogin: boolean; @@ -13,18 +13,16 @@ export const Menu = ({ isLogin }: menuProps) => { link('/signin'); }; + const onRegister = () => { + link('/register'); + }; + return ( {isLogin ? ( <> - - @@ -41,11 +39,13 @@ const Wrapper = styled.div` display: flex; align-items: center; gap: 24px; + height: 32px; `; -const Profile = styled.div` +const Profile = styled(Link)` width: 32px; height: 32px; border-radius: 50%; background-color: gray; + cursor: pointer; `; diff --git a/packages/user/src/components/Header/index.tsx b/packages/user/src/components/Header/index.tsx index 55571ee..42a0483 100644 --- a/packages/user/src/components/Header/index.tsx +++ b/packages/user/src/components/Header/index.tsx @@ -3,13 +3,16 @@ import { theme } from '@merge/design-system'; import { Logo } from './Logo'; import { Menu } from './Menu'; import { Outlet } from 'react-router-dom'; +import { Cookie } from '../../utils/cookie'; export const Header = () => { + const token = Cookie.get('accessToken'); + return ( <> - + diff --git a/packages/user/src/components/Registration/Form.tsx b/packages/user/src/components/Registration/Form.tsx index 43b4e4b..1f9d9bd 100644 --- a/packages/user/src/components/Registration/Form.tsx +++ b/packages/user/src/components/Registration/Form.tsx @@ -5,21 +5,27 @@ import RegisterLogoImg from '../../assets/registerLogo.svg'; import CheckBoxTrueImg from '../../assets/checkBoxTrue.svg'; import CheckBoxFalseImg from '../../assets/checkBoxFalse.svg'; import ScreenshotLabelImg from '../../assets/screenshotLabel.svg'; +import { projectType } from 'src/types/projectType'; -export const RegisterFormFirst = () => { - const [logo, setLogo] = useState(null); +type RegisterFormFirstPropsType = { + logo: File | null; + func: (event: ChangeEvent) => void; +}; - const handleLogoChange = (e: ChangeEvent) => { - const file = e.target.files && e.target.files.length > 0 ? e.target.files[0] : null; - if (!file) return; +type RegisterFormSecondPropsType = { + value: projectType; + projectImage: File[] | null; + func1: (e: ChangeEvent) => void; + func2: (e: ChangeEvent) => void; + func3: (event: ChangeEvent) => void; +}; - const reader = new FileReader(); - reader.onloadend = () => { - setLogo(reader.result as string); - }; - reader.readAsDataURL(file); - }; +type RegisterFormThirdPropsType = { + value: projectType; + func: (e: ChangeEvent) => void; +}; +export const RegisterFormFirst = ({ logo, func }: RegisterFormFirstPropsType) => { return ( @@ -30,79 +36,147 @@ export const RegisterFormFirst = () => { 프로젝트 로고 등록하기 - ); }; -export const RegisterFormSecond = () => { +export const RegisterFormSecond = ({ value, projectImage, func1, func2, func3 }: RegisterFormSecondPropsType) => { return ( 가 있는 필드는 필수 입력란 입니다. - - - + + + - 프로젝트 로고 등록하기 + 프로젝트 개요 혹은 프로젝트 설명을 작성하기 100/500 - + 프로젝트 스크린샷 또는 사진 등록하기 - + + {projectImage && + projectImage.map((element, index) => { + return ; + })} ); }; -export const RegisterFormThird = () => { +export const RegisterFormThird = ({ value, func }: RegisterFormThirdPropsType) => { return ( - - - - + + + + ); }; -export const RegisterFormForth = () => { - const [check, setCheck] = useState(false); +// export const RegisterFormForth = ({ value }: RegisterFormPropsType) => { +// const [check, setCheck] = useState(false); - return ( - - - ouath 사용을 원하는 경우에만 작성해주세요. - - - oauth 사용 여부 - - setCheck(!check)} check={check}> - oauth 사용 여부 - - - - - ); -}; +// return ( +// +// +// ouath 사용을 원하는 경우에만 작성해주세요. +// +// +// oauth 사용 여부 +// +// setCheck(!check)} check={check}> +// oauth 사용 여부 +// +// +// +// +// ); +// }; const Wrapper = styled.div<{ height: number }>` width: 832px; diff --git a/packages/user/src/components/Registration/Progress.tsx b/packages/user/src/components/Registration/Progress.tsx index b1f7424..dc68fdc 100644 --- a/packages/user/src/components/Registration/Progress.tsx +++ b/packages/user/src/components/Registration/Progress.tsx @@ -7,8 +7,8 @@ import LineImg from '../../assets/line.svg'; const nullFunc = () => { return null; }; - -const projectLevels: string[] = ['로고 등록', '상세 설명', '링크 입력', 'oauth 사용']; +// 'oauth 사용' +const projectLevels: string[] = ['로고 등록', '상세 설명', '링크 입력']; const containerLevels: string[] = ['상세 설명', '타입, 사용 여부 선택', '환경 변수 입력']; @@ -39,10 +39,12 @@ export const Progress = ({ progress, kind, onClick, + func, }: { progress: number; kind: pageKindType; onClick: (index: number) => void; + func: () => void; }) => { const levels = kind === 'deploy' ? containerLevels : projectLevels; @@ -72,14 +74,7 @@ export const Progress = ({ - diff --git a/packages/user/src/components/Registration/SubHeader.tsx b/packages/user/src/components/Registration/SubHeader.tsx index 32d37fd..b54be36 100644 --- a/packages/user/src/components/Registration/SubHeader.tsx +++ b/packages/user/src/components/Registration/SubHeader.tsx @@ -5,7 +5,7 @@ export const SubHeader = () => { return ( 프로젝트 등록하기 - + {/* */} ); }; diff --git a/packages/user/src/pages/HideProjects.tsx b/packages/user/src/pages/HideProjects.tsx new file mode 100644 index 0000000..cac5cf9 --- /dev/null +++ b/packages/user/src/pages/HideProjects.tsx @@ -0,0 +1,3 @@ +export const HideProjects = () => { + return
; +}; diff --git a/packages/user/src/pages/MyPage.tsx b/packages/user/src/pages/MyPage.tsx new file mode 100644 index 0000000..dc14cac --- /dev/null +++ b/packages/user/src/pages/MyPage.tsx @@ -0,0 +1,171 @@ +import { useEffect, useState } from 'react'; +import styled from '@emotion/styled'; +import { Button, theme } from '@merge/design-system'; +import { getMyProject } from '../apis/project'; +import { Link } from 'react-router-dom'; + +type projectType = { + project_name_en: string; + team_name_en: string; + id: string; + logo: string; +}; + +const dummyProjects: projectType[] = []; + +export const MyPage = () => { + const [projects, setProjects] = useState(); + + useEffect(() => { + getMyProject('dutexion@dsm.hs.kr') + .then((res) => { + setProjects(res.data); + console.log(res.data); + }) + .catch((err) => console.log(err)); + }, []); + + return ( + +
+
+ 1학년 1반 1번 + 강해민 +
+ https://github.com/nimeahgnak +
+ + {projects && + projects.map((element, index) => { + return ( + + +
+ {/* {element.admin && 관리자} */} +
{element.project_name_en}
+
{element.team_name_en}
+ {/*
{element.}
*/} +
+
+ ); + })} +
+ + + +
+ ); +}; + +const Wrapper = styled.div` + height: calc(100vh - 52px); + overflow-y: auto; + display: flex; + flex-direction: column; + align-items: center; +`; + +const Header = styled.div` + width: 1128px; + height: 70px; + padding: 0 40px; + margin-top: 66px; + display: flex; + justify-content: space-between; + background-color: ${theme.color.gray50}; + border-radius: 8px; + ${theme.font.subTitle2}; + align-items: center; + & > div { + color: ${theme.color.primary800}; + span { + color: ${theme.color.gray800}; + margin-left: 20px; + } + } + & > a { + text-decoration: none; + color: ${theme.color.link800}; + } +`; + +const Container = styled.div` + width: 1128px; + display: flex; + justify-content: start; + gap: 52px; + flex-wrap: wrap; + margin-top: 56px; + margin-bottom: 60px; +`; + +const Project = styled.div` + width: 184px; + height: 264px; + background-color: white; + border: 1px solid ${theme.color.gray100}; + padding: 12px; + border-radius: 8px; + img { + width: 160px; + height: 160px; + border-radius: 4px; + } + > div { + position: relative; + & > .first { + ${theme.font.subTitle2}; + color: ${theme.color.gray900}; + } + & > .second { + ${theme.font.subTitle3}; + color: ${theme.color.gray700}; + margin: 4px 0 8px 0; + } + & > .third { + width: 58px; + height: 22px; + font-size: 8px; + font-weight: 500; + letter-spacing: 0.008px; + color: ${theme.color.gray800}; + background-color: ${theme.color.gray100}; + border-radius: 4px; + display: flex; + justify-content: center; + align-items: center; + } + } +`; + +const Badge = styled.div` + position: absolute; + right: 0px; + top: 0; + width: 28px; + height: 28px; + background-color: ${theme.color.primaryA200}; + font-size: 8px; + font-weight: 500; + letter-spacing: 0.008px; + border-radius: 50%; + color: ${theme.color.white}; + display: flex; + justify-content: center; + align-items: center; +`; + +const ButtonContainer = styled(Link)` + position: absolute; + right: 36px; + bottom: 72px; + text-decoration: none; +`; diff --git a/packages/user/src/pages/Registration.tsx b/packages/user/src/pages/Registration.tsx index 18cf616..a272864 100644 --- a/packages/user/src/pages/Registration.tsx +++ b/packages/user/src/pages/Registration.tsx @@ -1,20 +1,17 @@ -import { useState, ReactElement } from 'react'; +import { ChangeEvent, useState, ReactElement, useEffect } from 'react'; import styled from '@emotion/styled'; +import { toast } from 'react-toastify'; import { SubHeader } from '../components/Registration/SubHeader'; import { Progress } from '../components/Registration/Progress'; import { RegisterFormFirst, RegisterFormSecond, RegisterFormThird, - RegisterFormForth, + // RegisterFormForth, } from '../components/Registration/Form'; - -const registerFormArray: ReactElement[] = [ - , - , - , - , -]; +import { projectType } from '../types/projectType'; +import { dataWhiteSpace } from '../func/dataWhiteSpace'; +import { createProject } from '../apis/project'; type pageKindType = 'register' | 'deploy'; @@ -22,11 +19,123 @@ export const Registration = () => { const [progress, setProgress] = useState(0); const [nowProgress, setNowProgress] = useState(0); + const [logo, setLogo] = useState(null); + const [projectData, setProjectData] = useState({ + project_name_ko: '', + project_name_en: '', + team_name_en: '', + description: '', + github_url: '', + web_url: '', + play_store_url: '', + app_store_url: '', + }); + const [projectImage, setProjectImage] = useState(null); + + const handleLogoChange = (event: ChangeEvent) => { + const files = event.target.files; + if (files) { + const file = files[0]; + const extension = file.name.split('.').pop()?.toLowerCase(); + + const allowedExtensions = ['png', 'jpg', 'jpeg', 'gif', 'pdf', 'tiff', 'psd', 'bmp']; + + if (extension && allowedExtensions.includes(extension)) { + setLogo(file); + } else { + toast.error('허용되지 않은 파일 형식입니다.'); + } + } + }; + + const handleProjectImageChange = (event: ChangeEvent) => { + const files = event.target.files; + console.log(projectImage); + + if (files) { + const validatedFiles = Array.from(files).filter((file) => { + const extension = file.name.split('.').pop()?.toLowerCase(); + const allowedExtensions = ['png', 'jpg', 'jpeg', 'gif', 'pdf', 'tiff', 'psd', 'bmp']; + return extension && allowedExtensions.includes(extension); + }); + + if (validatedFiles.length !== files.length) { + toast.error('하나 이상의 파일이 허용되지 않은 형식입니다.'); + } else { + setProjectImage(validatedFiles); + } + } + }; + + const onChange = (e: React.ChangeEvent) => { + const { name, value } = e.target; + setProjectData({ + ...projectData, + [name]: value, + }); + }; + + const onAreaChange = (e: React.ChangeEvent) => { + const { name, value } = e.target; + setProjectData({ + ...projectData, + [name]: value, + }); + }; + + useEffect(() => { + const { + project_name_ko, + project_name_en, + team_name_en, + description, + github_url, + web_url, + play_store_url, + app_store_url, + } = projectData; + + if (logo) { + setProgress(1); + if (dataWhiteSpace({ project_name_ko, project_name_en, team_name_en, description })) { + setProgress(2); + if (dataWhiteSpace({ github_url, web_url, play_store_url, app_store_url })) { + setProgress(3); + } + } + } + }, [logo, projectData, projectImage]); + + const registerFormArray: ReactElement[] = [ + , + , + , + // , + ]; + + const onSubmit = () => { + if (logo && projectImage) { + createProject({ project: projectData, logo: logo, projectImage: projectImage }) + .then((res) => { + console.log(res); + }) + .catch((err) => { + console.log(err); + }); + } + }; + return ( - + {registerFormArray[nowProgress]} diff --git a/packages/user/src/pages/SignIn.tsx b/packages/user/src/pages/SignIn.tsx index 5660f3b..3bfabec 100644 --- a/packages/user/src/pages/SignIn.tsx +++ b/packages/user/src/pages/SignIn.tsx @@ -1,4 +1,3 @@ -import { useNavigate } from 'react-router-dom'; import styled from '@emotion/styled'; import SignImg from '../assets/sign.svg'; import { theme, Input, Button } from '@merge/design-system'; @@ -10,7 +9,6 @@ import { signinType } from 'src/types/signType'; export const SignIn = () => { const [data, setData] = useState({ account_id: '', password: '' }); - const link = useNavigate(); const { account_id, password } = data; @@ -27,7 +25,7 @@ export const SignIn = () => { .then((res) => { Cookie.set('accessToken', res.data.access_token); Cookie.set('refreshToken', res.data.refresh_token); - link('/'); + window.location.href = '/'; }) .catch((err) => { console.error(err); diff --git a/packages/user/src/router.tsx b/packages/user/src/router.tsx index e0f6496..a3509e9 100644 --- a/packages/user/src/router.tsx +++ b/packages/user/src/router.tsx @@ -6,6 +6,8 @@ import { MyProject } from './pages/Projects/MyProject'; import { SignIn } from './pages/SignIn'; import { SignUp } from './pages/SignUp'; import { Registration } from './pages/Registration'; +import { MyPage } from './pages/MyPage'; +import { HideProjects } from './pages/HideProjects'; export const Router = createBrowserRouter([ { @@ -41,6 +43,13 @@ export const Router = createBrowserRouter([ path: 'register', element: , }, + { + path: 'my', + children: [ + { index: true, element: }, + { path: 'hide', element: }, + ], + }, ], errorElement: <>error, }, diff --git a/packages/user/src/types/projectType.ts b/packages/user/src/types/projectType.ts new file mode 100644 index 0000000..7756117 --- /dev/null +++ b/packages/user/src/types/projectType.ts @@ -0,0 +1,10 @@ +export type projectType = { + project_name_ko: string; + project_name_en: string; + team_name_en: string; + description: string; + github_url: string; + web_url: string; + play_store_url: string; + app_store_url: string; +};