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] 지원자페이지 구현 #1337

Merged
merged 6 commits into from
Apr 6, 2024
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
21 changes: 21 additions & 0 deletions components/admin/common/AdminTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,24 @@ export function AdminContent({
<div>{content?.toString()}</div>
);
}

export function DetailContentHover({
content,
maxLen,
}: {
content?: string;
maxLen: number;
}) {
if (!content) return <div>N/A</div>;

return content?.length > maxLen ? (
<div className={styles.tableBodyItemHover}>
<div className={styles.info}>
{(content?.toString() || '').slice(0, maxLen)}...
</div>
{/* <div className={`${styles.hoverInfo}`}>{content}</div> */}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분은 왜 주석인가요?

</div>
) : (
<div>{content?.toString()}</div>
);
}
Original file line number Diff line number Diff line change
@@ -1,115 +1,38 @@
import { useCallback, useEffect, useState } from 'react';
import { useSetRecoilState } from 'recoil';
import { useState } from 'react';
import {
Paper,
Table,
TableBody,
TableHead,
TableCell,
TableContainer,
TableRow,
} from '@mui/material';
import {
IrecruitUserTable,
Iquestion,
} from 'types/admin/adminRecruitmentsTypes';
// import { instanceInManage } from 'utils/axios';
import { mockInstance } from 'utils/mockAxios';
import { toastState } from 'utils/recoil/toast';
import {
AdminEmptyItem,
AdminTableHead,
AdminContent,
} from 'components/admin/common/AdminTable';
import PageNation from 'components/Pagination';
import styles from 'styles/admin/recruitments/Recruitments.module.scss';

//무한 스크롤로 변경
//필터 추가
/*
추가할 기능
가로세로 길이 조절
가로세로 위치 변경
*/
export interface IrecruitTable {
applications: IrecruitUserTable['applications'];
totalPage: number;
currentPage: number;
}
import useRecruitmentUserFilter from 'hooks/recruitments/useRecruitmentUserFilter';
import styles from 'styles/admin/recruitments/RecruitmentsUser.module.scss';
import FilterQptionsUI from './FilterOptions';
import renderTableCells from './renderTableCells';

const tableTitle: { [key: string]: string } = {
id: 'ID',
usedAt: '적용 시간',
title: '제목',
id: '',
intraId: 'intraId',
status: '상태',
detailRecruitment: '공고 상세보기',
detaillUser: '지원자 보기',
question: '질문',
};

