Skip to content

Commit 7105df4

Browse files
authored
Merge pull request #404 from Bamdoliro/develop
어드민
2 parents 52dbcf1 + d28d0cb commit 7105df4

File tree

18 files changed

+425
-31
lines changed

18 files changed

+425
-31
lines changed

apps/admin/src/app/page.tsx

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
'use client';
22

3+
import ButtonMenu from '@/components/common/ButtonMenu/ButtonMenu';
4+
import ButtonMenuItem from '@/components/common/ButtonMenu/ButtonMenuItem/ButtonMenuItem';
35
import FormList from '@/components/main/FormList/FormList';
46
import AppLayout from '@/layouts/AppLayout';
57
import initMockAPI from '@/mocks';
6-
import { useFormListQuery } from '@/services/form/queries';
7-
import { Button, Column, Row, SearchInput, Text } from '@maru/ui';
8+
import { IconCheckDocument, IconEditDocument, IconPrint, IconUpload } from '@maru/icon';
9+
import { color } from '@maru/theme';
10+
import { Column, Row, SearchInput, Text } from '@maru/ui';
811
import { flex } from '@maru/utils';
912
import { styled } from 'styled-components';
1013

@@ -13,19 +16,50 @@ if (process.env.NODE_ENV === 'development') {
1316
}
1417

