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

[Feat] 지원서 작성 페이지 레이아웃 구성 #1251 #1258

Merged
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
c82748e
[Feat] Layout 코드 구조 수정
yoouyeon Feb 26, 2024
179eafd
[Feat] 지원하기 버튼 추가
yoouyeon Feb 28, 2024
bd09063
[Feat] useCheckRecruit 훅 추가
yoouyeon Feb 28, 2024
dd9203d
[Design] 지원하기 버튼 디자인 수정
yoouyeon Mar 4, 2024
ae6ba72
[Fix] mock api, useCheckRecruit 훅 수정
yoouyeon Mar 4, 2024
2e4b4a2
[Feat] 모집 페이지 레이아웃 설정
yoouyeon Mar 4, 2024
a3df62e
[Design] 레이아웃 수정
yoouyeon Mar 4, 2024
cb59fb7
[Chore] lottie-react 설치
yoouyeon Mar 4, 2024
dcf4c46
[Design] 메인 페이지 헤더 디자인 적용
yoouyeon Mar 4, 2024
09d40dc
[Feat] 모집 기간 아님 페이지 추가
yoouyeon Mar 4, 2024
aa8a642
[Feat] 홈으로 돌아가기 버튼 추가
yoouyeon Mar 4, 2024
fc1150c
[Chore] mock data 수정
yoouyeon Mar 4, 2024
ce8ede3
[Chore] 주석 수정
yoouyeon Mar 4, 2024
efc0469
[Chore] useCheckRecruit 수정
yoouyeon Mar 4, 2024
3193e8a
feat: 테스트용 recruit 페이지 추가
PHJoon Mar 4, 2024
a1b4ed5
feat: recruit - ApplicationForm 컴포넌트 기본 내용만 추가(작업중)
PHJoon Mar 4, 2024
528769c
Merge branch 'Feat/1243-유저-페이지-레이아웃-구성' into Feat/1251-지원서-작성-페이지-레이아…
PHJoon Mar 4, 2024
0dbeb6e
[feat] 테스트용 recruit 페이지 삭제
PHJoon Mar 4, 2024
b726ad6
[test] recruit detail mock api 추가
PHJoon Mar 4, 2024
f38f961
[test] recruit mock api 수정
PHJoon Mar 4, 2024
d5ccc44
[Feat] recruit detail 타입 추가
PHJoon Mar 4, 2024
cd2490b
[mod] recruit detail 관련 타입명 수정
PHJoon Mar 4, 2024
083da8f
[style] application form scss 추가
PHJoon Mar 5, 2024
382cae0
[mod] recruit detail mock api 수정
PHJoon Mar 5, 2024
d4878dc
[feat] applicationForm 컴포넌트 기본 레이아웃
PHJoon Mar 5, 2024
2b74c84
[feat] recruit/application 페이지 추가
PHJoon Mar 5, 2024
45175eb
[feat] userRecruitDetail 훅 추가
PHJoon Mar 5, 2024
ea2e929
[feat] checklist 요소 인터페이스 추가
PHJoon Mar 5, 2024
bdffacb
[Fix] IQuestionForm의 inputType 수정
PHJoon Mar 6, 2024
3b591ce
[Fix] useRecruitDetail - refetchOnWindowFocus false 설정
PHJoon Mar 6, 2024
783b483
Merge branch 'recruit' into Feat/1251-지원서-작성-페이지-레이아웃-구성
PHJoon Mar 6, 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
91 changes: 37 additions & 54 deletions components/Layout/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { useRouter } from 'next/router';
import { useRecoilValue } from 'recoil';
import { colorModeState } from 'utils/recoil/colorMode';
import { openCurrentMatchState } from 'utils/recoil/match';
import AdminReject from 'components/admin/AdminReject';
import AdminLayout from 'components/admin/Layout';
Expand All @@ -10,7 +9,7 @@ import Header from 'components/Layout/Header';
import HeaderStateContext from 'components/Layout/HeaderContext';
import MainPageProfile from 'components/Layout/MainPageProfile';
import Megaphone from 'components/Layout/MegaPhone';
import StyledButton from 'components/UI/StyledButton';
import RecruitLayout from 'components/recruit/RecruitLayout';
import Statistics from 'pages/statistics';
import useAnnouncementCheck from 'hooks/Layout/useAnnouncementCheck';
import useGetUserSeason from 'hooks/Layout/useGetUserSeason';
Expand All @@ -19,16 +18,16 @@ import useSetAfterGameModal from 'hooks/Layout/useSetAfterGameModal';
import { useUser } from 'hooks/Layout/useUser';
import useAxiosResponse from 'hooks/useAxiosResponse';
import styles from 'styles/Layout/Layout.module.scss';
import PlayButton from './PlayButton';
import UserLayout from './UserLayout';

