Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

어드민 원서 상세조회 페이지 개발 #412

Merged
merged 9 commits into from
Sep 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions apps/admin/next.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
transpilePackages: ['@maru/theme'],
images: {
domains: ['s3.ap-northeast-2.amazonaws.com'],
},
};

module.exports = nextConfig;
50 changes: 50 additions & 0 deletions apps/admin/src/app/form/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
'use client';

import FormDetailContent from '@/components/form/FormDetailContent/FormDetailContent';
import { ROUTES } from '@/constants/common/constant';
import AppLayout from '@/layouts/AppLayout';
import { IconArrowLeft } from '@maru/icon';
import { color, font } from '@maru/theme';
import { Loader } from '@maru/ui';
import { flex } from '@maru/utils';
import Link from 'next/link';
import { Suspense } from 'react';
import styled from 'styled-components';

interface Props {
params: { id: number };
}

const FormDetailPage = ({ params: { id } }: Props) => {
return (
<AppLayout>
<StyledFormDetail>
<DirectLink href={ROUTES.MAIN}>
<IconArrowLeft width={18} height={18} />
돌아가기
</DirectLink>
<Suspense fallback={<Loader />}>
<FormDetailContent id={id} />
</Suspense>
</StyledFormDetail>
</AppLayout>
);
};

export default FormDetailPage;

const StyledFormDetail = styled.div`
position: relative;
${flex({ flexDirection: 'column' })}
gap: 24px;
width: 100%;
min-height: 100vh;
padding: 48px 60px 82px;
`;

const DirectLink = styled(Link)`
${flex({ alignItems: 'center' })}
gap: 2px;
${font.p3}
color: ${color.gray600};
`;
15 changes: 15 additions & 0 deletions apps/admin/src/components/common/DataBox/DataBox.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { Meta, StoryObj } from "@storybook/react";
import DataBox from "./DataBox";

export default {
component: DataBox,
title: "DataBox",
tags: ["autodocs"],
} satisfies Meta<typeof DataBox>;

export const Default: StoryObj<typeof DataBox> = {
args: {
label: '이름',
data: '문경자'
},
};
44 changes: 44 additions & 0 deletions apps/admin/src/components/common/DataBox/DataBox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { color } from '@maru/theme';
import { Text } from '@maru/ui';
import { flex } from '@maru/utils';
import styled from 'styled-components';

interface Props {
label: string;
data: string | number;
}

const DataBox = ({ label, data }: Props) => {
return (
<StyledDataBox>
<Text fontType="H4" color={color.gray900}>
{label}
</Text>
<DataUnderlineBox>
<Text fontType="p2" color={color.gray900}>
{data}
</Text>
</DataUnderlineBox>
</StyledDataBox>
);
};

export default DataBox;

const StyledDataBox = styled.div`
${flex({ flexDirection: 'column' })}
gap: 16px;
width: 604px;
height: 122px;
padding: 24px;
border-radius: 12px;
border: 1px solid ${color.gray200};
background: ${color.white};
`;

const DataUnderlineBox = styled.div`
${flex({ alignItems: 'flex-start' })}
width: 360px;
height: 30px;
border-bottom: 1px solid ${color.gray200};
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { FORM_DETAIL_STEP_LIST } from '@/constants/form/data';
import { color } from '@maru/theme';
import { Column, Row, UnderlineButton } from '@maru/ui';
import { flex } from '@maru/utils';
import { useState } from 'react';
import styled from 'styled-components';
import FormStatus from './FormStatus/FormStatus';
import Profile from './Profile/Profile';

interface Props {
id: number;
}

const FormDetailContent = ({ id }: Props) => {
const [currentFormDetailStep, setCurrentFormDetailStep] = useState('지원자 정보');

const handleFormDetailStepButtonClick = (formDetailStep: string) => {
setCurrentFormDetailStep(formDetailStep);
};

return (
<StyledFormDetailContent>
<Row gap={48}>
<Column gap={36}>
<Profile id={id} />
<FormStatus id={id} />
</Column>
<Column gap={48}>
<NavigationBar>
{FORM_DETAIL_STEP_LIST.map((formDetailStep, index) => (
<UnderlineButton
key={`form-detail-step ${index}`}
active={formDetailStep === currentFormDetailStep}
onClick={() => handleFormDetailStepButtonClick(formDetailStep)}>
{formDetailStep}
</UnderlineButton>
))}
</NavigationBar>
</Column>
</Row>
</StyledFormDetailContent>
);
};

export default FormDetailContent;

const StyledFormDetailContent = styled.div`
display: flex;
gap: 48px;
width: 100%;
height: 900px;
`;

const NavigationBar = styled.div`
${flex({ alignItems: 'center' })}
width: 100%;
height: 60px;
background-color: ${color.white};
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { useFormListQuery } from '@/services/form/queries';
import { color } from '@maru/theme';
import { Button, Column, Row, Text } from '@maru/ui';
import { flex } from '@maru/utils';
import styled from 'styled-components';

interface Props {
id: number;
}

