Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
d443af1
#24 chore: Mui date pickers와 dayjs 라이브러리 추가
altmit Nov 27, 2024
8e555ba
#24 refactor: Fetcher에 Delete 옵션 data 추가
altmit Nov 27, 2024
03cfe15
#24 refactor: Background color 모바일, 데스크탑 구분으로 수정
altmit Nov 27, 2024
9070e05
#24 feat: Breadcrumb 추가
altmit Nov 27, 2024
4a7391e
#24 feat: DatePicker, AsyncButton 컴포넌트 추가
altmit Nov 27, 2024
e987e32
#24 feat: Pagination 컴포넌트 추가
altmit Nov 27, 2024
1319975
#24 refactor: PortfoliosDropdown 컴포넌트 리팩터링
altmit Nov 27, 2024
316c1e6
#24 feat: Select 컴포넌트 추가
altmit Nov 27, 2024
efd2a23
#24 feat: Table 컴포넌트 추가
altmit Nov 27, 2024
7ae8eab
#24 refactor: Cookies 파라미터 Type 변경
altmit Nov 27, 2024
7fadcf7
#24 feat: 증권사 이미지로고 object 추가
altmit Nov 27, 2024
469302e
#24 feat: Portfolio API 일부 추가
altmit Nov 27, 2024
6540c6f
#24 feat: Portfolio 페이지 내에서 사용하는 Chart 컴포넌트 추가
altmit Nov 27, 2024
256bd2f
#24 feat: RealtimeValue 컴포넌트 추가
altmit Nov 27, 2024
871d109
#24 feat: utils 함수 추가
altmit Nov 27, 2024
fa35a37
#24 feat: Portfolio 페이지 내에서 사용하게될 customHook 추가
altmit Nov 27, 2024
fe8f99a
#24 feat: Portfolio Holding 관련 컴포넌트 추가
altmit Nov 27, 2024
cf1354c
#24 feat: Portfolio Overview 관련 컴포넌트 추가
altmit Nov 27, 2024
c66b6dc
#24 feat: Skeletons와 ErrorFallback 추가
altmit Nov 27, 2024
42b1fc5
#24 feat: Portfolio page와 MainPanel, ChartPanel 추가
altmit Nov 27, 2024
9f5d0ed
#24 refactor: Fetcher와 Generator에서 delete가 body를 받는 경우를 고려하지 못해서 수정
altmit Nov 27, 2024
8487b77
#24 feat: Drawer에서 사용하는 Transition 추가
altmit Nov 28, 2024
8a52339
#24 refactor: Zustand 버전업으로 인한 기존 문법 변경
altmit Nov 28, 2024
bcfbc48
#24 refactor: 서버렌더링에서 클라이언트 렌더링으로 변경
altmit Nov 28, 2024
388eff7
#24 feat: Portfolio 추가시에 사용되는 유틸함수 추가
altmit Nov 28, 2024
680bdf1
#24 refactor: Portfolio Dropdown에 포트폴리오 추가 Dialog 추가
altmit Nov 28, 2024
a762a70
#24 feat: Portfolio 관련 query 추가
altmit Nov 28, 2024
2b2957d
#24 refactor: query에서 Delete일 때 body 값 전달 방식 변경
altmit Nov 28, 2024
ed6e1dc
#24 feat: Portfolio Dialog 관련 컴포넌트들 추가
altmit Nov 28, 2024
06703d6
#24 feat: Portfolio Page 관련 Skeleton, errorFallback 추가
altmit Nov 28, 2024
2e619cb
#24 refactor: Dialog 주석처리 되어 있던 부분 추가
altmit Nov 28, 2024
34bbc56
#24 refactor: Theme으로 적용되던 color DesignSystem으로 변경
altmit Nov 28, 2024
8555a9d
#24 refactor: 서버렌더링으로 받고 있던 data 각 컴포넌트로 클라이언트 렌더링으로 변경
altmit Nov 28, 2024
05d7b9a
#24 refactor: Set, Map을 사용하기 위해서 compilerOptions에 downlevelIteration 추가
altmit Nov 28, 2024
c105810
#24 refactor: TablePagination import 추가
altmit Nov 28, 2024
2f2c767
#24 refactor: Cookies type 원복
altmit Nov 28, 2024
aaea6ed
#24 refactor: 아직 사용하지 않는 Error 주석처리
altmit Nov 28, 2024
a4a45a0
#24 refactor: 모바일 구현 이전 필요한 값들 주석 처리
altmit Nov 28, 2024
e7d5331
#24 refactor: 미사용 값들 주석 처리 및 컴포넌트 익명함수 제거
altmit Nov 28, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
897 changes: 130 additions & 767 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@
"@lukemorales/query-key-factory": "^1.3.4",
"@mui/material": "^6.1.0",
"@mui/material-nextjs": "^6.1.0",
"@mui/x-date-pickers": "^7.22.3",
"@tanstack/react-query": "^5.55.4",
"@tanstack/react-query-devtools": "^5.59.15",
"chokidar": "^4.0.1",
"dayjs": "^1.11.13",
"lightweight-charts": "^4.2.1",
"next": "14.2.10",
"react": "^18",
Expand Down
2 changes: 1 addition & 1 deletion scripts/utils/api-router-generator-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const createApiFiles = (methodDetails: MethodDetails[]) => {
// Method 별 처리
const methodHandlers = methods
.map((method) => {
const hasBody = ["POST", "PUT", "PATCH"].includes(method);
const hasBody = ["POST", "PUT", "PATCH", "DELETE"].includes(method);
const bodyContent = hasBody ? ", req.body" : "";

return `
Expand Down
5 changes: 3 additions & 2 deletions src/api/fetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ const addCookiesToHeaders = (
};
};

// 데이터를 받지 않는 요청 (GET, DELETE)
// 데이터를 받지 않는 요청 (GET)
const requestWithoutData = async <T>(
url: string,
method: string,
Expand Down Expand Up @@ -132,9 +132,10 @@ const createFetcher = (
}),
delete: <T>(
url: string,
data?: Record<string, unknown> | FormData,
options?: FetcherOptions
): Promise<FetcherResponse<T>> =>
requestWithoutData<T>(`${baseURL}${url}`, "DELETE", {
requestWithData<T>(`${baseURL}${url}`, "DELETE", data, {
...defaultOptions,
...options,
}),
Expand Down
5 changes: 4 additions & 1 deletion src/components/BasePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ const StyledBasePage = styled.div<{ $isMobile: boolean; $isIOSPWA: boolean }>`
? "64px"
: "0"};

background-color: ${designSystem.color.neutral.white};
background-color: ${({ $isMobile }) =>
$isMobile
? designSystem.color.neutral.white
: designSystem.color.neutral.gray50};
`;

const Main = styled.main<{ $isMobile: boolean }>`
Expand Down
67 changes: 67 additions & 0 deletions src/components/Breadcrumb.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import designSystem from "@/styles/designSystem";
import { useRouter } from "next/router";
import styled from "styled-components";
import { Icon } from "./Icon";

type Props = {
depthData: {
name: string;
url: string;
}[];
};

export default function Breadcrumb({ depthData }: Props) {
const router = useRouter();

const lastIndex = depthData.length - 1;

return (
<StyledBreadcrumb>
{depthData.map((data, index) => {
const { name, url } = data;
return (
<div key={index}>
<DepthTitle
$isLast={lastIndex === index}
onClick={() => router.push(url)}>
{name}
</DepthTitle>
{index !== depthData.length - 1 && (
<Icon icon="chevron-right" color="gray400" size={12} />
)}
</div>
);
})}
</StyledBreadcrumb>
);
}

const StyledBreadcrumb = styled.div`
display: flex;

gap: 2.5px;
${designSystem.font.title5};

> div {
display: flex;
align-items: center;
justify-content: flex-start;
gap: 2.5px;
${designSystem.font.title5};
color: ${designSystem.color.neutral.gray400};
}
`;

const DepthTitle = styled.span<{ $isLast: boolean }>`
text-decoration: ${({ $isLast }) => ($isLast ? "none" : "underline")};
text-decoration-color: ${designSystem.color.neutral.gray600};
color: ${({ $isLast }) =>
$isLast
? designSystem.color.neutral.gray800
: designSystem.color.neutral.gray600};

&:hover {
color: ${designSystem.color.neutral.gray800};
cursor: pointer;
}
`;
48 changes: 48 additions & 0 deletions src/components/Buttons/AsyncButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import designSystem from "@/styles/designSystem";
import Spinner from "../Spinner";
import Button, { ButtonProps } from "./Button";

type AsyncButtonProps = {
isPending: boolean;
} & ButtonProps;

export default function AsyncButton({
isPending,
children,
...props
}: AsyncButtonProps) {
return (
<Button {...props}>
{isPending ? (
<Spinner
size={calcSpinnerSize(props.size)}
color={determineSpinnerColor(props.variant)}
/>
) : (
children
)}
</Button>
);
}

const calcSpinnerSize = (size: ButtonProps["size"]) => {
switch (size) {
case "h24":
return 12;
case "h32":
return 15;
case "h44":
return 20;
}
};

const determineSpinnerColor = (variant: ButtonProps["variant"]) => {
switch (variant) {
case "primary":
return designSystem.color.neutral.white;
case "secondary":
return designSystem.color.primary.blue200;
case "tertiary":
return designSystem.color.neutral.gray400;
}
};
81 changes: 81 additions & 0 deletions src/components/DatePicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import useResponsiveLayout from "@/hooks/useResponsiveLayout";
import designSystem, { parseFontString } from "@/styles/designSystem";
import { ThemeProvider, createTheme } from "@mui/material";
import { DesktopDatePicker } from "@mui/x-date-pickers";
import { Dayjs } from "dayjs";
import { Icon } from "./Icon";

type SizeType = "small" | "big";

type Props = {
size: SizeType;
disabled?: boolean;
value: Dayjs | null;
onChange: (newVal: Dayjs | null) => void;
};

export default function DatePicker({
size,
disabled = false,
value,
onChange,
}: Props) {
const { isMobile } = useResponsiveLayout();

return (
<ThemeProvider theme={datePickerTheme(size, isMobile)}>
<DesktopDatePicker
disabled={disabled}
value={value}
onChange={onChange}
format="YYYY-MM-DD"
disableOpenPicker={false}
slotProps={{
textField: { placeholder: "매입 날짜" },
}}
slots={{
openPickerIcon: () => {
return <Icon icon="calendar" color="gray600" size={16} />;
},
}}
/>
</ThemeProvider>
);
}

const datePickerTheme = (size: SizeType, isMobile: boolean) => {
const WIDTH_SIZE = isMobile ? "100%" : size === "small" ? "127px" : "352px";
const HEIGHT_SIZE = isMobile
? size === "small"
? "32px"
: "48px"
: size === "small"
? "24px"
: "32px";

return createTheme({
components: {
MuiInputBase: {
styleOverrides: {
root: {
"width": WIDTH_SIZE,
"height": HEIGHT_SIZE,
"padding": "0 0 0 8px",
...parseFontString(designSystem.font.body3),
"&:hover, &:focus-within": {
fieldset: {
border: `1px solid ${designSystem.color.primary.blue500} !important`,
},
},
"fieldset": {
border: `1px solid ${designSystem.color.neutral.gray200}`,
},
},
input: {
padding: "0 !important",
},
},
},
},
});
};
42 changes: 42 additions & 0 deletions src/components/Pagination/LabelRowsPerPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Icon } from "@/components/Icon";
import designSystem from "@/styles/designSystem";
import styled from "styled-components";

type Props = {
count: number;
startRow: number | undefined;
endRow: number | undefined;
};

export function LabelRowsPerPage({ count, endRow, startRow }: Props) {
return (
<LabelRowsPerPageWrapper>
<StyledLabelRowsPerPage>
전체 <span>{count}</span> 중{" "}
<span>
{startRow && endRow
? endRow === 1
? 1
: `${startRow}-${endRow}`
: count}
</span>
</StyledLabelRowsPerPage>
<Icon icon="divider" size={12} color="gray100" />
</LabelRowsPerPageWrapper>
);
}

const LabelRowsPerPageWrapper = styled.span`
display: flex;
align-items: center;
`;

const StyledLabelRowsPerPage = styled.span`
margin-right: 8px;
${designSystem.font.body3};
color: ${designSystem.color.neutral.gray600};

> span {
color: ${designSystem.color.neutral.gray900};
}
`;
81 changes: 81 additions & 0 deletions src/components/Pagination/Pagination.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import useResponsiveLayout from "@/hooks/useResponsiveLayout";
import designSystem from "@/styles/designSystem";
import {
Pagination as MuiPagination,
PaginationItem,
paginationClasses,
} from "@mui/material";
import { ChangeEvent } from "react";
import styled from "styled-components";
import { Icon } from "../Icon";

type Props = {
count: number;
page: number;
onPageChange: (event: ChangeEvent<unknown>, page: number) => void;
};

export default function Pagination({ count, page, onPageChange }: Props) {
const { isMobile } = useResponsiveLayout();

return (
<StyledPagination
$isMobile={isMobile}
count={count}
page={page}
onChange={(event: ChangeEvent<unknown>, newPage: number) => {
if (page === newPage) return;

if (isMobile) {
window.scroll({ top: 0 });
}
onPageChange(event, newPage - 1);
}}
shape="rounded"
renderItem={(item) => (
<PaginationItem
slots={{
previous: () => (
<Icon icon="chevron-left" size={16} color="gray600" />
),
next: () => <Icon icon="chevron-right" size={16} color="gray600" />,
}}
{...item}
/>
)}
/>
);
}

const StyledPagination = styled(MuiPagination)<{ $isMobile: boolean }>`
display: flex;
justify-content: center;
height: ${({ $isMobile }) => ($isMobile ? "32px" : "24px")};

.${paginationClasses.ul} {
gap: 8px;

> li {
width: ${({ $isMobile }) => ($isMobile ? "32px" : "24px")};
height: ${({ $isMobile }) => ($isMobile ? "32px" : "24px")};

> button {
width: ${({ $isMobile }) => ($isMobile ? "32px" : "24px")};
min-width: ${({ $isMobile }) => ($isMobile ? "32px" : "24px")};
height: ${({ $isMobile }) => ($isMobile ? "32px" : "24px")};
margin: 0;
padding: 0;
${designSystem.font.body3};

&.Mui-selected {
background-color: ${designSystem.color.primary.blue50};
color: ${designSystem.color.primary.blue500};
}

&:hover {
background-color: ${designSystem.color.neutral.gray50};
}
}
}
}
`;
Loading