Skip to content

Commit

Permalink
QA 이슈 대응 - #62 (#63)
Browse files Browse the repository at this point in the history
* fix: 피드백 페이지 쿼리키 변경

* fix: router.refresh()제거

* fix: 피드백 관련 데이터 next캐싱 제거

* feat: 배경 그라데이션 색상 수정

* feat: 피드백 데이터 1초 단위 리패치 적용

* feat: 피드백 데이터 생성 실패 시에 사용되는 경고카드 생성

* fix: Navbar이동 시, 데이터 갱신이 되지 않는 오류 수정

* fix: 발표 삭제 후, 목록이 비었을 때 안내 모달창이 발생하지 않는 버그 수정

* feat: 비어있는 피드백 목록에 사용되는 페이지 생성

* fix: 발표 생성할 때 이미지가 없는 빈 슬라이드가 추가되는 버그 수정

* fix: 발표 자료 수정 이후 캐싱 데이터를 사용 해버리는 현상 제거

* chore: 피드백 채점 결과_실패(FAIL) 타입 추가

* chore: 인피니티 스크롤에 사용되는 함수 인자(pageParam) 옵셔널 타입 제거

* feat: 발표 연습의 마지막 페이지를 표시하는 컴포넌트 생성

* fix: CardList.tsx 의 overflow속성 제거

* fix: 발표 업로드 페이지 이미지 짤림 현상 해결

* feat: 총 발표시간>알람 시간 유효성 로직 추가

* refacotr: 불필요한 코드 제거

* refactor: 1초단위 피드백 리패치 로직 일부 수정
  • Loading branch information
minh0518 authored Jun 11, 2024
1 parent 8844972 commit deb67b6
Show file tree
Hide file tree
Showing 35 changed files with 404 additions and 160 deletions.
15 changes: 12 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"react-calendar": "^4.8.0",
"react-dom": "^18",
"react-hook-form": "^7.49.3",
"react-icons": "^5.2.1",
"react-intersection-observer": "^9.8.1",
"react-qr-code": "^2.0.12",
"zustand": "^4.4.7"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
position: relative;
width: 100%;
height: 250px;
margin-bottom: 12px;
border: 1px solid $gray-1;
margin-bottom: 12px;
border-radius: 16px;
}

Expand Down
55 changes: 39 additions & 16 deletions src/app/(afterlogin)/(common_navbar)/_components/CardItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import PracticeButton from './_Home/PracticeButton';
import FeedbackScoreButton from './_Feedback/FeedbackScoreButton';
import { FeedbackListTypeGuard, PresentationListTypeGuard } from '@/types/guards';
import { CDN_BASE_URL } from '@/config/path';
import { ReactNode } from 'react';
import FailFeedback from './_Feedback/FailFeedback';