const FormStatus = ({ id }: Props) => {
const { data: formList } = useFormListQuery();

const formDetailData = formList?.filter((form) => form.id === Number(id))[0];
const getStatusColor = (status: boolean | null) => {
return typeof status !== 'boolean' ? color.gray600 : status ? color.maruDefault : color.red;
};

const getStatusString = (status: boolean | null, trueString: string, falseString: string) => {
return typeof status !== 'boolean' ? '미정' : status ? trueString : falseString;
};

return (
<StyledFormStatus>
<FormStatusBox>
<Row gap={100} style={{ padding: '0 24px' }}>
<Text fontType="p2" color={color.gray900} width={60}>
제출 서류
</Text>
{formDetailData ? (
<Text fontType="p2" color={getStatusColor(formDetailData.hasDocument)}>
{getStatusString(formDetailData.hasDocument, '제출', '미제출')}
</Text>
) : null}
</Row>
<Line />
<Row gap={100} style={{ padding: '0 24px' }}>
<Text fontType="p2" color={color.gray900} width={60}>
1차 결과
</Text>
{formDetailData ? (
<Text fontType="p2" color={getStatusColor(formDetailData.firstRoundPassed)}>
{getStatusString(formDetailData.firstRoundPassed, '합격', '불합격')}
</Text>
) : null}
</Row>
<Line />
<Row gap={100} style={{ padding: '0 24px' }}>
<Text fontType="p2" color={color.gray900} width={60}>
최종 점수
</Text>
<Text
fontType="p2"
width={80}
color={
typeof formDetailData?.totalScore !== 'number'
? color.gray600
: color.gray900
}>
{typeof formDetailData?.totalScore !== 'number'
? '미정'
: formDetailData?.totalScore}
</Text>
</Row>
<Line />
<Row gap={100} style={{ padding: '0 24px' }}>
<Text fontType="p2" color={color.gray900} width={60}>
2차 결과
</Text>
{formDetailData ? (
<Text
fontType="p2"
color={getStatusColor(formDetailData.secondRoundPassed)}>
{getStatusString(formDetailData.secondRoundPassed, '합격', '불합격')}
</Text>
) : null}
</Row>
</FormStatusBox>
<Column gap={8}>
<Button size="SMALL">원서 상태 변경하기</Button>
<Button size="SMALL" option="SECONDARY">
제출 서류 조회하기
</Button>
</Column>
</StyledFormStatus>
);
};

export default FormStatus;

const StyledFormStatus = styled.div`
${flex({ flexDirection: 'column' })}
gap: 16px;
width: 280px;
`;

const FormStatusBox = styled.div`
${flex({ flexDirection: 'column' })}
gap: 12px;
padding: 16px 0;
border-radius: 12px;
border: 1px solid ${color.gray200};
`;

const Line = styled.div`
width: 100%;
height: 1px;
background: ${color.gray200};
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { FORM_TYPE } from '@/constants/main/constants';
import { useFormDetailQuery } from '@/services/form/queries';
import { IconBadge, IconCall, IconPerson, IconSchool } from '@maru/icon';
import { color } from '@maru/theme';
import { Column, Row, Text } from '@maru/ui';
import { flex } from '@maru/utils';
import Image from 'next/image';
import styled from 'styled-components';

interface Props {
id: number;
}

const Profile = ({ id }: Props) => {
const { data: formDetailData } = useFormDetailQuery(id);

return (
<StyledProfile>
<ProfileImageBox>
{formDetailData?.applicant.identificationPictureUri ? (
<ProfileImage
src={formDetailData?.applicant.identificationPictureUri}
alt="profile-image"
width={280}
height={280}
style={{ objectFit: 'cover', objectPosition: 'top' }}
/>
) : null}
</ProfileImageBox>
<Column gap={16}>
<Text fontType="H2" color={color.gray900}>
{formDetailData?.applicant.name}
</Text>
<Column gap={8}>
<Row gap={10}>
<IconBadge width={24} height={24} />
<Text fontType="p2" color={color.gray900}>
{formDetailData?.applicant.name}
</Text>
</Row>
<Row gap={10}>
<IconPerson width={24} height={24} />
<Text fontType="p2" color={color.gray900}>
{formDetailData?.type ? FORM_TYPE[formDetailData.type] : null}
</Text>
</Row>
<Row gap={10}>
<IconSchool width={24} height={24} />
<Text fontType="p2" color={color.gray900}>
{formDetailData?.education.schoolName}
</Text>
</Row>
<Row gap={10}>
<IconCall width={24} height={24} />
<Text fontType="p2" color={color.gray900}>
{formDetailData?.applicant.phoneNumber}
</Text>
</Row>
</Column>
</Column>
</StyledProfile>
);
};

export default Profile;

const StyledProfile = styled.div`
${flex({ flexDirection: 'column' })}
gap: 8px;
width: 280px;
`;

const ProfileImageBox = styled.div`
width: 280px;
height: 280px;
`;

const ProfileImage = styled(Image)`
border-radius: 999px;
`;
Loading