diff --git a/public/icon/apple_icon.svg b/public/icon/apple_icon.svg new file mode 100644 index 0000000..8e71303 --- /dev/null +++ b/public/icon/apple_icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/icon/apple_icon_selected.svg b/public/icon/apple_icon_selected.svg new file mode 100644 index 0000000..a319498 --- /dev/null +++ b/public/icon/apple_icon_selected.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/public/icon/close_icon.svg b/public/icon/close_icon.svg new file mode 100644 index 0000000..76871e6 --- /dev/null +++ b/public/icon/close_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icon/leftarrow_icon.svg b/public/icon/leftarrow_icon.svg new file mode 100644 index 0000000..a6e2d1f --- /dev/null +++ b/public/icon/leftarrow_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icon/pen_icon.svg b/public/icon/pen_icon.svg new file mode 100644 index 0000000..3dbe3a8 --- /dev/null +++ b/public/icon/pen_icon.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icon/pen_icon_selected.svg b/public/icon/pen_icon_selected.svg new file mode 100644 index 0000000..ce6c1a2 --- /dev/null +++ b/public/icon/pen_icon_selected.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/App.tsx b/src/App.tsx index a65b854..bdf2ea6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -31,7 +31,7 @@ function App() { } /> } /> } /> - } /> + } /> } /> } /> diff --git a/src/components/common/Modal/ModalBackdrop.tsx b/src/components/common/Modal/ModalBackdrop.tsx index 06e1c85..756ec8c 100644 --- a/src/components/common/Modal/ModalBackdrop.tsx +++ b/src/components/common/Modal/ModalBackdrop.tsx @@ -7,4 +7,5 @@ export const ModalBackdrop = styled.div` right: 0; top: 0; bottom: 0; + z-index: 1000; `; diff --git a/src/components/home/PlanBox.tsx b/src/components/home/PlanBox.tsx index db75b6b..83a3c6a 100644 --- a/src/components/home/PlanBox.tsx +++ b/src/components/home/PlanBox.tsx @@ -1,7 +1,9 @@ import styled, { css } from 'styled-components'; import { ProgressCircle } from './ProgressCircle'; +import { useNavigate } from 'react-router-dom'; interface PlanBoxProps { + planId: number; mainplan: string; subplans: string[]; startdate: string; @@ -14,10 +16,22 @@ export const PlanBox = ({ ...props }: PlanBoxProps) => { const mainColors = ['#9D9BFF', '#FFC066', '#FF7EBC', '#4ED99C']; const darkColors = ['#6D6AFF', '#FF9600', '#FF3898', '#00AD7C']; + const navigate = useNavigate(); + + const handleClickMainText = (planId: number) => { + navigate(`/plan/${planId}`); + }; + return ( - {props.mainplan} + { + handleClickMainText(props.planId); + }} + > + {props.mainplan} + {props.startdate}~ diff --git a/src/components/plan/BackBtn.tsx b/src/components/plan/BackBtn.tsx new file mode 100644 index 0000000..82924de --- /dev/null +++ b/src/components/plan/BackBtn.tsx @@ -0,0 +1,41 @@ +import styled from 'styled-components'; +import { useNavigate } from 'react-router-dom'; + +export const BackBtn = () => { + const navigate = useNavigate(); + + const handleClickBtn = () => { + navigate('/home'); + }; + + return ( + + 뒤로가기 + + ); +}; + +const BackButton = styled.button` + position: absolute; + top: 30px; + left: 30px; + width: 28px; + height: 28px; + + border-radius: 50%; + border: none; + background-color: ${({ theme }) => theme.colors.green}; + z-index: 300; + + img { + width: 22px; + height: 22px; + } + + display: flex; + align-items: center; + justify-content: center; +`; diff --git a/src/components/plan/PlanContent.tsx b/src/components/plan/PlanContent.tsx index ccdbebe..de08a9f 100644 --- a/src/components/plan/PlanContent.tsx +++ b/src/components/plan/PlanContent.tsx @@ -16,9 +16,12 @@ export const PlanContent = () => { const PlanContentContainer = styled.div` position: absolute; - top: 0; + top: 40px; left: 50%; transform: translate(-50%, 0); `; -const BackgroundImg = styled.img``; +const BackgroundImg = styled.img` + border: none; + outline: none; +`; diff --git a/src/components/plan/PlanLayout.tsx b/src/components/plan/PlanLayout.tsx index 237bef2..6d7d50b 100644 --- a/src/components/plan/PlanLayout.tsx +++ b/src/components/plan/PlanLayout.tsx @@ -2,7 +2,6 @@ import styled from 'styled-components'; import { TopImgContent } from './TopImgContent'; import { PlanContent } from './PlanContent'; -import { BottomContent } from './BottomContent'; export const PlanLayout = () => { return ( @@ -12,8 +11,6 @@ export const PlanLayout = () => { {/*계획표 이미지, 텍스트*/} - {/*이미지 저장 버튼*/} - {/*상단 이미지들*/} @@ -21,8 +18,10 @@ export const PlanLayout = () => { }; const LayoutContainer = styled.div` + padding-top: 40px; height: 100vh; position: relative; + background-color: #ccedff; `; const TopBackground = styled.div` @@ -33,7 +32,7 @@ const TopBackground = styled.div` const BottomBackground = styled.div` width: 100%; - height: 80px; + height: 100%; //height: calc(100vh - 500px); background-color: #8cd27b; `; diff --git a/src/components/plan/TopImgContent.tsx b/src/components/plan/TopImgContent.tsx index e622e31..6b783e6 100644 --- a/src/components/plan/TopImgContent.tsx +++ b/src/components/plan/TopImgContent.tsx @@ -25,7 +25,7 @@ const TopImgContentContainer = styled.div` const CloudImg = styled.img` position: absolute; - top: 15px; + top: 60px; left: 15px; width: 120px; diff --git a/src/components/plan/ViewToggle.tsx b/src/components/plan/ViewToggle.tsx new file mode 100644 index 0000000..f15da07 --- /dev/null +++ b/src/components/plan/ViewToggle.tsx @@ -0,0 +1,101 @@ +import styled from 'styled-components'; + +enum PlanView { + Image = 'Image', + Graph = 'Graph', +} +interface ViewToggleProps { + view: PlanView; + setView: React.Dispatch>; +} +export const ViewToggle = ({ view, setView }: ViewToggleProps) => { + const handleClickToggleBtn = () => { + if (view === PlanView.Image) setView(PlanView.Graph); + else setView(PlanView.Image); + }; + return ( + + + {view === PlanView.Image ? ( + 꿈틀나무 + ) : ( + 꿈틀나무 + )} + 꿈틀나무 + + + {view === PlanView.Graph ? ( + 만다라트 + ) : ( + 만다라트 + )} + 만다라트 + + + ); +}; + +const ViewToggleContainer = styled.div` + position: absolute; + top: 28px; + left: 50%; + transform: translate(-50%, 0); + z-index: 300; + + width: 140px; + height: 32px; + padding: 4px; + border-radius: 8px; + + background-color: ${({ theme }) => theme.colors.gray_300}; + + display: flex; + justify-content: space-between; +`; + +const ToggleBtn = styled.button` + width: 64px; + height: 24px; + border-radius: 4px; + padding: 0; + border: none; + display: flex; + align-items: center; + justify-content: center; + + font-family: 'PretendardMedium'; + font-size: 12px; + background-color: ${({ theme }) => theme.colors.gray_300}; + color: ${({ theme }) => theme.colors.gray_600}; + &.selected { + background-color: white; + color: ${({ theme }) => theme.colors.green}; + box-shadow: 1px 1px 8px 0px #d9d9d9; + } + + img { + width: 12px; + height: 12px; + margin-right: 2px; + } + + transition: background-color 0.1s ease; +`; diff --git a/src/components/planedit/DoneModal.tsx b/src/components/planedit/DoneModal.tsx new file mode 100644 index 0000000..7fc7174 --- /dev/null +++ b/src/components/planedit/DoneModal.tsx @@ -0,0 +1,102 @@ +import styled from 'styled-components'; +import { Modal } from '@components/common/Modal'; +import { ModalHeader, HeaderContent, CloseBtn } from './EditModal'; + +interface DoneModalProps { + isOpenModal: boolean; + setIsOpenModal: React.Dispatch>; + plan: string; +} +export const DoneModal = ({ + isOpenModal, + setIsOpenModal, + plan, +}: DoneModalProps) => { + return ( + + + + 목표 완료 + { + setIsOpenModal(false); + }} + > + 닫기 + + + + "{plan}" + 목표를 완료하셨나요? + + + 아직이에요 + 완료했어요 + + + + ); +}; + +const DoneModalContainer = styled.div``; + +const PlanContent = styled.div` + width: 100%; + height: 100px; + margin: 8px 0; + + display: flex; + flex-direction: column; + justify-content: space-evenly; +`; + +const PlanText = styled.div` + width: 100%; + font-family: 'Pretendard'; + color: ${({ theme }) => theme.colors.green}; + text-align: center; +`; +const GuideText = styled.div` + width: 100%; + text-align: center; + + font-family: 'PretendardMedium'; + font-size: 12px; + color: ${({ theme }) => theme.colors.gray_900}; +`; + +const BottomBtnContainer = styled.div` + display: flex; + justify-content: space-between; +`; + +const BottomBtn = styled.button` + width: 132px; + height: 28px; + border-radius: 4px; + border: none; + padding: 0; + + font-family: 'Pretendard'; + font-size: 11px; +`; + +export const CancelBtn = styled(BottomBtn)` + background-color: white; + border: 1px solid ${({ theme }) => theme.colors.green}; + color: ${({ theme }) => theme.colors.green}; +`; + +export const DoneBtn = styled(BottomBtn)` + background-color: ${({ theme }) => theme.colors.green}; + color: white; +`; diff --git a/src/components/planedit/EditButtons.tsx b/src/components/planedit/EditButtons.tsx new file mode 100644 index 0000000..00f8e50 --- /dev/null +++ b/src/components/planedit/EditButtons.tsx @@ -0,0 +1,59 @@ +import styled from 'styled-components'; +import { contentState } from './PlanEditContent'; + +interface EditButtonsProps { + content: contentState; + setContent: React.Dispatch>; +} +export const EditButtons = ({ content, setContent }: EditButtonsProps) => { + return ( + + { + setContent(contentState.Edit); + }} + > + 목표 수정하기 + + { + setContent(contentState.Done); + }} + > + 목표 완료하기 + + + ); +}; + +const EditButtonsContainer = styled.div` + display: flex; + justify-content: space-around; + + width: 390px; + + &.hide { + display: none; + } +`; + +const Button = styled.button` + width: 180px; + height: 32px; + border-radius: 8px; + + border: none; + color: white; + font-family: 'PretendardMedium'; + font-size: 12px; +`; + +const EditBtn = styled(Button)` + background-color: #9d9bff; +`; + +const DoneBtn = styled(Button)` + background-color: #ff7ebc; +`; diff --git a/src/components/planedit/EditModal.tsx b/src/components/planedit/EditModal.tsx new file mode 100644 index 0000000..f458492 --- /dev/null +++ b/src/components/planedit/EditModal.tsx @@ -0,0 +1,109 @@ +import styled from 'styled-components'; +import { Modal } from '@components/common/Modal'; + +interface EditModalProps { + isOpenModal: boolean; + setIsOpenModal: React.Dispatch>; +} +export const EditModal = ({ isOpenModal, setIsOpenModal }: EditModalProps) => { + return ( + + + + 목표 수정 + { + setIsOpenModal(false); + }} + > + 닫기 + + + + + 취소 + 편집 완료 + + + + ); +}; + +const EditModalContainer = styled.div` + border-radius: 4px; + background-color: white; +`; + +export const ModalHeader = styled.div` + display: flex; + justify-content: space-between; + align-items: center; +`; + +export const HeaderContent = styled.div` + font-family: 'PretendardMedium'; + font-size: 13px; + color: ${({ theme }) => theme.colors.gray_900}; +`; + +export const CloseBtn = styled.button` + background-color: transparent; + border: none; + padding: 0; + + img { + width: 16px; + height: 16px; + } +`; + +const EditTextArea = styled.textarea` + width: 230px; + height: 100px; + padding: 12px; + margin-top: 6px; + + border: none; + background-color: ${({ theme }) => theme.colors.gray_200}; + border-radius: 2px; + outline: none; + + font-family: 'Pretendard'; +`; + +const BottomBtnContainer = styled.div` + display: flex; + justify-content: space-between; + margin-top: 6px; +`; + +const BottomBtn = styled.button` + width: 112px; + height: 24px; + border-radius: 4px; + border: none; + padding: 0; + + font-family: 'Pretendard'; + font-size: 11px; +`; + +export const CancelBtn = styled(BottomBtn)` + background-color: white; + border: 1px solid ${({ theme }) => theme.colors.green}; + color: ${({ theme }) => theme.colors.green}; +`; + +export const DoneBtn = styled(BottomBtn)` + background-color: ${({ theme }) => theme.colors.green}; + color: white; +`; diff --git a/src/components/planedit/PlanEditContent.tsx b/src/components/planedit/PlanEditContent.tsx new file mode 100644 index 0000000..3e1c08e --- /dev/null +++ b/src/components/planedit/PlanEditContent.tsx @@ -0,0 +1,34 @@ +import styled from 'styled-components'; +import { useState } from 'react'; + +import { EditButtons } from './EditButtons'; +import { EditModal } from './EditModal'; +import { DoneModal } from './DoneModal'; + +export enum contentState { + Default = 'Default', + Edit = 'Edit', + Done = 'Done', +} +export const PlanEditContent = () => { + const [content, setContent] = useState(contentState.Default); + const [isOpenEditModal, setIsOpenEditModal] = useState(false); + const [isOpenDoneModal, setIsOpenDoneModal] = useState(true); + + return ( + + + + + + ); +}; + +const PlanEditContentContainer = styled.div``; diff --git a/src/components/planimg/index.tsx b/src/components/planimg/index.tsx index 7c932c2..dad24a7 100644 --- a/src/components/planimg/index.tsx +++ b/src/components/planimg/index.tsx @@ -99,7 +99,7 @@ const StyledMiddleTableCell = styled.div<{ order?: string }>` `; interface PropsType { - refLink: React.RefObject; + refLink?: React.RefObject; } export const PlanImg = ({ refLink }: PropsType) => { @@ -150,7 +150,7 @@ export const PlanImg = ({ refLink }: PropsType) => { const Container = styled.div` width: 390px; height: 390px; - margin-bottom: 50px; + margin-bottom: 30px; `; export const StyledBigTable = styled.div` diff --git a/src/pages/Home/index.tsx b/src/pages/Home/index.tsx index 81172d0..04e3dd2 100644 --- a/src/pages/Home/index.tsx +++ b/src/pages/Home/index.tsx @@ -6,28 +6,28 @@ import { AddPlanBtn } from '@components/home/AddPlanBtn'; const data = [ { - mainplan: '개발자로 취업하기', + mainplan: '1년 안에 백엔드 개발자로 취업하기', subplans: ['자기계발', '네트워킹', '프로젝트', '학습'], startdate: '2023.05.12', - percent: 50, + percent: 64, }, { - mainplan: '개발자로 취업하기', - subplans: ['자기계발', '네트워킹', '프로젝트', '학습'], - startdate: '2023.05.12', - percent: 50, + mainplan: '20년 안에 내집 마련하기', + subplans: ['부동산 투자', '주택청약', '재테크', '경제 공부'], + startdate: '2023.01.01', + percent: 10, }, { - mainplan: '개발자로 취업하기', - subplans: ['자기계발', '네트워킹', '프로젝트', '학습'], - startdate: '2023.05.12', - percent: 50, + mainplan: '하프 마라톤 완주하기', + subplans: ['기록관리', '러닝크루', '근력운동', '체중관리'], + startdate: '2023.08.30', + percent: 36, }, { - mainplan: '개발자로 취업하기', - subplans: ['자기계발', '네트워킹', '프로젝트', '학습'], + mainplan: '대학교 졸업', + subplans: ['학점 관리', '졸업 프로젝트', '졸업 논문', '졸업 시험'], startdate: '2023.05.12', - percent: 50, + percent: 82, }, ]; @@ -39,6 +39,7 @@ export const HomePage = () => { return ( { + return ( + + + + + ); +}; + +const PlanEditLayoutContainer = styled.div` + padding-top: 100px; + + display: flex; + flex-direction: column; + align-items: center; +`; diff --git a/src/pages/Plan/index.tsx b/src/pages/Plan/index.tsx index 3e927f4..88c5ca7 100644 --- a/src/pages/Plan/index.tsx +++ b/src/pages/Plan/index.tsx @@ -1,19 +1,25 @@ -import { useRecoilValueLoadable } from 'recoil'; -import { mandalartPlan } from '@recoil/plan'; - -import { Loading } from './Loading'; -import { Plan } from './Plan'; -import { ErrorPage } from '@pages/Error'; +import styled from 'styled-components'; +import { useState } from 'react'; +import { PlanLayout } from '@components/plan/PlanLayout'; +import { PlanEditLayout } from './PlanEditLayout'; +import { BackBtn } from '@components/plan/BackBtn'; +import { ViewToggle } from '@components/plan/ViewToggle'; +enum PlanView { + Image = 'Image', + Graph = 'Graph', +} export const PlanPage = () => { - const planLodable = useRecoilValueLoadable(mandalartPlan); - - switch (planLodable.state) { - case 'hasValue': - return ; - case 'loading': - return ; - case 'hasError': - return ; - } + const [view, setView] = useState(PlanView.Image); + return ( + + + + {view === PlanView.Image ? : } + + ); }; + +const PlanPageContainer = styled.div` + position: relative; +`; diff --git a/src/pages/Plan/index_.tsx b/src/pages/Plan/index_.tsx new file mode 100644 index 0000000..3e927f4 --- /dev/null +++ b/src/pages/Plan/index_.tsx @@ -0,0 +1,19 @@ +import { useRecoilValueLoadable } from 'recoil'; +import { mandalartPlan } from '@recoil/plan'; + +import { Loading } from './Loading'; +import { Plan } from './Plan'; +import { ErrorPage } from '@pages/Error'; + +export const PlanPage = () => { + const planLodable = useRecoilValueLoadable(mandalartPlan); + + switch (planLodable.state) { + case 'hasValue': + return ; + case 'loading': + return ; + case 'hasError': + return ; + } +};