1518
const MainPage = () => {
16-
const { data: formListData } = useFormListQuery();
17-
1819
return (
1920
<AppLayout>
2021
<StyledMainPage>
2122
<Text fontType="H1">원서 관리</Text>
2223
<Column gap={36}>
23-
<Row justifyContent="space-between" alignItems="center">
24-
<Text fontType="H5">{formListData?.length}</Text>
25-
<Row gap={8}>
26-
<SearchInput placeholder="통합 검색" />
27-
<Button size="SMALL">검색</Button>
28-
</Row>
24+
<Row justifyContent="space-between">
25+
<SearchInput placeholder="통합검색" />
26+
<ButtonMenu
27+
width={280}
28+
menuItemList={[
29+
<ButtonMenuItem>
30+
<IconCheckDocument
31+
color={color.gray600}
32+
width={24}
33+
height={24}
34+
/>
35+
<Text fontType="p2" color={color.gray900}>
36+
검토해야 하는 원서 모아보기
37+
</Text>
38+
</ButtonMenuItem>,
39+
<ButtonMenuItem>
40+
<IconEditDocument
41+
color={color.gray600}
42+
width={24}
43+
height={24}
44+
/>
45+
<Text fontType="p2" color={color.gray900}>
46+
2차 전형 점수 입력하기
47+
</Text>
48+
</ButtonMenuItem>,
49+
<ButtonMenuItem>
50+
<IconUpload color={color.gray600} width={24} height={24} />
51+
<Text fontType="p2" color={color.gray900}>
52+
명단 엑셀로 내보내기
53+
</Text>
54+
</ButtonMenuItem>,
55+
<ButtonMenuItem>
56+
<IconPrint color={color.gray600} width={24} height={24} />
57+
<Text fontType="p2" color={color.gray900}>
58+
원서 출력하기
59+
</Text>
60+
</ButtonMenuItem>,
61+
]}
62+
/>
2963
</Row>
3064
<FormList />
3165
</Column>
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { IconCheckDocument, IconEditDocument, IconPrint, IconUpload } from "@maru/icon";
2+
import { color } from "@maru/theme";
3+
import { Text } from "@maru/ui";
4+
import type { Meta, StoryObj } from "@storybook/react";
5+
import ButtonMenu from "./ButtonMenu";
6+
import ButtonMenuItem from "./ButtonMenuItem/ButtonMenuItem";
7+
8+
export default {
9+
component: ButtonMenu,
10+
title: "ButtonMenu",
11+
tags: ["autodocs"],
12+
} satisfies Meta<typeof ButtonMenu>;
13+
14+
export const Default: StoryObj<typeof ButtonMenu> = {
15+
args: {
16+
menuItemList: [
17+
<ButtonMenuItem>
18+
<IconCheckDocument
19+
color={color.gray600}
20+
width={24}
21+
height={24}
22+
/>
23+
<Text fontType="p2" color={color.gray900}>
24+
검토해야 하는 원서 모아보기
25+
</Text>
26+
</ButtonMenuItem>,
27+
<ButtonMenuItem>
28+
<IconEditDocument
29+
color={color.gray600}
30+
width={24}
31+
height={24}
32+
/>
33+
<Text fontType="p2" color={color.gray900}>
34+
2차 전형 점수 입력하기
35+
</Text>
36+
</ButtonMenuItem>,
37+
<ButtonMenuItem>
38+
<IconUpload
39+
color={color.gray600}
40+
width={24}
41+
height={24}
42+
/>
43+
<Text fontType="p2" color={color.gray900}>
44+
명단 엑셀로 내보내기
45+
</Text>
46+
</ButtonMenuItem>,
47+
<ButtonMenuItem>
48+
<IconPrint
49+
color={color.gray600}
50+
width={24}
51+
height={24}
52+
/>
53+
<Text fontType="p2" color={color.gray900}>
54+
원서 출력하기
55+
</Text>
56+
</ButtonMenuItem>,
57+
]
58+
},
59+
};
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { useBooleanState, useOutsideClick } from '@maru/hooks';
2+
import { IconArrowBottom, IconArrowTop } from '@maru/icon';
3+
import { color } from '@maru/theme';
4+
import { Text } from '@maru/ui';
5+
import { flex } from '@maru/utils';
6+
import { CSSProperties, ReactNode } from 'react';
7+
import styled, { css } from 'styled-components';
8+
9+
interface Props {
10+
width?: CSSProperties['width'];
11+
menuItemList: ReactNode[];
12+
}
13+
14+
const ButtonMenu = ({ width, menuItemList = [] }: Props) => {
15+
const {
16+
value: isOpen,
17+
setFalse: closeMenu,
18+
toggle: handleToggleButtonClick,
19+
} = useBooleanState();
20+
21+
const menuRef = useOutsideClick(closeMenu);
22+
23+
return (
24+
<div style={{ width: 120 }} ref={menuRef}>
25+
<StyledButtonMenu $isOpen={isOpen} onClick={handleToggleButtonClick}>
26+
<Text fontType="p2" color={color.gray700}>
27+
추가 기능
28+
</Text>
29+
{isOpen ? (
30+
<IconArrowTop color={color.gray600} width={24} height={24} />
31+
) : (
32+
<IconArrowBottom color={color.gray600} width={24} height={24} />
33+
)}
34+
</StyledButtonMenu>
35+
<MenuListBox $isOpen={isOpen}>
36+
<MenuList style={{ width }}>
37+
{menuItemList.map((menuItem) => (
38+
<MenuItem onClick={closeMenu}>{menuItem}</MenuItem>
39+
))}
40+
</MenuList>
41+
</MenuListBox>
42+
</div>
43+
);
44+
};
45+
46+
export default ButtonMenu;
47+
48+
const StyledButtonMenu = styled.div<{ $isOpen: boolean }>`
49+
${flex({ alignItems: 'center', justifyContent: 'space-between' })}
50+
width: 100%;
51+
height: 40px;
52+
padding: 0 10px 0 16px;
53+
background-color: ${color.white};
54+
border-radius: 6px;
55+
cursor: pointer;
56+
57+
${(props) =>
58+
props.$isOpen
59+
? css`
60+
border: 1px solid ${color.maruDefault};
61+
outline: 2px solid rgba(20, 112, 255, 0.25);
62+
`
63+
: css`
64+
border: 1px solid ${color.gray400};
65+
`}
66+
`;
67+
68+
const MenuListBox = styled.div<{ $isOpen: boolean }>`
69+
position: relative;
70+
display: ${(props) => (props.$isOpen ? 'block' : 'none')};
71+
`;
72+
73+
const MenuList = styled.div`
74+
z-index: 1;
75+
position: absolute;
76+
right: 0;
77+
margin-top: 8px;
78+
padding: 8px;
79+
min-width: 120px;
80+
background-color: ${color.white};
81+
border: 1px solid ${color.gray200};
82+
box-shadow: 0px 2px 8px 0px rgba(0, 0, 0, 0.08);
83+
border-radius: 12px;
84+
`;
85+
86+
const MenuItem = styled.div`
87+
width: 100%;
88+
height: 44px;
89+
cursor: pointer;
90+
`;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import type { Meta, StoryObj } from "@storybook/react";
2+
import ButtonMenuItem from "./ButtonMenuItem";
3+
4+
export default {
5+
component: ButtonMenuItem,
6+
title: "ButtonMenuItem",
7+
tags: ["autodocs"],
8+
} satisfies Meta<typeof ButtonMenuItem>;
9+
10+
export const Default: StoryObj<typeof ButtonMenuItem> = {
11+
args: {
12+
children: '이것은 버튼 메뉴 아이템입니다~~'
13+
},
14+
};
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { color } from '@maru/theme';
2+
import { flex } from '@maru/utils';
3+
import { ReactNode } from 'react';
4+
import styled from 'styled-components';
5+
6+
interface Props {
7+
children: ReactNode;
8+
}
9+
10+
const ButtonMenuItem = ({ children }: Props) => {
11+
return <StyledButtonMenuItem>{children}</StyledButtonMenuItem>;
12+
};
13+
14+
export default ButtonMenuItem;
15+
16+
const StyledButtonMenuItem = styled.div`
17+
${flex({ alignItems: 'center' })}
18+
gap: 12px;
19+
width: 100%;
20+
height: 44px;
21+
padding: 0 8px;
22+
23+
&:hover {
24+
background-color: ${color.gray100};
25+
}
26+
`;

apps/admin/src/components/main/FormList/FormList.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ const FormList = () => {
2121
school={item.school}
2222
status={item.status}
2323
type={item.type}
24+
totalScore={item.totalScore}
25+
hasDocument={item.hasDocument}
26+
firstRoundPassed={item.firstRoundPassed}
27+
secondRoundPassed={item.secondRoundPassed}
2428
/>
2529
))}
2630
</Column>

