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/make feedback page #659

Merged
merged 24 commits into from
Feb 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
aae559d
[Refactor] add tableType, constants for table components #623
mike2ox Feb 10, 2023
3e9ef83
[Test] add feedback mock api #623
mike2ox Feb 10, 2023
2890fca
[Style] update feedback table column #646
mike2ox Feb 10, 2023
223e4c4
[Feat] create feedback table template #646
mike2ox Feb 10, 2023
391982f
[Test] add feedback mock api #623
mike2ox Feb 10, 2023
3e7712e
[Feat] add search bar, check feedback modal #623
mike2ox Feb 10, 2023
c037c21
[Refactor] update api url #646
mike2ox Feb 13, 2023
722524c
[Fix] update loading defense logic #646
mike2ox Feb 14, 2023
c0d7cd7
[Refactor] modify to output the correct data by column type#646
mike2ox Feb 14, 2023
12b5ee8
[Fix] modify api url, empty result #646
mike2ox Feb 14, 2023
010694d
Merge branch 'main' into feat/make-feedback-page-#646
mike2ox Feb 15, 2023
0b4830b
[Refactor] modify to output data to match column type #646
mike2ox Feb 15, 2023
7608a0b
[Style] update FeedbackTable style #646
mike2ox Feb 15, 2023
9bfec3b
[Feat] add DetailContentModal #646
mike2ox Feb 15, 2023
05fae06
[Feat] implement a 'Read More' button to show full text #646
mike2ox Feb 15, 2023
7fc343b
[Style] update feedback modal style #646
mike2ox Feb 16, 2023
7351e16
[Style] apply code convention #646
mike2ox Feb 16, 2023
76d1f41
[Style] modify title typo #646
mike2ox Feb 16, 2023
2c06e3b
[Feat] add feedback property in modal #646
mike2ox Feb 16, 2023
4ec64ce
[Feat] activate sendHander, add sendNotification fetch #646
mike2ox Feb 16, 2023
62cf5f3
[Feat] enhance feature to choose whether to send notifications or notโ€ฆ
mike2ox Feb 16, 2023
93af167
[Refactor] modify from index to unique value #646
mike2ox Feb 17, 2023
76cb01c
[Refactor] rename modal in feedback page, apply convention #646
mike2ox Feb 17, 2023
a7bd541
Merge branch 'main' into feat/make-feedback-page-#646
mike2ox Feb 20, 2023
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
163 changes: 163 additions & 0 deletions components/admin/feedback/FeedbackTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import { useCallback, useEffect, useState } from 'react';
import { useRecoilState } from 'recoil';
import instance from 'utils/axios';
import { modalState } from 'utils/recoil/modal';
import PageNation from 'components/Pagination';
import AdminSearchBar from 'components/admin/common/AdminSearchBar';
import { tableFormat } from 'constants/admin/table';
import {
Paper,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
} from '@mui/material';
import style from 'styles/admin/feedback/FeedbackTable.module.scss';

export interface IFeedback {
id: number;
intraId: string;
category: number; // 1: bug, 2: suggestion, 3: question
content: string;
createdTime: Date;
isSolved: boolean;
}

interface IFeedbackTable {
feedbackList: IFeedback[];
totalPage: number;
currentPage: number;
}

const MAX_CONTENT_LENGTH = 20;