type AppLayoutProps = {
children: React.ReactNode;
};

export default function AppLayout({ children }: AppLayoutProps) {
const user = useUser();
const colorMode = useRecoilValue(colorModeState);
const presentPath = useRouter().asPath;
const router = useRouter();
const openCurrentMatch = useRecoilValue(openCurrentMatchState);

useAxiosResponse();
Expand All @@ -37,58 +36,42 @@ export default function AppLayout({ children }: AppLayoutProps) {
useLiveCheck(presentPath);
useAnnouncementCheck(presentPath);

const onClickMatch = () => {
router.replace('/');
router.push(`/match`);
};
if (!user || !user.intraId) return null;

if (!user) return null;
if (presentPath.includes('/admin')) {
if (!user.isAdmin) return <AdminReject />;
return <AdminLayout>{children}</AdminLayout>;
}

return presentPath.includes('/admin') ? (
user.isAdmin ? (
<AdminLayout>{children}</AdminLayout>
) : (
<AdminReject />
)
) : (
<div className={styles.appContainer}>
<div
className={`${styles.background} ${
colorMode ? styles[colorMode.toLowerCase()] : styles.basic
} `}
>
<div>
{presentPath === '/statistics' && user.isAdmin ? (
<Statistics />
) : (
user.intraId && (
<>
<HeaderStateContext>
<Header />
</HeaderStateContext>
{presentPath !== '/match' &&
presentPath !== '/manual' &&
presentPath !== '/store' && (
<div className={styles.buttonContainer}>
<div className={styles.buttonWrapper}>
<StyledButton onClick={onClickMatch} width={'5.5rem'}>
Play
</StyledButton>
</div>
</div>
)}
<div className={styles.topInfo}>
<Megaphone />
{openCurrentMatch && <CurrentMatch />}
{presentPath === '/' && <MainPageProfile />}
</div>
{children}
<Footer />
</>
)
)}
</div>
if (presentPath.includes('/recruit')) {
return (
<RecruitLayout>
<div>{children}</div>
</RecruitLayout>
);
}

// NOTE : 외부 툴을 사용해보고 외부 툴로 대체가 가능하다면 삭제 예정
if (presentPath === '/statistics' && user.isAdmin)
return (
<UserLayout>
<Statistics />
</UserLayout>
);

return (
<UserLayout>
<HeaderStateContext>
<Header />
</HeaderStateContext>
<PlayButton />
<div className={styles.topInfo}>
<Megaphone />
{openCurrentMatch && <CurrentMatch />}
{presentPath === '/' && <MainPageProfile />}
</div>
</div>
{children}
<Footer />
</UserLayout>
);
}
22 changes: 21 additions & 1 deletion components/Layout/MenuBar/MenuBarElement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ import CurrentMatchEmoji from 'public/image/menu_currentMatch.svg';
import HallOfFameEmoji from 'public/image/menu_halloffame.svg';
import ManualEmoji from 'public/image/menu_manual.svg';
import RankingEmoji from 'public/image/menu_ranking.svg';
import RecruitEmoji from 'public/image/menu_recruit.svg';
import ReportEmoji from 'public/image/menu_report.svg';
import SignOutEmoji from 'public/image/menu_signOut.svg';
import StatisticsEmoji from 'public/image/menu_statistics.svg';
import { useUser } from 'hooks/Layout/useUser';
import useCheckRecruit from 'hooks/recruit/useCheckRecruit';
import useAxiosGet from 'hooks/useAxiosGet';
import styles from 'styles/Layout/MenuBar.module.scss';

Expand Down Expand Up @@ -69,10 +71,20 @@ const MenuItem = ({ itemName, onClick }: menuItemProps) => {
name: '관리자',
svg: <AdminEmoji />,
},
Recruit: {
name: '지원하기',
svg: <RecruitEmoji />,
},
};
return (
<div className={styles.menuItem} onClick={onClick}>
<div className={styles.imageWrapper}>{menuList[itemName].svg}</div>
<div
className={
itemName === 'Recruit' ? styles.recruit : styles.imageWrapper
}
>
{menuList[itemName].svg}
</div>
<div className={styles.menuText}>{menuList[itemName].name}</div>
</div>
);
Expand All @@ -89,6 +101,7 @@ const MenuLink = ({ link, onClick, itemName }: MenuLinkProps) => {
export const MainMenu = () => {
const HeaderState = useContext<HeaderContextState | null>(HeaderContext);
const setModal = useSetRecoilState<Modal>(modalState);
const { isRecruiting } = useCheckRecruit();

const getAnnouncementHandler = useAxiosGet<any>({
url: '/pingpong/announcement',
Expand All @@ -106,6 +119,13 @@ export const MainMenu = () => {

return (
<nav className={styles.mainMenu}>
{isRecruiting && (
<MenuLink
link='/recruit'
itemName='Recruit'
onClick={HeaderState?.resetOpenMenuBarState}
/>
)}
<MenuLink
link='/store'
itemName='Store'
Expand Down
31 changes: 31 additions & 0 deletions components/Layout/PlayButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { useRouter } from 'next/router';
import StyledButton from 'components/UI/StyledButton';
import styles from 'styles/Layout/Layout.module.scss';

const PlayButton = () => {
const presentPath = useRouter().asPath;
const router = useRouter();

const onClickMatch = () => {
router.replace('/');
router.push(`/match`);
};

if (
presentPath === '/match' ||
presentPath === '/manual' ||
presentPath === '/store'
)
return null;
return (
<div className={styles.buttonContainer}>
<div className={styles.buttonWrapper}>
<StyledButton onClick={onClickMatch} width={'5.5rem'}>
Play
</StyledButton>
</div>
</div>
);
};

export default PlayButton;
24 changes: 24 additions & 0 deletions components/Layout/UserLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { useRecoilValue } from 'recoil';
import { colorModeState } from 'utils/recoil/colorMode';
import styles from 'styles/Layout/Layout.module.scss';

const UserLayout = ({ children }: { children: React.ReactNode }) => {
const colorMode = useRecoilValue(colorModeState);

return (
<div className={styles.appContainer}>
<div
className={`${styles.background} ${
colorMode ? styles[colorMode.toLowerCase()] : styles.basic
} `}
>
<div>
{/* TODO : 상위 div 컴포넌트 필요한지 다시 확인해보기 */}
{children}
</div>
</div>
</div>
);
};

export default UserLayout;
124 changes: 124 additions & 0 deletions components/recruit/ApplicationForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import {
Button,
Checkbox,
FormControl,
FormControlLabel,
FormGroup,
Radio,
RadioGroup,
TextField,
} from '@mui/material';
import { ICheck, IQuestionForm } from 'types/recruit/recruitments';
import useRecruitDetail from 'hooks/recruit/useRecruitDetail';
import applicationStyle from 'styles/recruit/application.module.scss';

export default function ApplicationForm({ id }: { id: number }) {
const { data, isLoading } = useRecruitDetail({ id });

if (isLoading) {
return <div>로딩중...</div>;
}

if (!data || Object.keys(data).length === 0) {
return <div>지원서 항목이 없습니다</div>;
}

return (
<div>
<div className={applicationStyle.titleContainer}>{data.title}</div>
<div className={applicationStyle.bodyContainer}>
{data.form.map((form: IQuestionForm, index: number) => (
<div className={applicationStyle.questionContainer} key={index}>
{form.inputType === 'TEXT' ? (
<TextForm question={form.question} />
) : form.inputType === 'SINGLE_CHECK' ? (
<SingleCheckForm
question={form.question}
checkList={form.checkList}
/>
) : form.inputType === 'MULTI_CHECK' ? (
<MultiCheckForm
question={form.question}
checkList={form.checkList}
/>
) : (
<div>유효하지 않은 폼</div>
)}
</div>
))}
</div>
<div className={applicationStyle.btnContainer}>
<Button
className={applicationStyle.submitBtn}
sx={{ borderRadius: '1rem', fontSize: '1.5rem' }}
variant='contained'
>
제출하기
</Button>
</div>
</div>
);
}

function TextForm({ question }: { question: string }) {
return (
<div>
<div className={applicationStyle.questionText}>{question}</div>
<TextField id='filled-basic' label='Filled' variant='filled' />
</div>
);
}

function SingleCheckForm({
question,
checkList,
}: {
question: string;
checkList: ICheck[] | undefined;
}) {
return (
<FormControl>
<div className={applicationStyle.questionText}>{question}</div>
<RadioGroup
aria-labelledby='radio-buttons-group-label'
name='radio-buttons-group'
>
{checkList?.map((check: ICheck) => {
return (
<FormControlLabel
key={check.id}
value={check.contents}
control={<Radio />}
label={check.contents}
/>
);
})}
</RadioGroup>
</FormControl>
);
}

function MultiCheckForm({
question,
checkList,
}: {
question: string;
checkList: ICheck[] | undefined;
}) {
return (
<div>
<div className={applicationStyle.questionText}>{question}</div>
<FormGroup>
{checkList?.map((check: ICheck) => {
return (
<FormControlLabel
key={check.id}
control={<Checkbox />}
label={check.contents}
/>
);
})}
</FormGroup>
</div>
);
}
Loading
Loading