Skip to content

Commit

Permalink
피드백 페이지 생성 - 발화 속도 피드백 (#56)
Browse files Browse the repository at this point in the history
* feat: 피드백 썸네일 임시 이미지 적용

* feat: 피드백 목록 단일 카드 정보 생성

* feat: 발표 연습 시작 api 적용

* fix: 최근 발표API 임시 주석처리

* feat: 최근 발표 섹션 복구

* feat: 홈 화면에서 바로 연습 시작시, 발표 시작 api적용

* fix: 발표 마지막 페이지 슬라이드 녹음본 저장 로직 추가

* fix: 누락된 api명세 및 UI문구 수정

* feat: 배경 그라데이션 적용

* feat: 발화 속도 피드백 페이지 구현

* fix: Navbar버튼과 모달 버튼 스타일 중첩 제거
  • Loading branch information
minh0518 authored May 22, 2024
1 parent 9cdf3cf commit eb46d84
Show file tree
Hide file tree
Showing 37 changed files with 486 additions and 154 deletions.
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);
}
}
13 changes: 7 additions & 6 deletions src/app/(afterlogin)/(common_navbar)/_components/CardInfo.tsx
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
Expand Up @@ -10,11 +10,18 @@
position: relative;
width: 100%;
height: 250px;
margin-bottom: 20px;
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;

Expand All @@ -29,35 +36,9 @@
}
}

.info {
width: 280px;

span {
@include line(1);
}

em {
background-color: $black;
}

&__box {
display: flex;
align-items: center;
justify-content: space-between;
}
}

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

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

&__box {
width: 120px;
height: 55px;
}
.infoBox {
display: flex;
align-items: center;
justify-content: space-between;
max-width: 440px;
}
95 changes: 54 additions & 41 deletions src/app/(afterlogin)/(common_navbar)/_components/CardItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,28 @@ 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 { useRouter } from 'next/navigation';
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;
usage: 'home' | 'feedback';
}

const CardItem = ({ listInfo, usage }: Props) => {
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 } = useDeletePresentation(listInfo.id);
const { mutate: presentationListMutate } = useDeletePresentation(listInfo.id);

const handleModify = () => {
router.push(`/upload/${listInfo.id}`);
Expand All @@ -38,52 +41,62 @@ const CardItem = ({ listInfo, usage }: Props) => {
};

const deleteItem = () => {
mutate();
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}>
<Image
src={`${process.env.NEXT_PUBLIC_BASE_URL_CDN}/${listInfo.thumbnailPath}`}
alt={`${listInfo.id} 썸네일`}
width={440}
height={250}
style={{ borderRadius: '16px' }}
/>
{thumbnailImage}
<div className={styles.menu__box}>
{/* TODO: 피드백에 맞는 버튼 로직 생성 필요 */}
<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>
{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.info__box}>
<div className={styles.listInfo}>
<CardInfo listInfo={listInfo} usage={usage} />
</div>
<div className={styles.action__box}>
{usage === 'home' ? (
<PracticeButton onClick={() => router.push(`/setting/${listInfo.id}`)} />
) : (
<></>
<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>
Expand Down
19 changes: 8 additions & 11 deletions src/app/(afterlogin)/(common_navbar)/_components/CardList.tsx
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
Expand Up @@ -8,14 +8,15 @@ interface Props {
}

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>
) : (
<></>
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>
)
);
};

Expand Down
Loading

0 comments on commit eb46d84

Please sign in to comment.