apps/admin/src/components/main/FormListHeader/FormListHeader.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,20 @@ const FormListHeader = () => {
2121
전형
2222
</Text>
2323
</Row>
24-
<Text fontType="p2" width={80}>
25-
상태
26-
</Text>
24+
<Row gap={48}>
25+
<Text fontType="p2" width={80}>
26+
제출서류
27+
</Text>
28+
<Text fontType="p2" width={80}>
29+
1차 결과
30+
</Text>
31+
<Text fontType="p2" width={80}>
32+
최종 점수
33+
</Text>
34+
<Text fontType="p2" width={80}>
35+
2차 결과
36+
</Text>
37+
</Row>
2738
</TableHeader>
2839
);
2940
};

apps/admin/src/components/main/FormListItem/FormListItem.tsx

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,29 @@
11
import TableItem from '@/components/common/TableItem/TableItem';
22
import { FORM_TYPE } from '@/constants/main/constants';
3-
import { FormStatus, FormType, GraduationType } from '@/types/main/client';
3+
import { Form, FormType } from '@/types/main/client';
4+
import { color } from '@maru/theme';
45
import { Row, Text } from '@maru/ui';
56

6-
interface Props {
7-
id: number;
8-
name: string;
9-
birthday: string;
10-
graduationType: GraduationType;
11-
school: string;
12-
status: FormStatus;
13-
type: FormType;
14-
}
7+
const FormListItem = ({
8+
id,
9+
name,
10+
birthday,
11+
graduationType,
12+
school,
13+
type,
14+
totalScore,
15+
hasDocument,
16+
firstRoundPassed,
17+
secondRoundPassed,
18+
}: Form) => {
19+
const getStatusColor = (status: boolean | null) => {
20+
return typeof status !== 'boolean' ? color.gray600 : status ? color.maruDefault : color.red;
21+
};
22+
23+
const getStatusString = (status: boolean | null, trueString: string, falseString: string) => {
24+
return typeof status !== 'boolean' ? '미정' : status ? trueString : falseString;
25+
};
1526

16-
const FormListItem = ({ id, name, birthday, graduationType, school, type, status }: Props) => {
1727
return (
1828
<TableItem>
1929
<Row gap={48}>
@@ -33,9 +43,23 @@ const FormListItem = ({ id, name, birthday, graduationType, school, type, status
3343
{FORM_TYPE[type as FormType]}
3444
</Text>
3545
</Row>
36-
<Text fontType="p2" width={80}>
37-
{status}
38-
</Text>
46+
<Row gap={48}>
47+
<Text fontType="p2" width={80} color={getStatusColor(hasDocument)}>
48+
{getStatusString(hasDocument, '제출', '미제출')}
49+
</Text>
50+
<Text fontType="p2" width={80} color={getStatusColor(firstRoundPassed)}>
51+
{getStatusString(firstRoundPassed, '합격', '불합격')}
52+
</Text>
53+
<Text
54+
fontType="p2"
55+
width={80}
56+
color={typeof totalScore !== 'number' ? color.gray600 : color.gray900}>
57+
{typeof totalScore !== 'number' ? '미정' : totalScore}
58+
</Text>
59+
<Text fontType="p2" width={80} color={getStatusColor(secondRoundPassed)}>
60+
{getStatusString(secondRoundPassed, '합격', '불합격')}
61+
</Text>
62+
</Row>
3963
</TableItem>
4064
);
4165
};

0 commit comments

Comments
 (0)