export default function FeedbackTable() {
const [feedbackInfo, setFeedbackInfo] = useState<IFeedbackTable>({
feedbackList: [],
totalPage: 0,
currentPage: 0,
});
const [currentPage, setCurrentPage] = useState<number>(1);
const [intraId, setIntraId] = useState<string>('');
const [modal, setModal] = useRecoilState(modalState);

const getUserFeedbacks = useCallback(async () => {
try {
// TODO : api ์ˆ˜์ • ํ•„์š”
const res = await instance.get(
`/pingpong/admin/feedback/users/${intraId}?page=${currentPage}&size=10`
);
setIntraId(intraId);
setFeedbackInfo({ ...res.data });
} catch (e) {
console.error('MS04');
}
}, [intraId, currentPage]);

const getAllFeedbacks = useCallback(async () => {
try {
const res = await instance.get(
`/pingpong/admin/feedback?page=${currentPage}&size=10`
);
setFeedbackInfo({ ...res.data });
} catch (e) {
console.error('MS03');
}
}, [currentPage]);

const initSearch = useCallback((intraId?: string) => {
setIntraId(intraId || '');
setCurrentPage(1);
}, []);
mike2ox marked this conversation as resolved.
Show resolved Hide resolved

const solvingFeedback = (feedback: IFeedback) => {
setModal({
modalName: 'ADMIN-CHECK_FEEDBACK',
feedback,
});
};

const openDetailModal = (feedback: IFeedback) => {
setModal({
modalName: 'ADMIN-DETAIL_CONTENT',
intraId: feedback.intraId,
detailContent: feedback.content,
});
};

useEffect(() => {
intraId ? getUserFeedbacks() : getAllFeedbacks();
}, [intraId, getUserFeedbacks, getAllFeedbacks, modal]);

return (
<>
<div className={style.feedbackWrap}>
<div className={style.header}>
<span className={style.title}>ํ”ผ๋“œ๋ฐฑ ๊ด€๋ฆฌ</span>
<AdminSearchBar initSearch={initSearch} />
</div>
<TableContainer className={style.tableContainer} component={Paper}>
<Table className={style.table} aria-label='customized table'>
<TableHead className={style.tableHeader}>
<TableRow>
{tableFormat['feedback'].columns.map((columnName) => (
<TableCell className={style.tableHeaderItem} key={columnName}>
{columnName}
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody className={style.tableBody}>
{feedbackInfo.feedbackList.map((feedback: IFeedback) => (
<TableRow key={feedback.id}>
{tableFormat['feedback'].columns.map((columnName: string) => {
const value = feedback[columnName as keyof IFeedback];
return (
<TableCell
className={style.tableBodyItem}
key={columnName}
>
{typeof value === 'boolean' ? (
<select
value={feedback.isSolved ? 1 : 0}
onChange={() => solvingFeedback(feedback)}
>
<option value='0'>์ฒ˜๋ฆฌ์ค‘</option>
<option value='1'>์ฒ˜๋ฆฌ์™„๋ฃŒ</option>
</select>
) : value.toString().length > MAX_CONTENT_LENGTH ? (
<div>
{value.toString().slice(0, MAX_CONTENT_LENGTH)}
<span
style={{ cursor: 'pointer', color: 'grey' }}
onClick={() => openDetailModal(feedback)}
>
...๋”๋ณด๊ธฐ
</span>
</div>
) : (
value.toString()
)}
</TableCell>
);
})}
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
<div className={style.pageNationContainer}>
<PageNation
curPage={feedbackInfo.currentPage}
totalPages={feedbackInfo.totalPage}
pageChangeHandler={(pageNumber: number) => {
setCurrentPage(pageNumber);
}}
/>
</div>
</div>
</>
);
}
31 changes: 14 additions & 17 deletions components/admin/notification/NotificationTable.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useCallback, useEffect, useState } from 'react';
import { tableColumnNames } from 'types/admin/tableTypes';
import { tableFormat } from 'constants/admin/table';
import {
Paper,
Table,
Expand All @@ -14,6 +14,7 @@ import PageNation from 'components/Pagination';
import AdminSearchBar from 'components/admin/common/AdminSearchBar';
import CreateNotiButton from 'components/admin/notification/CreateNotiButton';
import style from 'styles/admin/notification/NotificationTable.module.scss';
import instance from 'utils/axios';

interface INotification {
notiId: number;
Expand Down Expand Up @@ -41,9 +42,8 @@ export default function NotificationTable() {

const getUserNotifications = useCallback(async () => {
try {
// TODO! : change to real endpoint
const res = await axios.get(
`${process.env.NEXT_PUBLIC_ADMIN_MOCK_ENDPOINT}/notifications/${intraId}?page=${currentPage}`
const res = await instance.get(
`pingpong/admin/notifications?q=${intraId}&page=${currentPage}&size=10`
);
setIntraId(intraId);
setNotificationInfo({ ...res.data });
Expand All @@ -59,9 +59,8 @@ export default function NotificationTable() {

const getAllUserNotifications = useCallback(async () => {
try {
// TODO! : change to real endpoint
const res = await axios.get(
`${process.env.NEXT_PUBLIC_ADMIN_MOCK_ENDPOINT}/notifications?page=${currentPage}`
const res = await instance.get(
`pingpong/admin/notifications?page=${currentPage}&size=10`
);
setIntraId('');
setNotificationInfo({ ...res.data });
Expand All @@ -74,8 +73,8 @@ export default function NotificationTable() {
intraId ? getUserNotifications() : getAllUserNotifications();
}, [intraId, getUserNotifications, getAllUserNotifications]);

if (notificationInfo === undefined) {
return <div>loading...</div>;
if (notificationInfo.notiList.length === 0) {
return <div>๋น„์–ด์žˆ์Šต๋‹ˆ๋‹ค!</div>;
}

return (
Expand All @@ -90,7 +89,7 @@ export default function NotificationTable() {
<Table className={style.table} aria-label='customized table'>
<TableHead className={style.tableHeader}>
<TableRow>
{tableColumnNames['notification'].map((columnName) => (
{tableFormat['notification'].columns.map((columnName) => (
<TableCell className={style.tableHeaderItem} key={columnName}>
{columnName}
</TableCell>
Expand All @@ -100,15 +99,13 @@ export default function NotificationTable() {
<TableBody className={style.tableBody}>
{notificationInfo.notiList.map((notification: INotification) => (
<TableRow key={notification.notiId}>
{Object.values(notification).map(
(value: number | string | boolean, index: number) => {
{tableFormat['notification'].columns.map(
(columnName: string, index: number) => {
return (
<TableCell className={style.tableBodyItem} key={index}>
{typeof value === 'boolean'
? value
? 'Checked'
: 'Unchecked'
: value}
{notification[
columnName as keyof INotification
]?.toString()}
</TableCell>
);
}
Expand Down
22 changes: 20 additions & 2 deletions components/modal/ModalProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,23 @@ import AdminPenaltyModal from './admin/AdminPenaltyModal';
import AdminNotiAllModal from './admin/AdminNotiAllModal';
import AdminNotiUserModal from './admin/AdminNotiUserModal';
import AdminCheckFeedback from './admin/AdminFeedbackCheckModal';
import FeedbackDetailModal from './admin/FeedbackDetailModal';
import styles from 'styles/modal/Modal.module.scss';

export default function ModalProvider() {
const [
{ modalName, cancel, enroll, manual, announcement, exp, intraId, userId },
{
modalName,
cancel,
enroll,
manual,
announcement,
exp,
intraId,
detailContent,
feedback,
userId,
},
setModal,
] = useRecoilState(modalState);
const setReloadMatch = useSetRecoilState(reloadMatchState);
Expand All @@ -42,7 +54,13 @@ export default function ModalProvider() {
'ADMIN-PENALTY': intraId ? <AdminPenaltyModal value={intraId} /> : null,
'ADMIN-NOTI_ALL': intraId ? <AdminNotiAllModal value={intraId} /> : null,
'ADMIN-NOTI_USER': intraId ? <AdminNotiUserModal value={intraId} /> : null,
'ADMIN-CHECK_FEEDBACK': intraId ? <AdminCheckFeedback /> : null,
'ADMIN-CHECK_FEEDBACK': feedback ? (
<AdminCheckFeedback {...feedback} />
) : null,
'ADMIN-DETAIL_CONTENT':
intraId && detailContent ? (
<FeedbackDetailModal intraId={intraId} detailContent={detailContent} />
) : null,
};

useEffect(() => {
Expand Down
73 changes: 34 additions & 39 deletions components/modal/admin/AdminFeedbackCheckModal.tsx
Original file line number Diff line number Diff line change
@@ -1,59 +1,54 @@
import { useSetRecoilState } from 'recoil';
import { useEffect, useState } from 'react';
import styles from 'styles/admin/modal/AdminFeedbackCheck.module.scss';
import { modalState } from 'utils/recoil/modal';
import { IFeedback } from 'components/admin/feedback/FeedbackTable';
import instance from 'utils/axios';
// import { finished } from 'stream';
import { useRouter } from 'next/router';
import GameResultSmallItem from 'components/game/small/GameResultSmallItem';

interface Result {
status: string;
}
import { IoSend } from 'react-icons/io5';
import styles from 'styles/admin/modal/AdminFeedbackCheck.module.scss';

export default function AdminFeedbackCheck(props: any) {
const [result, setResult] = useState<Result>({
status: '',
});
export default function AdminFeedbackCheck({
id,
intraId,
isSolved,
}: IFeedback) {
const setModal = useSetRecoilState(modalState);

const sendReminder: { [key: string]: string } = {
YES: '์„ฑ๊ณต์ ์œผ๋กœ ์ „์†ก๋˜์—ˆ์Šต๋‹ˆ๋‹ค',
NO: '์ „์†ก์ด ์ทจ์†Œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค',
};

const finishSendHandler = async () => {
if (result.status == 'completed') {
try {
await instance.post(`/admin/feedback/is-solved`);
setModal({ modalName: null });
} catch (e) {
console.log(e);
}
}
};

const cancelReminderHandler = () => {
if (result.status == 'completed') {
const sendNotificationHandler = async (isSend: boolean) => {
try {
await instance.put(`pingpong/admin/feedback/is-solved`, {
feedbackId: id,
});
await instance.post(`pingpong/admin/notifications/${intraId}`, {
intraId,
message: isSolved
? 'ํ”ผ๋“œ๋ฐฑ์„ ๊ฒ€ํ† ์ค‘์ž…๋‹ˆ๋‹ค.'
: 'ํ”ผ๋“œ๋ฐฑ์ด ๋ฐ˜์˜๋˜์—ˆ์Šต๋‹ˆ๋‹ค.',
sendMail: !isSend,
});
setModal({ modalName: null });
} catch (e) {
console.error(e);
}
};

return (
<div className={styles.whole}>
<div className={styles.body}>
<div className={styles.title}>FEEDBACK REMINDER</div>
<div className={styles.text}>
Are you sure you want to send the reminder?
<div>
<IoSend size={42} color={'#1556B8'} />
</div>
<div className={styles.content}>์•Œ๋ฆผ์„ ๋ณด๋‚ด๊ฒ ์Šต๋‹ˆ๊นŒ?</div>
<div className={styles.btns}>
<button
className={sendReminder ? `${styles.hide}` : `${styles.btn}`}
onClick={finishSendHandler}
className={`${styles.btn} ${styles.first}`}
onClick={() => sendNotificationHandler(true)}
>
<div className={styles.btntext}>YES</div>
ํ™•์ธ
</button>
<button className={styles.btn} onClick={cancelReminderHandler}>
<div className={styles.btntext}>NO</div>
<button
className={`${styles.btn} ${styles.second}`}
onClick={() => sendNotificationHandler(false)}
>
์ทจ์†Œ
</button>
</div>
</div>
Expand Down
Loading