From d6c6fc50076b5667d2fdc7cb194a5eeeceb79d1a Mon Sep 17 00:00:00 2001 From: aaminha Date: Wed, 22 May 2024 21:34:56 +0900 Subject: [PATCH 1/9] =?UTF-8?q?refactor:=20=EC=A0=84=EB=B0=98=EC=A0=81?= =?UTF-8?q?=EC=9D=B8=20=EB=94=94=EC=9E=90=EC=9D=B8=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81=20(#61)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExperienceDetailPage/DetailSection.tsx | 142 +++++++++--------- src/components/common/Chip/PlainChip.tsx | 12 +- src/pages/ExperienceDetailPage.tsx | 50 +++--- 3 files changed, 105 insertions(+), 99 deletions(-) diff --git a/src/components/ExperienceDetailPage/DetailSection.tsx b/src/components/ExperienceDetailPage/DetailSection.tsx index 1c0edee..550e7b2 100644 --- a/src/components/ExperienceDetailPage/DetailSection.tsx +++ b/src/components/ExperienceDetailPage/DetailSection.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { styled } from 'styled-components'; @@ -7,11 +7,15 @@ import { PlainChip } from '@/components/common/Chip/PlainChip'; import { ExperienceDetailModal } from '@/components/common/Modal/ExperienceDetailModal'; import { DetailData } from '@/pages/ExperienceDetailPage'; -const DetailSection = ({ data }: { data: DetailData }) => { +const DetailSection = ({ data }: { data: DetailData | undefined }) => { const [isModalOpen, setModalOpen] = useState(false); - const [participants, setParticipants] = useState(data.participants); + const [participants, setParticipants] = useState(data ? data.participants : 0); const [isButtonDisabled, setButtonDisabled] = useState(false); + useEffect(() => { + if (data) setParticipants(data.participants); + }, [data]); + const handleOpenModal = () => { setModalOpen(true); }; @@ -26,60 +30,59 @@ const DetailSection = ({ data }: { data: DetailData }) => { setModalOpen(false); }; - return ( - - - Detail - - - - {participants}명 참여 중! - {data.title} - {data.subtitle} - - - - profile - - - {data.providerName} - - {data.providerJob} | {data.providerTitle} | {data.providerKeyword} - - - - - 신청하기 - - - - - ); + if (data) + return ( + + + Detail + + + + + {participants}명 참여 중! + + {data.title} + {data.subtitle} + + + + profile + + + {data.providerName} + + {data.providerJob} | {data.providerTitle} | {data.providerKeyword} + + + + + 신청하기 + + + + + ); }; export default DetailSection; const StyledContainer = styled.div` - width: 100%; - height: 100%; - justify-content: flex-start; - align-items: flex-start; + display: flex; gap: 32px; - display: inline-flex; `; -const ImageContainer = styled.div` +const ProgramImageContainer = styled.div` width: 597px; height: 373px; - position: relative; border-radius: 8px; overflow: hidden; flex-shrink: 0; @@ -88,62 +91,56 @@ const ImageContainer = styled.div` width: 100%; height: 100%; object-fit: cover; - border-radius: 8px; } `; const DetailContainer = styled.div` - flex: 1 1 0; - align-self: stretch; + flex: 1; + display: flex; flex-direction: column; - justify-content: flex-start; - align-items: flex-start; gap: 16px; - display: inline-flex; `; const TextContainer = styled.div` - align-self: stretch; flex: 1 1 0; + + display: flex; flex-direction: column; - justify-content: flex-start; - align-items: flex-start; gap: 10px; - display: flex; `; const TitleContainer = styled.div` - align-self: stretch; color: ${({ theme }) => `${theme.color.gray700}`}; ${({ theme }) => theme.font.desktop.title1}; + word-break: break-word; + overflow-wrap: break-word; `; const SubTitleContainer = styled.div` - align-self: stretch; color: ${({ theme }) => `${theme.color.gray500}`}; ${({ theme }) => theme.font.desktop.body2m}; + word-break: break-word; + overflow-wrap: break-word; `; const ProfileContainer = styled.div` - align-self: stretch; padding: 16px; background-color: ${({ theme }) => `${theme.color.white}`}; border-radius: 8px; - overflow: hidden; + word-break: break-word; + overflow-wrap: break-word; border: 2px solid ${({ theme }) => `${theme.color.gray150}`}; - justify-content: flex-start; - align-items: center; + + display: flex; gap: 16px; - display: inline-flex; - flex-shrink: 0; `; const ProfileImageContainer = styled.div` width: 56px; height: 56px; - background: linear-gradient(0deg, #f4efff 0%, #f4efff 100%); border-radius: 8px; overflow: hidden; + flex-shrink: 0; img { width: 100%; @@ -153,11 +150,12 @@ const ProfileImageContainer = styled.div` `; const ProfileTextContainer = styled.div` + word-wrap: break-all; + overflow-wrap: break-word; + + display: flex; flex-direction: column; - justify-content: center; - align-items: flex-start; gap: 4px; - display: inline-flex; `; const ProfileTitleContainer = styled.div` @@ -166,6 +164,10 @@ const ProfileTitleContainer = styled.div` `; const ProfileSubTitleContainer = styled.div` + width: 100%; + text-overflow: ellipsis; + word-break: break-word; + overflow-wrap: break-word; color: ${({ theme }) => `${theme.color.gray700}`}; ${({ theme }) => theme.font.desktop.body2m}; `; diff --git a/src/components/common/Chip/PlainChip.tsx b/src/components/common/Chip/PlainChip.tsx index 09fee0c..fd831eb 100644 --- a/src/components/common/Chip/PlainChip.tsx +++ b/src/components/common/Chip/PlainChip.tsx @@ -2,16 +2,22 @@ import styled, { css } from 'styled-components'; interface PlainChipProps { primary?: boolean; + width?: string; children: React.ReactNode; } -export const PlainChip = ({ primary = false, children }: PlainChipProps) => { - return {children}; +export const PlainChip = ({ primary = false, width, children }: PlainChipProps) => { + return ( + + {children} + + ); }; -const StyledContainer = styled.div<{ $primary: boolean }>` +const StyledContainer = styled.div<{ $primary: boolean; $width?: string }>` ${({ theme }) => theme.font.desktop.body1m}; + width: ${(props) => props.$width || 'auto'}; height: 42px; padding: 0 16px; border-radius: 8px; diff --git a/src/pages/ExperienceDetailPage.tsx b/src/pages/ExperienceDetailPage.tsx index 150229c..d14d11d 100644 --- a/src/pages/ExperienceDetailPage.tsx +++ b/src/pages/ExperienceDetailPage.tsx @@ -4,7 +4,6 @@ import { useParams } from 'react-router-dom'; import styled from 'styled-components'; import { authClient } from '@/apis/client'; -import TestImage from '@/assets/test1.png'; import { BubbleSection } from '@/components/ExperienceDetailPage/BubbleSection'; import DetailSection from '@/components/ExperienceDetailPage/DetailSection'; import { ImageSection } from '@/components/ExperienceDetailPage/ImageSection'; @@ -24,7 +23,8 @@ export interface DetailData { export const ExperienceDetailPage = () => { const { id } = useParams<{ id: string }>(); - const [data, setData] = useState(Dummy1); + const [data, setData] = useState(undefined); + const [keywords, setKeywords] = useState([]); useEffect(() => { const detailData = async () => { @@ -44,6 +44,7 @@ export const ExperienceDetailPage = () => { programURL: apiData.link, }; setData(formattedData); + setKeywords(apiData.keywords); } catch (error) { console.error(error); } @@ -52,37 +53,34 @@ export const ExperienceDetailPage = () => { detailData(); }, [id]); + if (!data) + return ( + + Loading... + + ); return ( - - - + + + + + ); }; const StyledContainer = styled.div` - width: 100%; - height: 100%; - padding: 64px 56px; + min-width: 1280px; + padding-top: 76px; +`; + +const StyledInnerContainer = styled.div` + width: 1280px; + padding: 56px; 64px; + margin: 0 auto; + + display: flex; flex-direction: column; - justify-content: flex-start; - align-items: flex-start; gap: 72px; - display: inline-flex; - padding-top: 142px; `; - -export const Dummy1: DetailData = { - imageURL: TestImage, //imageUrl - profileImageURL: TestImage, //providerUrl - participants: 28, - title: '퍼스널 브랜딩, ‘나’를 기획, 디자인하기', //name - subtitle: - '대한민국은 국제평화의 유지에 노력하고 침략적 전쟁을 부인한다. 국민의 모든 자유와 권리는 국가안전보장·질서유지 또는 공공복리를 위하여 필요한 경우에 한하여 법률로써 제한할 수 있으며.대한민국은 국제평화의 유지에 노력하고 침략적 전쟁을 부인한다.', //on - providerName: '신민선', - providerJob: '콘텐츠 마케터', - providerTitle: '퍼스널브랜딩', - providerKeyword: '어쩌구', - programURL: TestImage, -}; From 4f25d2e76f57492dbf95c029feacc538b416335d Mon Sep 17 00:00:00 2001 From: aaminha Date: Wed, 22 May 2024 22:00:15 +0900 Subject: [PATCH 2/9] =?UTF-8?q?feat:=20BubbleSection=20=EA=B5=AC=ED=98=84?= =?UTF-8?q?=20(#61)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ExperienceDetailPage/BubbleSection.tsx | 142 +++++++++++++++++- src/components/HomePage/PieceSection.tsx | 24 +-- src/styles/global/GlobalStyle.ts | 22 ++- 3 files changed, 163 insertions(+), 25 deletions(-) diff --git a/src/components/ExperienceDetailPage/BubbleSection.tsx b/src/components/ExperienceDetailPage/BubbleSection.tsx index d2c7d13..8f4dd20 100644 --- a/src/components/ExperienceDetailPage/BubbleSection.tsx +++ b/src/components/ExperienceDetailPage/BubbleSection.tsx @@ -1,3 +1,141 @@ -export const BubbleSection = () => { - return
여긴 버블버블
; +import styled from 'styled-components'; + +import { moveDown, moveLeft, moveRight, moveUp } from '@/styles'; + +interface BubbleSectionProps { + keywords: string[]; +} + +export const BubbleSection = ({ keywords }: BubbleSectionProps) => { + return ( + +
+ 이런 이미지 키워드의 사람이 선호하는 경험이에요. +
+ + {keywords.map((keyword, index) => ( + + {keyword} + + ))} + +
+ ); }; + +const StyledContainer = styled.section` + width: 100%; + + .title { + ${({ theme }) => theme.font.desktop.title1}; + color: ${({ theme }) => theme.color.gray700}; + margin-bottom: 24px; + + .highlight { + color: ${({ theme }) => theme.color.primary500}; + } + } +`; + +const StyledBubbleContainer = styled.div` + height: 481px; + position: relative; + + border-radius: 8px; + border: 2px solid ${({ theme }) => theme.color.primary100}; + background: linear-gradient(180deg, rgba(255, 255, 255, 0) 59.5%, #e1d1ff 100%), #fff; +`; + +const StyledBubble = styled.div<{ $weight: number }>` + width: ${({ $weight }) => $weight * 270}px; + height: ${({ $weight }) => $weight * 270}px; + + display: flex; + justify-content: center; + align-items: center; + will-change: transform; + + text-align: center; + color: ${({ theme }) => theme.color.primary800}; + ${({ theme }) => theme.font.desktop.body1b}; + + position: absolute; + + border-radius: 50%; + background: radial-gradient( + 50% 50% at 50% 50%, + rgba(255, 255, 255, 0) 76%, + rgba(204, 179, 253, 0.4) 100% + ); + + box-shadow: + -0.319px 51.263px 151.773px 0px rgba(48, 13, 115, 0.12), + -0.04px 6.444px 21.116px 0px rgba(48, 13, 115, 0.06); + + &.b0 { + bottom: 10%; + left: 30%; + + animation: + ${moveDown} 2000ms ease-in-out infinite, + ${moveLeft} 3000ms ease-in-out infinite; + } + + &.b1 { + top: 12%; + right: 32%; + + animation: + ${moveDown} 3000ms ease-in-out infinite, + ${moveRight} 2000ms ease-in-out infinite; + } + + &.b2 { + bottom: 20%; + right: 17%; + + animation: + ${moveUp} 2500ms ease-in-out infinite, + ${moveLeft} 3000ms ease-in-out infinite; + } + + &.b3 { + top: 20%; + left: 20%; + + animation: + ${moveUp} 2300ms ease-in-out infinite, + ${moveLeft} 2700ms ease-in-out infinite; + } + + &.b4 { + top: 10%; + right: 14%; + + animation: + ${moveDown} 2500ms ease-in-out infinite, + ${moveRight} 2300ms ease-in-out infinite; + } + + &.b5 { + top: 20%; + left: 10%; + + animation: + ${moveUp} 2600ms ease-in-out infinite, + ${moveRight} 3000ms ease-in-out infinite; + } + + &.b6 { + bottom: 14%; + left: 20%; + + animation: + ${moveDown} 3000ms ease-in-out infinite, + ${moveRight} 2800ms ease-in-out infinite; + } +`; diff --git a/src/components/HomePage/PieceSection.tsx b/src/components/HomePage/PieceSection.tsx index 24cce01..863dba6 100644 --- a/src/components/HomePage/PieceSection.tsx +++ b/src/components/HomePage/PieceSection.tsx @@ -1,11 +1,11 @@ -import styled, { keyframes } from 'styled-components'; +import styled from 'styled-components'; import { ReactComponent as ArrowIcon } from '@/assets/icons/arrowDown.svg'; import { PlainChip } from '@/components/common/Chip/PlainChip'; import { CARD_IMAGE } from '@/constants/card'; import { PERSONA } from '@/constants/persona'; import { userService } from '@/services/UserService'; -import { SectionContainer } from '@/styles'; +import { SectionContainer, moveDown, moveLeft, moveRight, moveUp } from '@/styles'; import { DefineResult } from '@/types/test.type'; interface PieceSectionProps { @@ -107,26 +107,6 @@ const StyledCardContainer = styled.div` } `; -const moveDown = keyframes` - 50% { - transform: translateY(-20px); - }`; - -const moveUp = keyframes` - 50% { - transform: translateY(20px); - }`; - -const moveRight = keyframes` - 50% { - transform: translateX(20px); - }`; - -const moveLeft = keyframes` - 50% { - transform: translateX(-20px); - }`; - const StyledBubble = styled.div<{ $weight: number }>` width: ${({ $weight }) => $weight * 360}px; height: ${({ $weight }) => $weight * 360}px; diff --git a/src/styles/global/GlobalStyle.ts b/src/styles/global/GlobalStyle.ts index c752e97..15f2543 100644 --- a/src/styles/global/GlobalStyle.ts +++ b/src/styles/global/GlobalStyle.ts @@ -1,4 +1,4 @@ -import styled, { createGlobalStyle } from 'styled-components'; +import styled, { createGlobalStyle, keyframes } from 'styled-components'; import reset from 'styled-reset'; export const GlobalStyle = createGlobalStyle` @@ -74,3 +74,23 @@ export const SectionContainer = styled.div` width: 1280px; margin: 0 auto; `; + +export const moveDown = keyframes` + 50% { + transform: translateY(-20px); + }`; + +export const moveUp = keyframes` + 50% { + transform: translateY(20px); + }`; + +export const moveRight = keyframes` + 50% { + transform: translateX(20px); + }`; + +export const moveLeft = keyframes` + 50% { + transform: translateX(-20px); + }`; From 2be8417e0fa306bafb138adfa018d8ade6386f8a Mon Sep 17 00:00:00 2001 From: aaminha Date: Wed, 22 May 2024 22:22:57 +0900 Subject: [PATCH 3/9] =?UTF-8?q?feat:=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EC=84=B9=EC=85=98=20=EA=B5=AC=ED=98=84=20=EB=B0=8F=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EA=B5=AC=EC=A1=B0=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?(#61)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/programAPI.ts | 5 ++ .../ExperienceDetailPage/DetailSection.tsx | 18 +++---- .../ExperienceDetailPage/ImageSection.tsx | 39 +-------------- src/pages/ExperienceDetailPage.tsx | 47 +++++-------------- src/routers/router.tsx | 3 +- src/types/program.type.ts | 16 +++++++ 6 files changed, 47 insertions(+), 81 deletions(-) diff --git a/src/apis/programAPI.ts b/src/apis/programAPI.ts index 6a34879..8ccac07 100644 --- a/src/apis/programAPI.ts +++ b/src/apis/programAPI.ts @@ -48,4 +48,9 @@ export const programAPI = { ); return response.data; }, + // 프로그램 상세페이지 조회 + getProgramDetail: async (type: string, id: string) => { + const response = await authClient.get(`/api/programs/${type}/${id}`); + return response.data; + }, }; diff --git a/src/components/ExperienceDetailPage/DetailSection.tsx b/src/components/ExperienceDetailPage/DetailSection.tsx index 550e7b2..0420886 100644 --- a/src/components/ExperienceDetailPage/DetailSection.tsx +++ b/src/components/ExperienceDetailPage/DetailSection.tsx @@ -5,11 +5,11 @@ import { styled } from 'styled-components'; import { PlainButton } from '@/components/common/Button/PlainButton'; import { PlainChip } from '@/components/common/Chip/PlainChip'; import { ExperienceDetailModal } from '@/components/common/Modal/ExperienceDetailModal'; -import { DetailData } from '@/pages/ExperienceDetailPage'; +import { ProgramDetailResult } from '@/types/program.type'; -const DetailSection = ({ data }: { data: DetailData | undefined }) => { +const DetailSection = ({ data }: { data: ProgramDetailResult | undefined }) => { const [isModalOpen, setModalOpen] = useState(false); - const [participants, setParticipants] = useState(data ? data.participants : 0); + const [participants, setParticipants] = useState(data ? data.participants : 0); const [isButtonDisabled, setButtonDisabled] = useState(false); useEffect(() => { @@ -25,7 +25,7 @@ const DetailSection = ({ data }: { data: DetailData | undefined }) => { }; const handleIncreaseParticipants = () => { - setParticipants((prevParticipants) => prevParticipants + 1); + setParticipants((prev) => prev + 1); setButtonDisabled(true); setModalOpen(false); }; @@ -34,24 +34,24 @@ const DetailSection = ({ data }: { data: DetailData | undefined }) => { return ( - Detail + Detail {participants}명 참여 중! - {data.title} - {data.subtitle} + {data.name} + {data.oneLineDescription} - profile + profile {data.providerName} - {data.providerJob} | {data.providerTitle} | {data.providerKeyword} + {data.providerJob} | {data.providerKeyword} diff --git a/src/components/ExperienceDetailPage/ImageSection.tsx b/src/components/ExperienceDetailPage/ImageSection.tsx index ff20314..654517d 100644 --- a/src/components/ExperienceDetailPage/ImageSection.tsx +++ b/src/components/ExperienceDetailPage/ImageSection.tsx @@ -1,18 +1,7 @@ import { styled } from 'styled-components'; -import { Dummy1 } from '@/pages/ExperienceDetailPage'; - -export const ImageSection = () => { - return ( - - 프로그램 소개 - - - Detail - - - - ); +export const ImageSection = ({ description }: { description: string | undefined }) => { + return {description && Detail}; }; const StyledContainer = styled.div` @@ -24,27 +13,3 @@ const StyledContainer = styled.div` gap: 24px; display: inline-flex; `; - -const TitleContainer = styled.div` - text-align: center; - color: ${({ theme }) => `${theme.color.gray900}`}; - ${({ theme }) => theme.font.desktop.title1}; -`; - -const ImageBorderContainer = styled.div` - align-self: stretch; - padding: 32px; - background-color: ${({ theme }) => `${theme.color.white}`}; - border-radius: 8px; - overflow: hidden; - border: 2px solid ${({ theme }) => `${theme.color.gray150}`}; - flex-direction: column; - justify-content: flex-start; - align-items: flex-start; - gap: 20px; - display: flex; -`; - -const ImageContainer = styled.div` - //연동하면서 수정 예정 -`; diff --git a/src/pages/ExperienceDetailPage.tsx b/src/pages/ExperienceDetailPage.tsx index d14d11d..79a96be 100644 --- a/src/pages/ExperienceDetailPage.tsx +++ b/src/pages/ExperienceDetailPage.tsx @@ -3,55 +3,34 @@ import { useEffect, useState } from 'react'; import { useParams } from 'react-router-dom'; import styled from 'styled-components'; -import { authClient } from '@/apis/client'; +import { programAPI } from '@/apis/programAPI'; import { BubbleSection } from '@/components/ExperienceDetailPage/BubbleSection'; import DetailSection from '@/components/ExperienceDetailPage/DetailSection'; import { ImageSection } from '@/components/ExperienceDetailPage/ImageSection'; - -export interface DetailData { - imageURL: string; - profileImageURL: string; - participants: number; - title: string; - subtitle: string; - providerName: string; - providerJob: string; - providerTitle: string; - providerKeyword: string; - programURL: string; -} +import { ProgramDetailResult } from '@/types/program.type'; export const ExperienceDetailPage = () => { - const { id } = useParams<{ id: string }>(); - const [data, setData] = useState(undefined); + const { type, id } = useParams(); + const [data, setData] = useState(undefined); const [keywords, setKeywords] = useState([]); + const [description, setDescription] = useState(undefined); useEffect(() => { const detailData = async () => { try { - const response = await authClient.get(`/api/programs/branding/${id}`); - const apiData = response.data.payload; - const formattedData: DetailData = { - imageURL: apiData.imageUrl, - profileImageURL: apiData.providerImage, - participants: apiData.participants, - title: apiData.name, - subtitle: apiData.oneLineDescription, - providerName: apiData.providerName, - providerJob: apiData.providerJob, - providerTitle: apiData.name, - providerKeyword: apiData.keywords.join(', '), - programURL: apiData.link, - }; - setData(formattedData); - setKeywords(apiData.keywords); + if (type && id) { + const response = await programAPI.getProgramDetail(type, id); + setData(response.payload); + setKeywords(response.payload.keywords); + setDescription(response.payload.descriptionUrl); + } } catch (error) { console.error(error); } }; detailData(); - }, [id]); + }, [type, id]); if (!data) return ( @@ -64,7 +43,7 @@ export const ExperienceDetailPage = () => { - + ); diff --git a/src/routers/router.tsx b/src/routers/router.tsx index 2855514..a4a5f61 100644 --- a/src/routers/router.tsx +++ b/src/routers/router.tsx @@ -32,7 +32,8 @@ export const Router = () => { } /> } /> } /> - } /> + {/* } /> */} + } /> {/* }> } /> */} diff --git a/src/types/program.type.ts b/src/types/program.type.ts index 0d2e244..bc02d36 100644 --- a/src/types/program.type.ts +++ b/src/types/program.type.ts @@ -9,3 +9,19 @@ export interface RecommendProgramItem { price: number; keywords: string[]; } + +export interface ProgramDetailResult { + imageUrl: string; + name: string; + oneLineDescription: string; + price: number; + form: string | null; + descriptionUrl: string; + link: string; + keywords: string[]; + participants: number; + providerImage: string; + providerName: string; + providerJob: string; + providerKeyword: string; +} From 90e24ac419568df894cbd889b5e9b6c591ac25cd Mon Sep 17 00:00:00 2001 From: aaminha Date: Wed, 22 May 2024 22:28:39 +0900 Subject: [PATCH 4/9] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EB=9E=A8=20=EC=83=81=EC=84=B8=20=EC=A1=B0=ED=9A=8C=EC=97=90=20?= =?UTF-8?q?=EC=8B=A4=ED=8C=A8=ED=96=88=EC=9D=84=20=EB=95=8C=20=EC=95=A1?= =?UTF-8?q?=EC=85=98=20=EC=B6=94=EA=B0=80=20(#61)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/ExperienceDetailPage.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/pages/ExperienceDetailPage.tsx b/src/pages/ExperienceDetailPage.tsx index 79a96be..765f55e 100644 --- a/src/pages/ExperienceDetailPage.tsx +++ b/src/pages/ExperienceDetailPage.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from 'react'; -import { useParams } from 'react-router-dom'; +import { useNavigate, useParams } from 'react-router-dom'; import styled from 'styled-components'; import { programAPI } from '@/apis/programAPI'; @@ -14,6 +14,7 @@ export const ExperienceDetailPage = () => { const [data, setData] = useState(undefined); const [keywords, setKeywords] = useState([]); const [description, setDescription] = useState(undefined); + const navigate = useNavigate(); useEffect(() => { const detailData = async () => { @@ -26,6 +27,9 @@ export const ExperienceDetailPage = () => { } } catch (error) { console.error(error); + window.alert('프로그램 정보를 불러오는데 실패했습니다.'); + // 프로그램 페이지로 이동 + navigate('/program'); } }; From 6b208b0b43972db999df74d9486e332eb3018a17 Mon Sep 17 00:00:00 2001 From: aaminha Date: Wed, 22 May 2024 22:43:48 +0900 Subject: [PATCH 5/9] =?UTF-8?q?fix:=20QA=20=EB=B0=98=EC=98=81=20-=20TestNa?= =?UTF-8?q?vigation=20=ED=85=8D=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/common/Layout/TestLayout.tsx | 2 +- .../common/Navigation/TestNavigation.tsx | 47 +++++++++++-------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/components/common/Layout/TestLayout.tsx b/src/components/common/Layout/TestLayout.tsx index ba9f77f..ae597f6 100644 --- a/src/components/common/Layout/TestLayout.tsx +++ b/src/components/common/Layout/TestLayout.tsx @@ -1,6 +1,6 @@ import { Outlet } from 'react-router-dom'; -import TestNavigation from '@/components/common/Navigation/TestNavigation'; +import { TestNavigation } from '@/components/common/Navigation/TestNavigation'; export const TestLayout = () => { return ( diff --git a/src/components/common/Navigation/TestNavigation.tsx b/src/components/common/Navigation/TestNavigation.tsx index 0c3874e..afee254 100644 --- a/src/components/common/Navigation/TestNavigation.tsx +++ b/src/components/common/Navigation/TestNavigation.tsx @@ -1,6 +1,33 @@ -import { useNavigate } from 'react-router-dom'; +import { useLocation, useNavigate } from 'react-router-dom'; import styled from 'styled-components'; +const NAVIGATION_LIST: { [key: string]: string } = { + define: 'Define 정의하기', + design: 'Design 설계하기', + discover: 'Discover 이해하기', +}; + +export const TestNavigation = () => { + const navigate = useNavigate(); + const location = useLocation(); + + const handleButtonClick = () => { + navigate('/understand'); + }; + + const currentKey = Object.keys(NAVIGATION_LIST).find((key) => location.pathname.includes(key)); + const currentTitle = currentKey ? NAVIGATION_LIST[currentKey] : ''; + + return ( + + + {currentTitle} + 종료하기 + + + ); +}; + const StyledContainer = styled.header` display: flex; justify-content: space-between; @@ -69,21 +96,3 @@ const StyledButton = styled.button` ${({ theme }) => theme.font.mobile.body1b}; } `; - -const TestNavigation = () => { - const navigate = useNavigate(); - - const handleButtonClick = () => { - navigate('/understand'); - }; - return ( - - - Define 정의하기 - 종료하기 - - - ); -}; - -export default TestNavigation; From b702d4eec5f7e47a3517754ad5ffc033d5059102 Mon Sep 17 00:00:00 2001 From: aaminha Date: Wed, 22 May 2024 22:44:55 +0900 Subject: [PATCH 6/9] =?UTF-8?q?fix:=20QA=20=EB=B0=98=EC=98=81=20-=20?= =?UTF-8?q?=EB=AC=B8=ED=95=AD=20=EC=88=98=20=EC=98=A4=ED=83=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/DesignStartPage/DesignStartView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/DesignStartPage/DesignStartView.tsx b/src/components/DesignStartPage/DesignStartView.tsx index f9a9580..f508d93 100644 --- a/src/components/DesignStartPage/DesignStartView.tsx +++ b/src/components/DesignStartPage/DesignStartView.tsx @@ -21,7 +21,7 @@ export const DesignStartView = () => { 어떤 브랜더가 되고 싶나요? - 문항은 총 3문항으로, 앞으로 만들어 갈 나의 브랜드 + 문항은 총 5문항으로, 앞으로 만들어 갈 나의 브랜드 방향성을 설정하는 첫 걸음이에요.
설계하기 테스트를 통해 나의 브랜드 컨셉을 정해보고,{' '} From 73d48ead315ae402780eaaf3c89068e2308700dc Mon Sep 17 00:00:00 2001 From: aaminha Date: Wed, 22 May 2024 22:56:11 +0900 Subject: [PATCH 7/9] =?UTF-8?q?fix:=20=EB=B9=84=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=20=EC=9C=A0=EC=A0=80=EB=8A=94=20=EC=A0=91=EA=B7=BC=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EB=AA=BB=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(#61)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routers/router.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/routers/router.tsx b/src/routers/router.tsx index a4a5f61..7c7d4ae 100644 --- a/src/routers/router.tsx +++ b/src/routers/router.tsx @@ -32,11 +32,9 @@ export const Router = () => { } /> } /> } /> - {/* } /> */} - } /> - {/* }> - } /> - */} + }> + } /> + }> From aab36dcd4c7bf8c791dd767df5b589c10fa17fd6 Mon Sep 17 00:00:00 2001 From: aaminha Date: Wed, 22 May 2024 23:30:25 +0900 Subject: [PATCH 8/9] =?UTF-8?q?design:=20ExperienceDetailModal=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20(#61)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/common/Modal/DefaultModal.tsx | 2 + .../common/Modal/ExperienceDetailModal.tsx | 49 +++++-------------- 2 files changed, 13 insertions(+), 38 deletions(-) diff --git a/src/components/common/Modal/DefaultModal.tsx b/src/components/common/Modal/DefaultModal.tsx index d3e1cbc..a69ba2c 100644 --- a/src/components/common/Modal/DefaultModal.tsx +++ b/src/components/common/Modal/DefaultModal.tsx @@ -56,4 +56,6 @@ const StyledModalContainer = styled.div<{ $width: string; $height: string }>` border-radius: 16px; background: rgba(255, 255, 255, 0.95); + box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.25); + backdrop-filter: blur(4px); `; diff --git a/src/components/common/Modal/ExperienceDetailModal.tsx b/src/components/common/Modal/ExperienceDetailModal.tsx index eca38b2..24b1cce 100644 --- a/src/components/common/Modal/ExperienceDetailModal.tsx +++ b/src/components/common/Modal/ExperienceDetailModal.tsx @@ -3,6 +3,7 @@ import { useEffect } from 'react'; import styled from 'styled-components'; import { PlainButton } from '@/components/common/Button/PlainButton'; +import { DefaultModal } from '@/components/common/Modal/DefaultModal'; interface ModalProps { isOpen: boolean; @@ -24,51 +25,31 @@ export const ExperienceDetailModal = ({ isOpen, onClose, onConfirm }: ModalProps if (!isOpen) return null; return ( - - + + 프로그램 신청 - + 해당 프로그램을 신청하시겠습니까? - + - + 취소하기 - + 신청하기 - - + + ); }; -const ModalOverlay = styled.div` - position: fixed; - top: 0; - left: 0; - width: 100%; +const ContentContainer = styled.div` height: 100%; - background: rgba(17.85, 17.85, 17.85, 0.36); - backdrop-filter: blur(10px); display: flex; - justify-content: center; - align-items: center; -`; - -const ModalContent = styled.div` - width: 618px; - height: 338px; - background: white; - padding: 24px; - box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.25); - border-radius: 16px; - overflow: hidden; - backdrop-filter: blur(8px); flex-direction: column; justify-content: space-between; align-items: center; - display: inline-flex; `; const TitleContainer = styled.div` @@ -78,7 +59,7 @@ const TitleContainer = styled.div` ${({ theme }) => theme.font.desktop.body1b}; `; -const ContentContainer = styled.div` +const DescriptionContainer = styled.div` color: ${({ theme }) => `${theme.color.gray700}`}; ${({ theme }) => theme.font.desktop.title2}; `; @@ -90,14 +71,6 @@ const ButtonContainer = styled.div` display: inline-flex; `; -const StyledButton = styled(PlainButton)` - background: ${({ theme }) => `${theme.color.gray200}`}; - &:hover { - background: ${({ theme }) => `${theme.color.gray200}`}; - } - color: inherit; -`; - const Highlight = styled.span` color: ${({ theme }) => theme.color.primary500}; `; From 3e329ead4a7e8731417329529662acd93937aeaf Mon Sep 17 00:00:00 2001 From: aaminha Date: Wed, 22 May 2024 23:38:26 +0900 Subject: [PATCH 9/9] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EB=9E=A8=20=EC=8B=A0=EC=B2=AD=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#61)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/programAPI.ts | 8 ++++++++ .../ExperienceDetailPage/DetailSection.tsx | 20 ++++++++++++++----- src/components/common/Button/PlainButton.tsx | 1 + src/pages/ExperienceDetailPage.tsx | 15 +++++++------- src/types/program.type.ts | 2 ++ 5 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/apis/programAPI.ts b/src/apis/programAPI.ts index 8ccac07..19a8862 100644 --- a/src/apis/programAPI.ts +++ b/src/apis/programAPI.ts @@ -53,4 +53,12 @@ export const programAPI = { const response = await authClient.get(`/api/programs/${type}/${id}`); return response.data; }, + // 프로그램 신청 + applyProgram: async (type: string, programId: string) => { + const response = await authClient.post(`/api/programs/apply`, { + type, + programId, + }); + return response.data; + }, }; diff --git a/src/components/ExperienceDetailPage/DetailSection.tsx b/src/components/ExperienceDetailPage/DetailSection.tsx index 0420886..1145985 100644 --- a/src/components/ExperienceDetailPage/DetailSection.tsx +++ b/src/components/ExperienceDetailPage/DetailSection.tsx @@ -2,15 +2,21 @@ import { useEffect, useState } from 'react'; import { styled } from 'styled-components'; +import { programAPI } from '@/apis/programAPI'; import { PlainButton } from '@/components/common/Button/PlainButton'; import { PlainChip } from '@/components/common/Chip/PlainChip'; import { ExperienceDetailModal } from '@/components/common/Modal/ExperienceDetailModal'; import { ProgramDetailResult } from '@/types/program.type'; -const DetailSection = ({ data }: { data: ProgramDetailResult | undefined }) => { +interface DetailSectionProps { + data: ProgramDetailResult | undefined; + programId: string; +} + +const DetailSection = ({ data, programId }: DetailSectionProps) => { const [isModalOpen, setModalOpen] = useState(false); const [participants, setParticipants] = useState(data ? data.participants : 0); - const [isButtonDisabled, setButtonDisabled] = useState(false); + const [isButtonDisabled, setButtonDisabled] = useState(data ? data.apply : false); useEffect(() => { if (data) setParticipants(data.participants); @@ -25,9 +31,13 @@ const DetailSection = ({ data }: { data: ProgramDetailResult | undefined }) => { }; const handleIncreaseParticipants = () => { - setParticipants((prev) => prev + 1); - setButtonDisabled(true); - setModalOpen(false); + if (data) { + programAPI.applyProgram(data.type, programId).then(() => { + setParticipants((prev) => prev + 1); + setButtonDisabled(true); + setModalOpen(false); + }); + } }; if (data) diff --git a/src/components/common/Button/PlainButton.tsx b/src/components/common/Button/PlainButton.tsx index fdb6052..05856dd 100644 --- a/src/components/common/Button/PlainButton.tsx +++ b/src/components/common/Button/PlainButton.tsx @@ -82,5 +82,6 @@ const StyledButton = styled.button` &:disabled { color: ${(props) => props.theme.color.gray700}; background: ${(props) => props.theme.color.gray200}; + cursor: default; } `; diff --git a/src/pages/ExperienceDetailPage.tsx b/src/pages/ExperienceDetailPage.tsx index 765f55e..f16cf27 100644 --- a/src/pages/ExperienceDetailPage.tsx +++ b/src/pages/ExperienceDetailPage.tsx @@ -36,19 +36,20 @@ export const ExperienceDetailPage = () => { detailData(); }, [type, id]); - if (!data) + if (data && id) return ( - Loading... + + + + + ); + return ( - - - - - + Loading... ); }; diff --git a/src/types/program.type.ts b/src/types/program.type.ts index bc02d36..81da054 100644 --- a/src/types/program.type.ts +++ b/src/types/program.type.ts @@ -24,4 +24,6 @@ export interface ProgramDetailResult { providerName: string; providerJob: string; providerKeyword: string; + type: string; + apply: boolean; }