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: 1차 UT QA 홈 페이지 반영 #58

Merged
merged 9 commits into from
Feb 13, 2025
11 changes: 9 additions & 2 deletions src/app/(route)/(home)/components/Page/Content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Capsule } from '@/components/Capsule';
import { LockBtn } from '@/components/LockBtn';
import type { LockBtnVariant } from '@/components/LockBtn/LockBtn';
import { ROUTES } from '@/constants';
import { useToast } from '@/hooks';
import { useGetSurvey } from '@/query-hooks/useSurvey';

import { Drawer } from '../Drawer';
Expand All @@ -19,7 +20,7 @@ export default function HomeContent() {
const routes = useRouter();
const [isOpen, setIsOpen] = useState<boolean>(true);
const { data } = useGetSurvey();

const { showToast } = useToast();
const daysArr = Array.from({ length: 15 }, (_, i) => i + 1);

const getLockState = (day: number): LockBtnVariant => {
Expand All @@ -44,6 +45,12 @@ export default function HomeContent() {
if (getLockState(day) === 'default') {
routes.push(`${ROUTES.game}/${data?.bundleId}`);
}
if (getLockState(day) === 'disabled' && day > (data?.nextSurveyIndex ?? 0)) {
showToast('하루에 한 회차씩 답변할 수 있어요');
}
if (getLockState(day) === 'completed') {
routes.push(`${ROUTES.reportQuestions}?focus=${day}`);
}
};

return (
Expand All @@ -54,7 +61,7 @@ export default function HomeContent() {
<Capsule className={styles.capsule}>
<label>캐릭터 완성까지</label>
<LineIcon color="#A9AEBA" />
<label>{`${15 - data.surveyHistoryDetails.length}`}</label>
<label>{`${15 - data.surveyHistoryDetails.length}회차`}</label>
</Capsule>
</div>
<Drawer isOpen={isOpen} setIsOpen={setIsOpen}>
Expand Down
4 changes: 2 additions & 2 deletions src/app/(route)/mypage/components/UserSettings.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React, { useState } from 'react';
import { useRouter } from 'next/navigation';

import { ArrowRightIcon as ArrowRightIcon } from '@/assets/icons';
import { ROUTES } from '@/constants';

import styles from './UserSettings.module.scss';
import { useRouter } from 'next/navigation';
import { ROUTES } from '@/constants';

const UserSettings = () => {
const [notificationsEnabled, setNotificationsEnabled] = useState(true);
Expand Down
2 changes: 1 addition & 1 deletion src/app/(route)/mypage/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

import React from 'react';

import UserSettings from '@/app/(route)/mypage/components/UserSettings';
import { useMemberStore } from '@/stores';

import ProfileCard from './components/ProfileCard';
import UserActionFooter from './components/UserActionFooter';
import UserSettings from '@/app/(route)/mypage/components/UserSettings';

export default function MyPage() {
const { member } = useMemberStore();
Expand Down
6 changes: 4 additions & 2 deletions src/app/(route)/report/analysis/page.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
'use client';

import { useEffect, useState } from 'react';
import CharacterSelectLayout from '../components/CharacterSelectLayout';

import { useGetCharacters } from '@/query-hooks/useCharacter';
import { useMemberStore } from '@/stores';
import type { CharacterItem } from '@/query-hooks/useCharacter/types';
import { useMemberStore } from '@/stores';

import CharacterSelectLayout from '../components/CharacterSelectLayout';

export default function ReportAnalysis() {
const { data: characterData = { characters: [] } } = useGetCharacters({ memberId: useMemberStore().getMemberId() });
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { BottomSheet } from '@/components/BottomSheet';
import { CheckIcon } from '@/assets/icons';
import { BottomSheet } from '@/components/BottomSheet';
import type { CharacterItem } from '@/query-hooks/useCharacter/types';
import { formatToKoreanOrder } from '@/utils';

import styles from './CharacterSelectBottomSheet.module.scss';
import { formatToKoreanOrder } from '@/utils';
import type { CharacterItem } from '@/query-hooks/useCharacter/types';

type Props = {
open: boolean;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { ArrowDown2Icon } from '@/assets/icons';
import { TextButton } from '@/components/TextButton';
import type { CharacterItem } from '@/query-hooks/useCharacter/types';
import { formatToKoreanOrder } from '@/utils';

import styles from './CharacterSelectButton.module.scss';
import { formatToKoreanOrder } from '@/utils';
import type { CharacterItem } from '@/query-hooks/useCharacter/types';

type Props = {
selectedCharacter: CharacterItem;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import type { PropsWithChildren } from 'react';

import CharacterSelectButton from '../CharacterSelectButton';
import type { CharacterItem } from '@/query-hooks/useCharacter/types';

import CharacterSelectBottomSheet from '../CharacterSelectBottomSheet';
import CharacterSelectButton from '../CharacterSelectButton';
import CharacterTabNav from '../CharacterTabNav';

import styles from './CharacterSelectLayout.module.scss';
import type { CharacterItem } from '@/query-hooks/useCharacter/types';

type Props = {
open: boolean;
Expand Down
6 changes: 6 additions & 0 deletions src/app/(route)/report/questions/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { PropsWithChildren } from 'react';
import { Suspense } from 'react';

export default function ReportQuestionsLayout({ children }: PropsWithChildren) {
return <Suspense>{children}</Suspense>;
Copy link
Collaborator

Choose a reason for hiding this comment

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

굿 나도 요렇게 바꿔야겠다 fallback으로 로딩뷰 여기다 넣어주는게 좀 더 깔끔할 수도 있겠다
페이지 전체적으로 이렇게 적용할 생각이야?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

next 에서 loading 페이지 만들어주면 next 자동으로 suspense 랑 fallback 알아서 해주는 걸루 알고있어! 그래서 로딩 관련해서는 loading.tsx 페이지만 적절히 만들어주면 될 거 같어

}
25 changes: 21 additions & 4 deletions src/app/(route)/report/questions/page.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,31 @@
'use client';

import { useEffect, useState } from 'react';
import { useEffect, useRef, useState } from 'react';
import { useSearchParams } from 'next/navigation';
import dayjs from 'dayjs';

import { Card } from '@/components/Card';
import styles from './page.module.scss';
import CharacterSelectLayout from '../components/CharacterSelectLayout';
import { useGetCharacters } from '@/query-hooks/useCharacter';
import { useMemberStore } from '@/stores';
import type { CharacterItem } from '@/query-hooks/useCharacter/types';
import { useGetSubmissions } from '@/query-hooks/useSurvey';
import { useMemberStore } from '@/stores';

import CharacterSelectLayout from '../components/CharacterSelectLayout';

import styles from './page.module.scss';

export default function ReportQuestions() {
const [open, setOpen] = useState(false);
const searchParams = useSearchParams();

const focusIndex = searchParams.get('focus') ? Number(searchParams.get('focus')) - 1 : null;
const cardRefs = useRef<(HTMLDivElement | null)[]>([]);

useEffect(() => {
if (focusIndex !== null && cardRefs.current[focusIndex]) {
cardRefs.current[focusIndex]?.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
}, [focusIndex]);
const [selectedCharacter, setSelectedCharacter] = useState<CharacterItem>({ ordinalNumber: 0, bundleId: 0 });

const { data: characterData = { characters: [] } } = useGetCharacters({ memberId: useMemberStore().getMemberId() });
Expand Down Expand Up @@ -49,12 +62,16 @@ export default function ReportQuestions() {
<div className={styles.contentContainer}>
Copy link
Collaborator

Choose a reason for hiding this comment

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

수민이 작업과 아주 무관하게.. 카드 개수가 많아지면 맨 아래 카드가 안보이는 오류가 있는 것 같당
contentContainer에 margin-bottom 적당히 필요할 듯..
요 페이지 내 스크롤 이벤트도 좀 문제가 있는데 고칠필요는 없고 우리끼리 인지하고 있다가 고치면 될듯!

{submissionData.surveyRecords.map((question, index) => (
<Card
ref={(el) => {
cardRefs.current[index] = el;
}}
key={index}
count={index + 1}
date={formatDate(question.submittedAt)}
question={question.question}
answer={question.answer}
retrospective={question.retrospective}
isOpen={focusIndex === index}
className={styles.card}
/>
))}
Expand Down
5 changes: 3 additions & 2 deletions src/app/(route)/term/page.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
'use client';

import { ArrowLeft2Icon } from '@/assets/icons';

import { useRouter } from 'next/navigation';

import { ArrowLeft2Icon } from '@/assets/icons';
import { ROUTES } from '@/constants';

import styles from './page.module.scss';

export default function TermPage() {
Expand Down
6 changes: 5 additions & 1 deletion src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ReactQueryDevtools } from '@tanstack/react-query-devtools';

import { AppleScript } from '@/components/appleScript';
import { KakaoScript } from '@/components/KakaoScript';
import { Toast } from '@/components/Toast';

import ReactQueryProviders from '../providers/ReactQueryProvider';

Expand Down Expand Up @@ -43,7 +44,10 @@ export default function RootLayout({
<body>
<ReactQueryProviders>
<div className="container">
<main className="layout">{children}</main>
<main className="layout">
{children}
<Toast />
</main>
</div>
<ReactQueryDevtools />
</ReactQueryProviders>
Expand Down
74 changes: 36 additions & 38 deletions src/components/Card/Card.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { forwardRef } from 'react';
import { Card as CardComponent } from '@radix-ui/themes';
import cn from 'classnames';
import { Accordion, Separator } from 'radix-ui';
Expand Down Expand Up @@ -56,42 +57,39 @@ const BasicCard = ({ count, date, question, answer, retrospective }: CardProps)
</div>
);

export default function Card({
count,
date,
question,
answer,
hiddenCollapse = false,
isOpen = false,
retrospective,
className,
}: CardProps) {
if (hiddenCollapse) {
return <BasicCard count={count} date={date} question={question} answer={answer} />;
}
const Card = forwardRef<HTMLDivElement, CardProps>(
({ count, date, question, answer, hiddenCollapse = false, isOpen = false, retrospective, className }, ref) => {
if (hiddenCollapse) {
return <BasicCard count={count} date={date} question={question} answer={answer} />;
}

return (
<div className={cn(styles.container, className)}>
<Accordion.Root type="single" collapsible {...(isOpen ? { defaultValue: 'item-1' } : {})}>
<Accordion.Item value="item-1">
<Accordion.Trigger className={styles.trigger}>
<CardComponent className={styles.wrapper}>
<Accordion.Header className={styles.header}>
<div className={styles.date}>
<h4 className={styles.cardSubTitle4}>{count}회차</h4>
<ArrowDownIcon width="1.5rem" height="1.5rem" className={styles.icon} />
</div>
</Accordion.Header>
<Accordion.Content className={styles.contents}>
<Chip size="sm" color="neutral" className={styles.chip}>
{date}
</Chip>
<CardContents question={question} answer={answer} retrospective={retrospective} />
</Accordion.Content>
</CardComponent>
</Accordion.Trigger>
</Accordion.Item>
</Accordion.Root>
</div>
);
}
return (
<div ref={ref} className={cn(styles.container, className)}>
<Accordion.Root type="single" collapsible {...(isOpen ? { defaultValue: 'item-1' } : {})}>
<Accordion.Item value="item-1">
<Accordion.Trigger className={styles.trigger}>
<CardComponent className={styles.wrapper}>
<Accordion.Header className={styles.header}>
<div className={styles.date}>
<h4 className={styles.cardSubTitle4}>{count}회차</h4>
<ArrowDownIcon width="1.5rem" height="1.5rem" className={styles.icon} />
</div>
</Accordion.Header>
<Accordion.Content className={styles.contents}>
<Chip size="sm" color="neutral" className={styles.chip}>
{date}
</Chip>
<CardContents question={question} answer={answer} retrospective={retrospective} />
</Accordion.Content>
</CardComponent>
</Accordion.Trigger>
</Accordion.Item>
</Accordion.Root>
</div>
);
},
);

Card.displayName = 'Card';

export default Card;
73 changes: 73 additions & 0 deletions src/components/Toast/Toast.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
.viewport {
list-style: none;
z-index: 100;
outline: none;
}

.root {
background: rgba(68, 73, 83, 0.95);
border-radius: 0.75rem;
z-index: 100;
position: absolute;
bottom: 4.25rem;
width: calc(100% - 2.5rem);
padding-left: 1.7188rem;
padding-right: 1.7188rem;
text-align: center;
height: 4.5rem;
left: 50%;
transform: translateX(-50%);
@include flex-center;

&[data-state='open'] {
animation: slideIn 300ms ease-out;
}

&[data-state='closed'] {
animation: slideOut 600ms ease-in;
}

&[data-swipe='move'] {
transform: translateY(var(--radix-toast-swipe-move-y));
}

&[data-swipe='cancel'] {
transform: translateY(0);
transition: transform 300ms ease-out;
}

&[data-swipe='end'] {
animation: swipeOut 300ms ease-out;
}
}

@keyframes slideIn {
from {
transform: translateX(-50%) translateY(5vh);
}
to {
transform: translateX(-50%) translateY(0);
}
}

@keyframes slideOut {
from {
transform: translateX(-50%) translateY(0);
}
to {
transform: translateX(-50%) translateY(9.5vh);
}
}

@keyframes swipeOut {
from {
transform: translateX(-50%) translateY(var(--radix-toast-swipe-end-y));
}
to {
transform: translateX(-50%) translateY(100%);
}
}
.title {
color: $white;
@include subTitle4;
}
20 changes: 20 additions & 0 deletions src/components/Toast/Toast.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
'use client';

import { Toast as RadixToast } from 'radix-ui';

import { useToast } from '@/hooks';

import styles from './Toast.module.scss';

export default function Toast() {
const { toast, updateToast } = useToast();

return (
<RadixToast.Provider swipeDirection="down" duration={1000}>
<RadixToast.Root className={styles.root} open={toast.open} onOpenChange={updateToast}>
<RadixToast.Title className={styles.title}>{toast.message}</RadixToast.Title>
</RadixToast.Root>
<RadixToast.Viewport className={styles.viewport} />
</RadixToast.Provider>
);
}
1 change: 1 addition & 0 deletions src/components/Toast/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as Toast } from './Toast';
1 change: 1 addition & 0 deletions src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './usePreviousValue';
export * from './useToast';
Loading