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

2차 배포 - 피드백 페이지 생성 #57

Merged
merged 5 commits into from
May 22, 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
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
@import '@/styles/globals';
@import '@/styles/mixins';

.info {
display: flex;
flex-direction: column;
gap: 5px;

&__title {
margin: 0 0 12px;
font-size: $h2;

@include line(2);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,22 @@ import styles from './CardInfo.module.scss';

import HomeCardDescription from './_Home/HomeCardDescription';
import { PresentationListTypeGuard } from '@/types/guards';
import { usePathname } from 'next/navigation';
import FeedbackCardDescription from './_Feedback/FeedbackCardDescription';

interface Props {
listInfo: CardListType;
usage: 'home' | 'feedback';
}
const CardInfo = ({ listInfo, usage }: Props) => {
const CardInfo = ({ listInfo }: Props) => {
const pathname = usePathname();
const usage: 'feedback' | 'home' = pathname === `/feedback/list` ? 'feedback' : 'home';
return (
<div className={styles.info}>
<span className={styles.info__title}>{listInfo.title}</span>
{usage === 'home' && PresentationListTypeGuard(listInfo) ? (
<>
<HomeCardDescription listInfo={listInfo} />
</>
<HomeCardDescription listInfo={listInfo} />
) : (
<></>
<FeedbackCardDescription listInfo={listInfo} />
)}
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
@import '@/styles/globals';
@import '@/styles/mixins';

.container {
width: 440px;
height: 335px;
}

.thumbnail {
position: relative;
width: 100%;
height: 250px;
margin-bottom: 12px;
border: 1px solid $gray-1;
border-radius: 16px;
}

.dummyImg {
width: 440px;
height: 250px;
border-radius: 16px;
background-color: black;
}

.menu {
@include pure-button;

display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
height: 100%;

span {
font-size: $font-2;
}
}

.infoBox {
display: flex;
align-items: center;
justify-content: space-between;
max-width: 440px;
}
119 changes: 119 additions & 0 deletions src/app/(afterlogin)/(common_navbar)/_components/CardItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
'use client';

import FlyoutMenu from '@/app/_components/_modules/FlyoutMenu';
import styles from './CardItem.module.scss';

import useToggle from '@/app/_hooks/useToggle';
import Confirm from '@/app/_components/_modules/_modal/Confirm';
import { CardListType } from '@/types/service';
import Image from 'next/image';
import { usePathname, useRouter } from 'next/navigation';
import DeleteIcon from '../home/_components/_svgs/DeleteIcon';
import ModifyIcon from '../home/_components/_svgs/ModifyIcon';
import MenuIcon from '../home/_components/_svgs/MenuIcon';
import { useDeletePresentation } from '../home/_hooks/presentationList';
import CardInfo from './CardInfo';
import PracticeButton from './_Home/PracticeButton';
import FeedbackScoreButton from './_Feedback/FeedbackScoreButton';
import { FeedbackListTypeGuard, PresentationListTypeGuard } from '@/types/guards';

interface Props {
listInfo: CardListType;
}

const CardItem = ({ listInfo }: Props) => {
const router = useRouter();
const flyout = useToggle();
const modal = useToggle();
const pathname = usePathname();
const usage: 'feedback' | 'home' = pathname === `/feedback/list` ? 'feedback' : 'home';

const { mutate: presentationListMutate } = useDeletePresentation(listInfo.id);

const handleModify = () => {
router.push(`/upload/${listInfo.id}`);
flyout.onClose();
};

const handleDelete = () => {
flyout.onClose();
modal.onOpen();
};

const deleteItem = () => {
presentationListMutate();
};

const thumbnailImage = listInfo.thumbnailPath ? (
<Image
src={`${process.env.NEXT_PUBLIC_BASE_URL_CDN}/${listInfo.thumbnailPath}`}
alt={`${listInfo.id} 썸네일`}
width={440}
height={250}
style={{ borderRadius: '16px' }}
/>
) : (
<div className={styles.dummyImg} />
);

return (
<>
<article className={styles.container}>
<div className={styles.thumbnail}>
{thumbnailImage}
<div className={styles.menu__box}>
{usage === 'home' && (
<FlyoutMenu context={flyout}>
<FlyoutMenu.ToggleButton>
<MenuIcon />
</FlyoutMenu.ToggleButton>
<FlyoutMenu.MenuList>
<FlyoutMenu.MenuItem>
<button className={styles.menu} onClick={handleModify}>
<ModifyIcon />
<span>수정</span>
</button>
</FlyoutMenu.MenuItem>
<FlyoutMenu.MenuItem>
<button className={styles.menu} onClick={handleDelete}>
<DeleteIcon />
<span>삭제</span>
</button>
</FlyoutMenu.MenuItem>
</FlyoutMenu.MenuList>
</FlyoutMenu>
)}
</div>
</div>
<div className={styles.infoBox}>
<CardInfo listInfo={listInfo} />
<div>
{usage === 'home' && PresentationListTypeGuard(listInfo) && (
<PracticeButton id={listInfo.id} />
)}
{usage === 'feedback' && FeedbackListTypeGuard(listInfo) && (
<FeedbackScoreButton
status={listInfo.status}
score={listInfo.totalScore}
onClick={() => router.push(`/feedback/${listInfo.id}`)}
/>
)}
</div>
</div>
</article>

<Confirm
context={modal}
title="발표 연습 파일을 삭제하시겠어요?"
message="삭제한 파일은 복원할 수 없습니다."
okayText="삭제하기"
cancelText="취소"
onOkayClick={() => {
deleteItem();
}}
/>
</>
);
};

export default CardItem;
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
@import '@/styles/mixins';

.container {
width: 1440px;
min-width: 1440px;
margin-top: 32px;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,16 @@ import { clientHomeApi } from '@/services/client/home';
import { useInView } from 'react-intersection-observer';
import { Fragment, useEffect } from 'react';
import { CardListType, FeedbackListType, PresentationListType } from '@/types/service';
import { useRouter } from 'next/navigation';
import { usePathname, useRouter } from 'next/navigation';
import PlusIcon from '../home/_components/_svgs/PlusIcon';
import CardItem from './CardItem';
import { clientFeedbackApi } from '@/services/client/feedback';

// 패칭 api
// 최근 발표 사용 여부
// 이미지 아래 설명 컨텐츠
// 이미지 아래 버튼
interface Props {
usage: 'home' | 'feedback';
}
const CardList = ({ usage }: Props) => {
// 여기서 피드백의 경우, 완료가 안 된게 있으면 1초마다 fetch
const CardList = () => {
// 피드백의 경우, 완료가 안 된게 있으면 1초마다 fetch
const router = useRouter();
const pathname = usePathname();
const usage: 'feedback' | 'home' = pathname === `/feedback/list` ? 'feedback' : 'home';

const { data, fetchNextPage, hasNextPage, isFetching, refetch } = useInfiniteQuery({
queryKey: [usage, 'list'],
Expand All @@ -33,6 +28,8 @@ const CardList = ({ usage }: Props) => {
return await response.json();
}
},
staleTime: 0,
gcTime: 0,
initialPageParam: 0,
getNextPageParam: (lastPage, pages) => {
if (lastPage && pages) {
Expand Down Expand Up @@ -69,7 +66,7 @@ const CardList = ({ usage }: Props) => {
<Fragment key={index}>
{eachPage.page.content.map((listInfo: CardListType, index) => (
<li className={styles.exercise} key={index}>
<CardItem listInfo={listInfo} usage={usage} />
<CardItem listInfo={listInfo} />
</li>
))}
</Fragment>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@import '@/styles/globals';
@import '@/styles/mixins';

.generateDate {
color: $gray-6;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { FeedbackListTypeGuard } from '@/types/guards';
import { CardListType } from '@/types/service';
import styles from './FeedbackCardDescription.module.scss';

interface Props {
listInfo: CardListType;
}

const FeedbackCardDescription = ({ listInfo }: Props) => {
return (
listInfo &&
FeedbackListTypeGuard(listInfo) && (
<p className={styles.generateDate}>{listInfo.practiceDate}</p>
)
);
};

export default FeedbackCardDescription;
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
@import '@/styles/globals';
@import '@/styles/mixins';

.action {
@include pure-button;
@include button_theme_inverted;

width: 100%;
height: 100%;
border-radius: 100px;
font-size: 20px;

&__box {
width: 67px;
height: 36px;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react';
import styles from './FeedbackScoreButton.module.scss';
import { FeedbackListType } from '@/types/service';

interface Props {
status: FeedbackListType['page']['content'][0]['status'];
score: number;
onClick: () => void;
}

const FeedbackScoreButton = ({ status, score, onClick }: Props) => {
return (
<div className={styles.action__box}>
<button
className={styles.action}
onClick={() => {
status === 'DONE' && onClick();
}}
>
{status === 'DONE' ? `${score}점` : '채점중'}
</button>
</div>
);
};

export default FeedbackScoreButton;
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React from 'react';
import styles from './HomeCardDescription.module.scss';
import { CardListType } from '@/types/service';
import { PresentationListTypeGuard } from '@/types/guards';

interface Props {
listInfo: CardListType;
}

const HomeCardDescription = ({ listInfo }: Props) => {
return (
listInfo &&
PresentationListTypeGuard(listInfo) && (
<span className={styles.desc}>
D{listInfo.dday < 0 ? `+${Math.abs(listInfo.dday)}` : `-${listInfo.dday}`}
<em className={styles.division}></em>
발표 시간 {listInfo.timeLimit.hours * 60 + listInfo.timeLimit.minutes}분
</span>
)
);
};

export default HomeCardDescription;
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
'use client';
import React, { useState } from 'react';
import styles from './PracticeButton.module.scss';
import { useStartPresentation } from '../../home/_hooks/presentationList';

interface Props {
id: number;
}
const PracticeButton = ({ id }: Props) => {
const [start, setStart] = useState(false);

useStartPresentation(id, start);
return (
<div className={styles.action__box}>
<button className={styles.action} onClick={() => setStart(true)}>
연습하기
</button>
</div>
);
};

export default PracticeButton;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { FeedbackInfoType } from '@/types/service';

interface Props {
feedbackData: FeedbackInfoType;
}
const CategoryFeedback = ({ feedbackData }: Props) => {
return <div></div>;
};

export default CategoryFeedback;
Loading
Loading