function DetailRecruitUserList({ recruitId }: { recruitId: number }) {
const [recruitUserData, setRecruitUserData] = useState<IrecruitTable>({
applications: [],
totalPage: 0,
currentPage: 0,
});
const [currentPage, setCurrentPage] = useState<number>(1);
const setSnackBar = useSetRecoilState(toastState);

const getRecruitUserHandler = useCallback(async () => {
try {
// const res = await instanceInManage.get(
// `/recruitments/${recruitId}/applications`
// );
const id = recruitId;
const res = await mockInstance.get(`/admin/recruitments/${id}`);
setRecruitUserData({
applications: res.data.applications,
totalPage: res.data.totalPages,
currentPage: res.data.number + 1,
});
} catch (e: any) {
setSnackBar({
toastName: 'get recruitment',
severity: 'error',
message: `API 요청에 문제가 발생했습니다.`,
clicked: true,
});
}
}, [currentPage]);

useEffect(() => {
getRecruitUserHandler();
}, [currentPage]);

const renderTableCell = (
recruit: IrecruitUserTable['applications'][number]
) => {
return (
<TableRow className={styles.tableRow} key={recruit.applicationId}>
<TableCell className={styles.tableBodyItem}>
<AdminContent
content={recruit.intraId || ''}
maxLen={16}
detailTitle={recruit.applicationId.toString()}
detailContent={recruit.status || ''}
/>
</TableCell>
{recruit.form?.map((formItem: Iquestion, index: number) => (
<TableCell className={styles.tableBodyItem} key={index}>
<AdminContent
content={
formItem.answer ||
formItem.checkList?.map((item) => item.contents).join(', ') ||
''
}
maxLen={16}
/>
</TableCell>
))}
</TableRow>
);
};
const { recruitUserData, questions } = useRecruitmentUserFilter(
recruitId,
currentPage
);

if (!recruitUserData.applications.length) {
if (!recruitUserData.applications) {
return (
<TableContainer className={styles.tableContainer} component={Paper}>
<Table className={styles.table} aria-label='customized table'>
Expand All @@ -124,11 +47,25 @@ function DetailRecruitUserList({ recruitId }: { recruitId: number }) {

return (
<>
{FilterQptionsUI(recruitUserData.applications)}
<TableContainer className={styles.tableContainer} component={Paper}>
<Table className={styles.table} aria-label='customized table'>
<AdminTableHead tableName={'recruitUserList'} table={tableTitle} />
<TableHead className={styles.tableHeader}>
<TableRow>
<TableCell className={styles.tableHeaderItem}></TableCell>
<TableCell className={styles.tableHeaderItem}>intraId</TableCell>
<TableCell className={styles.tableHeaderItem}>status</TableCell>
{questions.map((question, index) => (
<TableCell className={styles.tableHeaderItem} key={index}>
{question}
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody className={styles.tableBody}>
{recruitUserData.applications.map(renderTableCell)}
{recruitUserData.applications.map((recruit) =>
renderTableCells(recruit, questions)
)}
</TableBody>
</Table>
</TableContainer>
Expand Down
79 changes: 79 additions & 0 deletions components/admin/recruitments/recruitmentsuser/FilterOptions.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import React, { useState, useEffect, useCallback } from 'react';
import {
FormControl,
Select,
OutlinedInput,
MenuItem,
ListItemText,
SelectChangeEvent,
} from '@mui/material';
import {
IcheckItem,
IrecruitUserTable,
} from 'types/admin/adminRecruitmentsTypes';
import useRecruitmentUserFilter from 'hooks/recruitments/useRecruitmentUserFilter';
import styles from 'styles/admin/recruitments/RecruitmentsUser.module.scss';
import RecruitSearchBar from './RecruitSearchBar';

const ITEM_HEIGHT = 48;
const ITEM_PADDING_TOP = 8;
const MenuProps = {
PaperProps: {
style: {
maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP,
width: 250,
},
},
};

function FilterQptionsUI(recruitUserData: IrecruitUserTable[]) {
const [answers, setAnswers] = useState<Array<IcheckItem>>([]);
const { checklistIds, handleChecklistChange } = useRecruitmentUserFilter();

useEffect(() => {
setAnswers(
recruitUserData.reduce((acc, recruit) => {
recruit.form.forEach((formItem) => {
if (formItem.inputType !== 'TEXT') {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

filter로 써도 좋을 것 같아요

formItem.checkedList?.forEach((item) => {
if (!acc.some((answer) => answer.checkId === item.checkId)) {
acc.push(item);
}
});
}
});
return acc;
}, [] as Array<IcheckItem>)
);
}, [recruitUserData]);

return (
<div className={styles.filterWrap}>
<div className={styles.searchWrap}>
<RecruitSearchBar />
</div>
<div className={styles.selectWrap}>
<FormControl sx={{ m: 1, width: 200 }}>
<Select
labelId='demo-multiple-checkbox-label'
id='demo-multiple-checkbox'
Comment on lines +58 to +59
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아이디에 demo 붙어 있는데, 임시로 조치한 부분이 아니면 제거하는 것이 좋을 것 같습니다.

multiple
value={checklistIds}
onChange={handleChecklistChange}
input={<OutlinedInput label='Tag' />}
renderValue={(selected) => selected.join(', ')}
MenuProps={MenuProps}
>
{answers.map((answer: IcheckItem, index) => (
<MenuItem key={index} value={answer.checkId}>
<ListItemText primary={answer.content} />
</MenuItem>
))}
</Select>
</FormControl>
</div>
</div>
);
}

export default FilterQptionsUI;
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
AdminTableHead,
} from 'components/admin/common/AdminTable';
import PageNation from 'components/Pagination';
import styles from 'styles/admin/recruitments/Recruitments.module.scss';
import styles from 'styles/admin/recruitments/RecruitmentsUser.module.scss';
import 'react-datepicker/dist/react-datepicker.css';

const tableTitle: { [key: string]: string } = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { GoSearch } from 'react-icons/go';
import { IoIosCloseCircle } from 'react-icons/io';
import useRecruitmentUserFilter from 'hooks/recruitments/useRecruitmentUserFilter';
import useSearchBar from 'hooks/useSearchBar';
import styles from 'styles/admin/common/AdminSearchBar.module.scss';

const MAX_SEARCH_LENGTH = 15;

export default function RecruitSearchBar() {
const { keyword, setKeyword, searchBarRef } = useSearchBar();

const { initSearch } = useRecruitmentUserFilter();

const adminhandleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
if (event.key === 'Enter') {
event.currentTarget.blur();
initSearch(keyword);
}
};

return (
<div className={styles.adminSearchBar} ref={searchBarRef}>
<input
type='text'
onChange={(e) => {
setKeyword(e.target.value);
}}
onKeyDown={adminhandleKeyDown}
placeholder='검색하기...'
maxLength={MAX_SEARCH_LENGTH}
value={keyword}
/>
<div className={styles.icons}>
{keyword ? (
<span
className={styles.reset}
onClick={() => {
initSearch();
setKeyword('');
}}
>
<IoIosCloseCircle />
</span>
) : (
<span
onClick={() => {
initSearch();
}}
>
<GoSearch />
</span>
)}
</div>
</div>
);
}
Loading