Skip to content
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
2 changes: 2 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
<link rel="icon" href="/favicon.png" type="image/png" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/static/pretendard.css" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
<meta name="format-detection" content="no" />

<meta property="og:title" content="dash" />
<meta property="og:description" content="당신에게 춤을 더 가까이, 꿈꾸던 댄스 클래스를 만나다" />
<meta property="og:type" content="website" />
Expand Down
5 changes: 5 additions & 0 deletions public/svg/ic_circle_caution_filled.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 6 additions & 6 deletions public/svg/ic_profile_basic.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/pages/auth/apis/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const useLoginMutation = () => {
instance.defaults.headers.Authorization = `Bearer ${accessToken}`;
if (!isOnboarded || isDeleted) {
clearStorage();
navigate(ROUTES_CONFIG.onboarding.path, { state: { accessToken, refreshToken } });
navigate(ROUTES_CONFIG.onboarding.path, { state: { accessToken, refreshToken, isDeleted } });
return;
}

Expand Down
42 changes: 27 additions & 15 deletions src/pages/class/components/ClassInfoWrapper/ClassInfoWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
import { useNavigate } from 'react-router-dom';
import Card from '@/pages/class/components/Card/Card';
import * as styles from '@/pages/class/components/ClassInfoWrapper/classInfoWrapper.css';
import {
cardItemStyle,
cardStyle,
classTitle,
priceTextStyle,
priceWrapper,
profileStyle,
reviewSubText,
reviewTextStyle,
sectionContainer,
tagWrapper,
teacherWrapper,
} from '@/pages/class/components/ClassInfoWrapper/classInfoWrapper.css';
import type { LessonDetailResponseTypes } from '@/pages/class/types/api';
import { getDDayLabel } from '@/pages/class/utils/dDay';
import type { GenreTypes } from '@/pages/onboarding/types/genreTypes';
Expand Down Expand Up @@ -37,8 +49,8 @@ const ClassInfoWrapper = ({ lessonData }: { lessonData: LessonDetailResponseType
const MAX_DISPLAY_RESERVATION_COUNT = 999;

return (
<section className={styles.sectionContainer} aria-label={`${name} 클래스 정보`}>
<div className={styles.tagWrapper}>
<section className={sectionContainer} aria-label={`${name} 클래스 정보`}>
<div className={tagWrapper}>
<Tag type="genre" size="medium">
<Text tag="b3_m" color="white">
{translatedGenre}
Expand All @@ -51,24 +63,24 @@ const ClassInfoWrapper = ({ lessonData }: { lessonData: LessonDetailResponseType
</Tag>
</div>

<Head level="h2" tag="h5_sb" className={styles.classTitle}>
<Head level="h2" tag="h5_sb" className={classTitle}>
{name}
</Head>

<div>
<button className={styles.teacherWrapper} onClick={() => handleTeacherClick(teacherId)}>
<img src={teacherImageUrl} alt={`${teacherNickname} 프로필`} className={styles.profileStyle} />
<Text as="span" tag="b2_m" color="gray9">
<button className={teacherWrapper} onClick={() => handleTeacherClick(teacherId)}>
<img src={teacherImageUrl} alt={`${teacherNickname} 프로필`} className={profileStyle} />
<Text as="span" tag="b1_sb" color="gray9">
{teacherNickname}
</Text>
</button>
</div>

<div className={styles.priceWrapper}>
<div className={priceWrapper}>
<Head level="h4" tag="h6_sb" color="gray6">
{lessonRounds.length}회
</Head>
<div className={styles.priceTextStyle}>
<div className={priceTextStyle}>
<Head level="h5" tag="h3_sb">
{price.toLocaleString()}
</Head>
Expand All @@ -78,8 +90,8 @@ const ClassInfoWrapper = ({ lessonData }: { lessonData: LessonDetailResponseType
</div>
</div>

<Card className={styles.cardStyle}>
<div className={styles.cardItemStyle}>
<Card className={cardStyle}>
<div className={cardItemStyle}>
<Text tag="b3_sb" color="gray7">
난이도
</Text>
Expand All @@ -88,7 +100,7 @@ const ClassInfoWrapper = ({ lessonData }: { lessonData: LessonDetailResponseType
</Text>
</div>

<div className={styles.cardItemStyle}>
<div className={cardItemStyle}>
<Text tag="b3_sb" color="gray7">
인원
</Text>
Expand All @@ -101,15 +113,15 @@ const ClassInfoWrapper = ({ lessonData }: { lessonData: LessonDetailResponseType
</Text>
</div>

<div className={styles.cardItemStyle}>
<div className={cardItemStyle}>
<Text tag="b3_sb" color="gray7">
리뷰
</Text>
<div className={styles.reviewTextStyle}>
<div className={reviewTextStyle}>
<Text tag="h6_sb" color="gray10">
-
</Text>
<Text tag="c1_r" color="gray6" className={styles.reviewSubText}>
<Text tag="c1_r" color="gray6" className={reviewSubText}>
(-)
</Text>
</div>
Expand Down
52 changes: 29 additions & 23 deletions src/pages/class/components/TabWrapper/TabLocation/TabLocation.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
import { lazy, Suspense } from 'react';
import Card from '@/pages/class/components/Card/Card';
import * as styles from '@/pages/class/components/TabWrapper/TabLocation/tabLocation.css';
import {
addressContainerStyle,
addressRowStyle,
addressTitleStyle,
cardInnerStyle,
cardStyle,
emptyMessageWrapper,
iconWrapper,
sectionStyle,
streetAddressStyle,
textGroupStyle,
} from '@/pages/class/components/TabWrapper/TabLocation/tabLocation.css';
import type { LessonDetailResponseTypes } from '@/pages/class/types/api';
import Head from '@/shared/components/Head/Head';
import Text from '@/shared/components/Text/Text';
import { sprinkles } from '@/shared/styles/sprinkles.css';

const IcLocation60 = lazy(() => import('@/shared/assets/svg/IcLocation60'));

Expand All @@ -16,40 +26,34 @@ const TabLocation = ({ lessonData }: { lessonData: LessonDetailResponseTypes })
const hasOldStreetAddress = !!oldStreetAddress;

return (
<section className={sprinkles({ display: 'flex', flexDirection: 'column', justifyContent: 'center', gap: 12 })}>
<section className={sectionStyle}>
{isEmpty ? (
<Head
level="h5"
tag="b1_sb"
color="gray9"
className={sprinkles({ display: 'flex', justifyContent: 'center', pt: 30, pb: 48 })}>
<Head level="h5" tag="b1_sb" color="gray9" className={emptyMessageWrapper}>
아직 장소가 등록되지 않은 클래스예요
</Head>
) : (
<Card className={styles.cardStyle}>
<div className={sprinkles({ display: 'flex', justifyContent: 'space-between', width: '100%', gap: 16 })}>
<div className={sprinkles({ display: 'flex', flexDirection: 'column', gap: 6 })}>
<Card className={cardStyle}>
<div className={cardInnerStyle}>
<div className={addressContainerStyle}>
<Text tag="b2_sb" color="black">
{location}
</Text>

<div className={sprinkles({ display: 'flex', flexDirection: 'column', gap: 4 })}>
<div className={textGroupStyle}>
{hasStreetAddress && (
<div className={sprinkles({ display: 'flex' })}>
<div className={sprinkles({ mr: 4 })}>
<Text tag="b3_m" color="gray6" className={styles.addressTitleStyle}>
주소
</Text>
</div>
<Text tag="b3_m" color="gray7" className={styles.streetAddressStyle}>
<div className={addressRowStyle}>
<Text tag="b3_m" color="gray6" className={addressTitleStyle}>
주소
</Text>
<Text tag="b3_m" color="gray7" className={streetAddressStyle}>
{[streetAddress, streetDetailAddress].filter(Boolean).join(' ')}
</Text>
</div>
)}

{hasOldStreetAddress && (
<div className={sprinkles({ display: 'flex' })}>
<Text tag="b3_m" color="gray6" className={styles.addressTitleStyle}>
<div className={addressRowStyle}>
<Text tag="b3_m" color="gray6" className={addressTitleStyle}>
지번
</Text>
<Text tag="b3_m" color="gray7">
Expand All @@ -60,8 +64,10 @@ const TabLocation = ({ lessonData }: { lessonData: LessonDetailResponseTypes })
</div>
</div>

<Suspense fallback={<div style={{ width: '6rem', height: '6rem' }} />}>
<IcLocation60 width={'6rem'} />
<Suspense fallback={<div className={iconWrapper} />}>
<div className={iconWrapper}>
<IcLocation60 width="6rem" height="6rem" />
</div>
</Suspense>
</div>
</Card>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,64 @@
import { style } from '@vanilla-extract/css';
import { vars } from '@/shared/styles/theme.css';

export const sectionStyle = style({
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
gap: '1.2rem',
});

export const emptyMessageWrapper = style({
display: 'flex',
justifyContent: 'center',
paddingTop: '3rem',
paddingBottom: '4.8rem',
});

export const cardStyle = style({
border: `1px solid ${vars.colors.gray03}`,
});

export const cardInnerStyle = style({
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
width: '100%',
gap: '1.6rem',
});

export const addressContainerStyle = style({
display: 'flex',
flexDirection: 'column',
gap: '0.6rem',
});

export const addressTitleStyle = style({
marginRight: '0.4rem',
whiteSpace: 'nowrap',
});

export const streetAddressStyle = style({
whiteSpace: 'pre-line',
overflowWrap: 'break-word',
wordBreak: 'break-all',
});

export const addressTitleStyle = style({
marginRight: '0.4rem',
whiteSpace: 'nowrap',
export const addressRowStyle = style({
display: 'flex',
});

export const cardStyle = style({
border: `1px solid ${vars.colors.gray03}`,
export const textGroupStyle = style({
display: 'flex',
flexDirection: 'column',
gap: '0.4rem',
});

export const iconWrapper = style({
width: '60px',
height: '60px',
flexShrink: 0,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
});
25 changes: 12 additions & 13 deletions src/pages/dancer/Dancer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,44 @@ import { useParams } from 'react-router-dom';
import { useGetDancerDetail } from '@/pages/dancer/apis/queries';
import DancerInfo from '@/pages/dancer/components/DancerInfo/DancerInfo';
import TabWrapper from '@/pages/dancer/components/TabWrapper/TabWrapper';
import * as styles from '@/pages/dancer/dancer.css';
import { topImgStyle, gradientOverlayStyle, textWrapperStyle, genresWrapperStyle } from '@/pages/dancer/dancer.css';
import ErrorPage from '@/pages/error/ErrorPage';
import Head from '@/shared/components/Head/Head';
import Tag from '@/shared/components/Tag/Tag';
import Text from '@/shared/components/Text/Text';
import { genreMapping } from '@/shared/constants/index';
import { sprinkles } from '@/shared/styles/sprinkles.css';

const Dancer = () => {
const { id } = useParams<{ id: string }>();

if (!id) {
return <ErrorPage />;
}
const { data, isError, isPending } = useGetDancerDetail(id ?? '', {
enabled: Boolean(id),
});
Comment on lines +15 to +17
Copy link
Member

Choose a reason for hiding this comment

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

useGetDancerDetail에 id를 넘겨서 내부에서 enabled를 지정하는게 아니라 외부에서 지정해준 이유는 명시적으로 id가 있을 때 query 요청이 간다는 것을 보여주기 위함인가요??
(의도에 대한 질문이라 정답은 없을 것 같아요!)

Copy link
Member Author

Choose a reason for hiding this comment

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

넵 id가 있을 때만 요청이 나간다는 걸 드러내는게 명확하다고 느꼈고,
기존 코드에서 eslint-ignore를 쓰는 구조를 없애고 싶어서 enabled로 제어하도록 수정했습니다.


// eslint-disable-next-line react-hooks/rules-of-hooks
const { data, isError, isLoading } = useGetDancerDetail(id);

if (isLoading) {
if (isPending || !id) {
return <></>;
}

if (isError || !data) {
return <ErrorPage />;
}

const { imageUrls, genres, nickname } = data;

const translatedGenres = (genres || []).map((genre) => genreMapping[genre] || genre);

return (
<>
<div
className={styles.topImgStyle}
className={topImgStyle}
role="img"
aria-label={`${nickname}의 대표 이미지`}
style={{
backgroundImage: `url(${imageUrls[0]})`,
}}>
<div className={styles.gradientOverlayStyle} />
<div className={styles.textWrapperStyle}>
<div className={sprinkles({ display: 'flex', flexDirection: 'row', gap: 4 })}>
<div className={gradientOverlayStyle} />
<div className={textWrapperStyle}>
<div className={genresWrapperStyle} role="list">
{translatedGenres.map((genre, id) => (
<Tag key={id} size="medium" type="genre">
<Text tag="b3_m" color="white">
Expand Down
5 changes: 3 additions & 2 deletions src/pages/dancer/apis/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import { getDancerDetail } from '@/pages/dancer/apis/axios';
import type { DancerDetailResponseTypes } from '@/pages/dancer/types/api';
import { teacherKeys } from '@/shared/constants/queryKey';

export const useGetDancerDetail = (teacherId: string) => {
export const useGetDancerDetail = (teacherId: string, options?: { enabled?: boolean }) => {
return useQuery<DancerDetailResponseTypes, AxiosError>({
queryKey: teacherKeys.me._ctx.profile(+teacherId).queryKey,
queryKey: teacherKeys.me._ctx.profile(Number(teacherId)).queryKey,
queryFn: () => getDancerDetail(teacherId),
enabled: options?.enabled,
});
};
Loading