Skip to content

Commit

Permalink
Feat/intersting stock (#20)
Browse files Browse the repository at this point in the history
* ✨ 관심공모주 페이지 생성 및 관심 공모주 없을때 표시

* ✨ 마이페이지 모달 추가

* ✨ 디테일에 바텀시트 연결
  • Loading branch information
ckhe1215 authored Oct 14, 2023
1 parent 00b1685 commit 3c0c827
Show file tree
Hide file tree
Showing 14 changed files with 449 additions and 29 deletions.
26 changes: 26 additions & 0 deletions src/app/interesting-stock/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"use client";

import { Header } from "@/components/common/Header";
import colors from "@/styles/colors";
import React, { FC } from "react";
import styled from "styled-components";

interface LayoutProps {
children: React.ReactNode;
}

const InterestingStockLayout: FC<LayoutProps> = ({ children }) => {
return (
<MainWrapper>
<Header title="나의 관심 공모주" />
<main>{children}</main>
</MainWrapper>
);
};

export default InterestingStockLayout;

const MainWrapper = styled.div`
background-color: ${colors.WHITE};
height: 100vh;
`;
66 changes: 66 additions & 0 deletions src/app/interesting-stock/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"use client";

import TapMenu from "@/components/common/TapMenu";
import { getFonts } from "@/styles/fonts";
import HeartIcon from "@/svg/HeartIcon";
import { FC, useState } from "react";
import styled from "styled-components";

type status = "ALL" | "READY" | "IN_PROGRESS" | "DONE";

type Subscription = "disable" | "able" | "limit";

interface DummyInterestingStockType {
id: string;
title: string;
love: boolean;
category: string;
account: string;
minPrice: number;
maxPrice: number;
subscription?: Subscription;
subscriptionDueDate: string;
accountDueDate: string;
}

const MyPage: FC = () => {
const [menuState, setMenuState] = useState<status>("ALL");

const dummyInterestingStocks: DummyInterestingStockType[] = [];

return (
<>
<TapMenu
onChange={setMenuState}
value={menuState}
options={[
{ label: "전체 0", value: "ALL" },
{ label: "청약전 0", value: "READY" },
{ label: "청약중 0", value: "IN_PROGRESS" },
{ label: "청약종료 0", value: "DONE" },
]}
/>
{/* TODO : 데이터 있으면 UpComingStock 카드 재활용 */}
{dummyInterestingStocks.length === 0 ? (
<EmptyInterestingStockList>
<HeartIcon.fill width={80} height={80} />
<h3>아직 관심 공모주가 없어요.</h3>
</EmptyInterestingStockList>
) : null}
</>
);
};

export default MyPage;

const EmptyInterestingStockList = styled.div`
display: flex;
flex-direction: column;
align-items: center;
padding-top: 120px;
h3 {
padding-top: 8px;
${getFonts("H3_SEMIBOLD")}
}
`;
101 changes: 101 additions & 0 deletions src/components/common/Modal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import colors from "@/styles/colors";
import { getFonts } from "@/styles/fonts";
import { FC } from "react";
import styled from "styled-components";
import { initalModalData, ModalData } from "../mypage/MenuSection";
import { Overlay } from "./Overlay";

interface ModalProps {
/**
* 모달 박스 제목
*/
title: string;
/**
* 모달 박스 내용
*/
content?: string;
/**
* 모달 버튼 내용
*/
buttonText: [string, string] | string;
/**
* Primary 버튼 클릭 핸들러
*/
handlePrimaryButtonClick: () => void;
/**
* 모달 on/off 핸들러
*/
setIsModalShowing: (v: ModalData) => void;
}

const Modal: FC<ModalProps> = (props) => {
const { title, content, buttonText, handlePrimaryButtonClick, setIsModalShowing } = props;
return (
<Overlay onClick={() => setIsModalShowing(initalModalData)}>
<ModalBox onClick={(e) => e.stopPropagation()}>
<h3>{title}</h3>
{content ? <p>{content}</p> : null}
<div>
{typeof buttonText === "string" ? (
<ModalButton primary={true} onClick={handlePrimaryButtonClick}>
{buttonText}
</ModalButton>
) : (
buttonText.map((text, idx) => (
<ModalButton
primary={idx === 1}
onClick={idx === 1 ? handlePrimaryButtonClick : () => setIsModalShowing(initalModalData)}
key={text}
>
{text}
</ModalButton>
))
)}
</div>
</ModalBox>
</Overlay>
);
};

export default Modal;

const ModalBox = styled.div`
position: absolute;
top: 50%;
left: 50%;
translate: -50% -50%;
width: 295px;
padding: 24px 16px 16px 16px;
border-radius: 8px;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
background-color: white;
h3 {
${getFonts("H3_SEMIBOLD")}
}
p {
${getFonts("BODY1_REGULAR")}
padding-top: 11px;
white-space: pre-line;
}
div {
display: flex;
gap: 8px;
padding-top: 24px;
width: 100%;
}
`;

const ModalButton = styled.button<{ primary: boolean }>`
flex-grow: 1;
padding: 16px 20px 16px 20px;
border-radius: 6px;
background-color: ${(props) => (props.primary ? colors.ON.PRIMARY : colors.BLUE[1])};
color: ${(props) => (props.primary ? colors.WHITE : colors.ON.PRIMARY)};
${getFonts("BUTTON1_SEMIBOLD")}
`;
28 changes: 28 additions & 0 deletions src/components/common/Overlay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import colors from "@/styles/colors";
import { FC } from "react";
import styled from "styled-components";

interface OverlayProps {
/**
* 오버레이 클릭 핸들러
*/
onClick: () => void;
/**
* 오버레이 내부 컨텐츠
*/
children: React.ReactNode;
}

export const Overlay: FC<OverlayProps> = (props) => {
const { onClick, children } = props;
return <OverlayBox onClick={onClick}>{children}</OverlayBox>;
};

const OverlayBox = styled.div`
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: ${colors.BLACK_TRANSPARENT_SCALE[5]};
`;
13 changes: 7 additions & 6 deletions src/components/common/TapMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ interface TapMenuProps {
/**
* 버튼목록에 들어갈 값 배열
*/
options: [TapMenuOption, TapMenuOption];
options: TapMenuOption[];
/**
* 값 변경 콜백
*/
Expand All @@ -26,12 +26,13 @@ interface TapMenuProps {
const TapMenu: FC<TapMenuProps> = (props) => {
const { value, onChange, options } = props;
return (
<TabBox>
<TabBox count={options.length}>
{options.map((option) => {
return (
<TabButton
key={option.value}
$active={(value === option.value).toString()}
count={options.length}
onClick={() => {
onChange(option.value);
}}
Expand All @@ -46,18 +47,18 @@ const TapMenu: FC<TapMenuProps> = (props) => {

export default TapMenu;

const TabBox = styled.div`
padding-inline: 16px;
const TabBox = styled.div<{ count: number }>`
padding-inline: ${(props) => (props.count === 2 ? "16px" : "auto")};
border-bottom: 1px solid ${colors.GRAY[2]};
`;

const TabButton = styled.button<{ $active: string }>`
const TabButton = styled.button<{ $active: string; count: number }>`
cursor: pointer;
background-color: transparent;
border: none;
width: 50%;
padding-block: 15px;
${(props) => `
width: ${`${100 / props.count}%`};
${props.$active === "true" ? getFonts("H5_SEMIBOLD") : getFonts("H5_REGULAR")}
color: ${props.$active === "true" ? colors.FONT_LIGHT.PRIMARY : colors.FONT_LIGHT.SECONDARY};
border-bottom: solid 4px ${props.$active === "true" ? colors.ON.BASIC_LIGHT : colors.ON.BASIC_DARK};
Expand Down
40 changes: 40 additions & 0 deletions src/components/common/bottomSheet/BottomSheet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { FC } from "react";
import { Overlay } from "../Overlay";
import styled from "styled-components";
import colors from "@/styles/colors";

interface BottomSheetProps {
/**
* 오버레이 클릭 핸들러
*/
handleOverlayClick: () => void;
/**
* 바텀시트 컨텐츠
*/
children: React.ReactNode;
}

export const BottomSheet: FC<BottomSheetProps> = (props) => {
const { handleOverlayClick, children } = props;
return (
<Overlay onClick={handleOverlayClick}>
<BottomSheetBox onClick={(e) => e.stopPropagation()}>{children}</BottomSheetBox>
</Overlay>
);
};

const BottomSheetBox = styled.div`
box-sizing: border-box;
position: fixed;
bottom: 0;
left: 50%;
translate: -50%;
background-color: ${colors.WHITE};
border-radius: 16px 16px 0 0;
padding: 20px 16px 20px 16px;
width: 375px;
@media (max-width: 375px) {
width: 100vw;
}
`;
8 changes: 6 additions & 2 deletions src/components/common/bottomSheet/BottomSheetGuide.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,21 @@ interface BottomSheetGuideProps {
* 가이드 컨텐츠(p, span으로 구성)
*/
children: ReactNode;
/**
* 닫기 버튼 클릭 핸들러
*/
handleClose: () => void;
}

export const BottomSheetGuide: FC<BottomSheetGuideProps> = (props) => {
const { title, children } = props;
const { title, children, handleClose } = props;
return (
<>
<BottomSheetTitle>
<h2>{title}</h2>
</BottomSheetTitle>
<BottomSheetGuideContent>{children}</BottomSheetGuideContent>
<Button color="secondary" fill={false} width="100%" font="BUTTON1_REGULAR">
<Button color="secondary" fill={false} width="100%" font="BUTTON1_REGULAR" onClick={handleClose}>
닫기
</Button>
</>
Expand Down
Loading

0 comments on commit 3c0c827

Please sign in to comment.