interface Props {
listInfo: CardListType;
Expand All @@ -41,27 +43,48 @@ const CardItem = ({ listInfo }: Props) => {
modal.onOpen();
};

const deleteItem = () => {
presentationListMutate();
const getThumbnailImg = (): ReactNode => {
if (PresentationListTypeGuard(listInfo)) {
return listInfo.thumbnailPath ? (
<Image
src={`${CDN_BASE_URL}/${listInfo.thumbnailPath}`}
alt={`${listInfo.id} 썸네일`}
width={440}
height={250}
style={{ borderRadius: '16px' }}
/>
) : (
<div className={styles.dummyImg} />
);
}
if (FeedbackListTypeGuard(listInfo)) {
if (listInfo.status === 'FAIL') {
return <FailFeedback />;
}
if (
(listInfo.status === 'DONE' || listInfo.status === 'IN_PROGRESS') &&
listInfo.thumbnailPath
) {
return (
<Image
src={`${CDN_BASE_URL}/${listInfo.thumbnailPath}`}
alt={`${listInfo.id} 썸네일`}
width={440}
height={250}
style={{ borderRadius: '16px' }}
/>
);
} else {
return <div className={styles.dummyImg} />;
}
}
};

const thumbnailImage = listInfo.thumbnailPath ? (
<Image
src={`${CDN_BASE_URL}/${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}
{getThumbnailImg()}
<div className={styles.menu__box}>
{usage === 'home' && (
<FlyoutMenu context={flyout}>
Expand Down Expand Up @@ -110,7 +133,7 @@ const CardItem = ({ listInfo }: Props) => {
okayText="삭제하기"
cancelText="취소"
onOkayClick={() => {
deleteItem();
presentationListMutate();
}}
/>
</>
Expand Down
36 changes: 32 additions & 4 deletions src/app/(afterlogin)/(common_navbar)/_components/CardList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import PlusIcon from '../home/_components/_svgs/PlusIcon';
import CardItem from './CardItem';
import { clientFeedbackApi } from '@/services/client/feedback';

type StatusArrType = FeedbackListType['page']['content'][0]['status'][];

const CardList = () => {
// 피드백의 경우, 완료가 안 된게 있으면 1초마다 fetch
const router = useRouter();
Expand Down Expand Up @@ -51,10 +53,37 @@ const CardList = () => {
});

useEffect(() => {
if (inView) {
// if (inView) {
// !isFetching && hasNextPage && fetchNextPage();
// }
if (usage === 'feedback') {
!isFetching && hasNextPage && fetchNextPage();
}
if (usage === 'home' && inView) {
!isFetching && hasNextPage && fetchNextPage();
}
}, [inView, isFetching, hasNextPage, fetchNextPage]);
}, [inView, isFetching, hasNextPage, fetchNextPage, usage]);

useEffect(() => {
let refetchInterval: NodeJS.Timeout | undefined;
if (data !== undefined && usage === 'feedback') {
const statusInfo: StatusArrType = [];
data?.pages.forEach((i) => {
const row = i.page.content.map((i: FeedbackListType['page']['content'][0]) => i.status);
statusInfo.push(...row);
});

const shouldRefetch = statusInfo.some((status) => status === 'IN_PROGRESS');
if (shouldRefetch) {
refetchInterval = setInterval(() => {
refetch();
}, 1000);
}
}
return () => {
if (refetchInterval) clearInterval(refetchInterval);
};
}, [data, refetch, usage]);

return (
<section className={styles.container}>
Expand Down Expand Up @@ -82,8 +111,7 @@ const CardList = () => {
<span>새 발표 추가하기</span>
</button>
</ul>

<div ref={ref} style={{ height: '30px' }} />
{usage === 'home' && <div ref={ref} style={{ height: '30px' }} />}
</section>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
@import '@/styles/globals';
@import '@/styles/mixins';

.container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 10px;
min-width: 1440px;
width: 100dvw;
height: calc(100dvh - 68px);

p {
font-size: $font-1;
}

a {
margin: 20px;

@include button_size_web;
@include button_theme_default;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';
import styles from './EmptyFeedback.module.scss';
import Link from 'next/link';

const EmptyFeedback = () => {
return (
<div className={styles.container}>
<p>아직 연습할 발표가 없네요!</p>
<p>완성도 있는 발표를 위해 연습을 시작해볼까요?</p>
<Link href={'/upload/new'}>발표 연습 시작하기</Link>
</div>
);
};

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

.container {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
background-color: $gray-0;
width: 100%;
height: 100%;
gap: 20px;
border-radius: 16px;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react';
import styles from './FailFeedback.module.scss';
import { IoIosWarning } from 'react-icons/io';
const FailFeedback = () => {
return (
<div className={styles.container}>
<IoIosWarning size={60} />
<h3>피드백 데이터 생성에 실패했습니다.</h3>
</div>
);
};

export default FailFeedback;
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,26 @@ interface Props {
}

const FeedbackScoreButton = ({ status, score, onClick }: Props) => {
const getFeedBackScore = () => {
if (status === 'DONE') {
return `${score}점`;
}
if (status === 'IN_PROGRESS') {
return `채점중`;
}
};
return (
<div className={styles.action__box}>
<button
className={styles.action}
onClick={() => {
status === 'DONE' && onClick();
}}
>
{status === 'DONE' ? `${score}점` : '채점중'}
</button>
{status !== 'FAIL' && (
<button
className={styles.action}
onClick={() => {
status === 'DONE' && onClick();
}}
>
{getFeedBackScore()}
</button>
)}
</div>
);
};
Expand Down
5 changes: 3 additions & 2 deletions src/app/(afterlogin)/(common_navbar)/feedback/list/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import { HydrationBoundary, QueryClient, dehydrate } from '@tanstack/react-query
import React from 'react';
import styles from './page.module.scss';
import CardList from '../../_components/CardList';
import EmptyFeedback from '../../_components/_Feedback/EmptyFeedback';

export default async function Page() {
const queryClient = new QueryClient();
const listResponse = await queryClient.fetchInfiniteQuery({
queryKey: ['home', 'list'],
queryKey: ['feedback', 'list'],
queryFn: async ({ pageParam = 0 }) => {
const response = await serverFeedbackApi.getFeedbackList({ pageParam });
return await response.json();
Expand All @@ -22,7 +23,7 @@ export default async function Page() {
return (
<>
{isEmpty ? (
<>empty</>
<EmptyFeedback />
) : (
<div className={styles.container}>
<HydrationBoundary state={dehydratedState}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export const useGetLatestPresentation = () => {

export const useDeletePresentation = (id: number) => {
const queryClient = useQueryClient();
const router = useRouter();

const response = useMutation({
mutationKey: ['delete', id],
Expand All @@ -24,6 +25,8 @@ export const useDeletePresentation = (id: number) => {
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['home', 'list'] });
queryClient.invalidateQueries({ queryKey: ['home', 'latest'] });
router.refresh();
},
onError: (error) => {
alert(error.message);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ const ControlButtons = ({
setPresentationData((prev) => {
const shallow = { ...prev };
shallow.title = getValues('title');
shallow.timeLimit.hours = Number(getValues('timeLimit_hour'));
shallow.timeLimit.minutes = Number(getValues('timeLimit_minute'));
shallow.alertTime.hours = Number(getValues('alertTime_hour'));
shallow.alertTime.minutes = Number(getValues('alertTime_minute'));

const shallowSlides = [...shallow.slides];
shallowSlides[currentPageIndex] = {
...shallowSlides[currentPageIndex],
Expand All @@ -122,6 +127,11 @@ const ControlButtons = ({
setPresentationData((prev) => {
const shallow = { ...prev };
shallow.title = getValues('title');
shallow.timeLimit.hours = Number(getValues('timeLimit_hour'));
shallow.timeLimit.minutes = Number(getValues('timeLimit_minute'));
shallow.alertTime.hours = Number(getValues('alertTime_hour'));
shallow.alertTime.minutes = Number(getValues('alertTime_minute'));

const shallowSlides = [...shallow.slides];
shallowSlides[currentPageIndex] = {
...shallowSlides[currentPageIndex],
Expand Down Expand Up @@ -162,7 +172,7 @@ const ControlButtons = ({
src={`${CDN_BASE_URL}/${item.imageFilePath}`}
fill
alt="ppt이미지"
style={{ objectFit: 'contain', borderRadius: '8px' }}
style={{ borderRadius: '8px' }}
/>
<button onClick={(e) => remove(e, index)} className={styles.closeButton}>
<PptImageSvgs>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const CreatePresentation = () => {
deadlineDate: null,
timeLimit: {
hours: null,
minutes: 1,
minutes: null,
},
alertTime: {
hours: null,
Expand Down
Loading

0 comments on commit deb67b6

Please sign in to comment.