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: 1 addition & 1 deletion src/assets/icons/chevron_down.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/assets/icons/chevron_up.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/assets/icons/smile.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: 2 additions & 0 deletions src/assets/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import NaverIcon from '@/assets/icons/naver_icon.svg?react';
import SoundIcon from '@/assets/icons/sound.svg?react';
import EnvironmentIcon from '@/assets/icons/environment.svg?react';
import CompanionIcon from '@/assets/icons/companion.svg?react';
import SmileIcon from '@/assets/icons/smile.svg?react';
import GalleryProfileIcon from '@/assets/icons/file_select.svg?react';

import TicketAlt from '@/assets/gif/ticket_alt.gif';
Expand Down Expand Up @@ -86,5 +87,6 @@ export {
EnvironmentIcon,
CompanionIcon,
TicketAlt,
SmileIcon,
GalleryProfileIcon,
};
57 changes: 57 additions & 0 deletions src/components/common/TagReviewNumber/TagCardList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { useState } from 'react';
import { ChevronDownIcon, ChevronUpIcon } from '@/assets';
import { TagReviewNumber } from '@/components';

interface TagReview {
iconType: 'sound' | 'environment' | 'companion';
title: string;
count: number;
}

interface TagCardListProps {
tags: TagReview[];
maxVisible?: number;
}

const TagCardList = ({ tags, maxVisible = 4 }: TagCardListProps) => {
const [expanded, setExpanded] = useState(false);
const visibleTags = expanded ? tags : tags.slice(0, maxVisible);
const hasOverflow = tags.length > maxVisible;

return (
<div className="mt-5">
<div className="flex flex-col gap-3">
{visibleTags.map((tag, index) => (
<TagReviewNumber
key={index}
iconType={tag.iconType}
title={tag.title}
count={tag.count}
/>
))}
</div>
{!expanded && hasOverflow && (
<div className="pointer-events-none absolute -mt-16 h-17 w-full bg-gradient-to-t from-gray-900 to-transparent" />
)}
{hasOverflow && (
<div className="relative z-10 mt-4 flex items-center justify-center">
<div className="h-px flex-1 bg-gray-700" />
<button
onClick={() => setExpanded((prev) => !prev)}
className="text-caption-1 flex items-center gap-1 rounded-full border border-gray-800 px-3 py-1 text-gray-700"
>
{expanded ? '접기' : '태그 더보기'}
{expanded ? (
<ChevronUpIcon className="h-4 w-4 text-gray-700" />
) : (
<ChevronDownIcon className="h-4 w-4 text-gray-700" />
)}
</button>
<div className="h-px flex-1 bg-gray-700" />
</div>
)}
</div>
);
};

export default TagCardList;
22 changes: 6 additions & 16 deletions src/components/common/TagReviewNumber/TagReviewNumber.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';

import {SoundIcon, CompanionIcon, EnvironmentIcon} from '@/assets';
import { SoundIcon, CompanionIcon, EnvironmentIcon } from '@/assets';

const iconComponents = {
sound: SoundIcon,
Expand All @@ -15,31 +15,21 @@ interface TagReviewNumberProps {
className?: string;
}


const TagReviewNumber: React.FC<TagReviewNumberProps> = ({
iconType,
title,
count,
className,
}) => {
const TagReviewNumber: React.FC<TagReviewNumberProps> = ({ iconType, title, count, className }) => {
const IconComponent = iconComponents[iconType];

return (
<div
className={`
flex w-[335px] h-[48px] items-center gap-3 rounded-l
bg-gray-800/30 p-3
${className}
`}
className={`flex h-[48px] w-full items-center gap-3 rounded-l bg-gray-800/30 p-3 ${className} `}
>
<div className="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-m bg-gray-800">
<div className="rounded-m flex h-8 w-8 flex-shrink-0 items-center justify-center bg-gray-800">
<IconComponent className="text-red-300" />
</div>

<p className="flex-1 truncate text-body-2 text-white">{title}</p>
<p className="text-body-2 flex-1 truncate text-white">{title}</p>
<p className="text-title-4 text-white">{count}</p>
</div>
);
};

export default TagReviewNumber;
export default TagReviewNumber;
4 changes: 4 additions & 0 deletions src/components/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import ConfirmModal from '@/components/common/Modal/ConfirmModal';
import SeatFocusModal from '@/components/common/Modal/SeatModal/SeatFocusModal';
import SeatWriteModal from '@/components/common/Modal/SeatModal/SeatWriteModal';
import ProgressBar from '@/components/common/ProgressBar/ProgressBar';
import TagReviewNumber from './common/TagReviewNumber/TagReviewNumber';
import TagCardList from './common/TagReviewNumber/TagCardList';
import TheaterList from './common/Theater/TheaterList';
export { default as FilterCheckbox } from '@/components/common/ReviewFilter/FilterCheckbox';
export { default as MyLevelCard } from './common/LevelCard/MyLevelCard';
Expand Down Expand Up @@ -61,5 +63,7 @@ export {
SeatFocusModal,
SeatWriteModal,
ProgressBar,
TagReviewNumber,
TagCardList,
TheaterList,
};
15 changes: 15 additions & 0 deletions src/constants/taglist.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
interface TagReview {
iconType: 'sound' | 'environment' | 'companion';
title: string;
count: number;
}

const tagList: TagReview[] = [
{ iconType: 'sound', title: '음향이 최고예요', count: 132 },
{ iconType: 'sound', title: '서라운드가 좋아요', count: 132 },
{ iconType: 'environment', title: '입출입이 편리해요', count: 132 },
{ iconType: 'companion', title: '혼자서도 좋아요', count: 132 },
{ iconType: 'companion', title: '친구랑 재밌었어요', count: 62 },
{ iconType: 'environment', title: '분위기가 좋아요', count: 55 },
] as const;
export default tagList;
31 changes: 13 additions & 18 deletions src/pages/home/TheaterDetail.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { useNavigate, useParams } from 'react-router-dom';
import { Header, Image, Badge, ReviewCard } from '@/components';
import { StarSmall, ArrowRight } from '@/assets';
import { Header, Image, ReviewCard, TagCardList } from '@/components';
import { StarSmall, ArrowRight, SmileIcon } from '@/assets';
import { getRandomImage, reviewSummaryMock } from '@/__mocks';
import { cinemaData } from '@/constants';
import tagList from '@/constants/taglist';

const CinemaDetailPage = () => {
const { auditoriumId } = useParams<{ auditoriumId: string }>();
Expand All @@ -19,12 +20,6 @@ const CinemaDetailPage = () => {
: cinema.theaterName
: '영화관 정보 없음';

const infoList = [
{ label: '스크린', value: cinema?.screenSize },
{ label: '영사 포맷', value: '정보 없음' }, //스웨거에 없는 것 같습니다...
{ label: '음향', value: cinema?.soundType },
];

const reviews = reviewSummaryMock.filter(
(review) =>
review.movieSeatInfo.theaterName === cinema?.theaterName &&
Expand Down Expand Up @@ -58,18 +53,18 @@ const CinemaDetailPage = () => {
</div>
</div>

{/*상세 정보*/}
<div className="flex flex-col gap-y-3 pt-5">
{infoList.map(({ label, value }) => (
<div key={label} className="flex items-center gap-4">
<Badge type="info" className="h-7 w-[85px] justify-center">
{label}
</Badge>
<span className="text-caption-2 text-white">{value || '정보 없음'}</span>
</div>
))}
{/*AI 후기 요약*/}
<div className="mt-5 flex flex-col rounded-lg border border-gray-500 px-4 py-3">
<div className="text-caption-1 flex items-center gap-1 text-red-300">
<SmileIcon />
<span>AI 후기 요약</span>
</div>
<div className="text-caption-2 mt-1 text-gray-300">어쩌고저쩌고 후기 내용</div>
</div>

{/*많이 사용된 태그*/}
<TagCardList tags={tagList} />

{/*후기*/}
<div className="mt-9">
<div className="mb-3 flex items-center justify-between">
Expand Down
88 changes: 24 additions & 64 deletions src/pages/home/TheatersList.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
import { useState } from 'react';
import { ToggleTab, Button, Header } from '@/components';
import { groupCinemasByTheater } from '@/utils/groupCinemasByTheater';
import { ToggleTab, Header, TheaterList } from '@/components';
import { useLocation, useNavigate } from 'react-router-dom';
import type { CinemaFormat } from '@/types/onboarding';
import { useTheatersQuery } from '@/hooks/queries/useTheatersQuery';

export default function TheaterListPage() {
const navigate = useNavigate();
const location = useLocation();

const queryTab = new URLSearchParams(location.search).get('tab');
const defaultTab: 'IMAX' | 'Dolby Cinema' =
queryTab?.toLowerCase() === 'dolby' ? 'Dolby Cinema' : 'IMAX';
const initialTab: CinemaFormat = queryTab === 'dolby' ? 'Dolby' : 'IMAX';

const [selectedTab, setSelectedTab] = useState<'IMAX' | 'Dolby Cinema'>(defaultTab);
const [selectedCinema, setSelectedCinema] = useState<string | null>(null);
const [selectedHall, setSelectedHall] = useState<string | null>(null);
const [selectedTab, setSelectedTab] = useState<CinemaFormat>(initialTab);
const [selectedAuditorium, setSelectedAuditorium] = useState<string | null>(null);

const cinemas = groupCinemasByTheater(selectedTab);
const { data: theaters } = useTheatersQuery({ type: selectedTab, page: 1, size: 10 });

const handleTabChange = (tab: string) => {
setSelectedTab(tab as CinemaFormat);
setSelectedAuditorium(null);
};

return (
<div className="flex min-h-screen flex-col pt-11">
Expand All @@ -28,68 +32,24 @@ export default function TheaterListPage() {
<ToggleTab
options={[
{ label: 'IMAX', value: 'IMAX' },
{ label: 'Dolby Cinema', value: 'Dolby Cinema' },
{ label: 'Dolby Cinema', value: 'Dolby' },
]}
selected={selectedTab}
onSelect={(option) => {
setSelectedTab(option as 'IMAX' | 'Dolby Cinema');
setSelectedCinema(null);
setSelectedHall(null);
}}
onSelect={handleTabChange}
/>
</div>
</div>
<div className="mt-5 px-5">
{/* 리스트 */}

{/* 리스트 */}
<div className="scrollbar-hidden max-h-[calc(100vh-136px)] overflow-y-auto pt-5">
<div className="flex flex-col items-center gap-3">
{Object.entries(cinemas).map(([theaterName, halls]) => {
const isSelected = selectedCinema === theaterName;
const isMulti = halls.length > 1;
return (
<div key={theaterName} className="w-full px-5">
<Button
onClick={() => {
if (!isMulti) {
navigate(`/theaters/${halls[0].auditoriumId}`);
} else {
setSelectedCinema(theaterName);
setSelectedHall(null);
}
}}
variant="secondary-assistive"
color="gray"
size="lg"
fontType="title-3"
className="w-full justify-start rounded-lg text-left"
selected={isSelected}
>
{theaterName}
</Button>

{isSelected && isMulti && (
<div className="mx-auto mt-2 ml-10 grid grid-cols-2 gap-2 px-1">
{halls.map((hall) => (
<Button
key={hall.auditoriumId}
onClick={() => {
navigate(`/theaters/${hall.auditoriumId}`);
}}
variant="secondary-assistive"
color="gray"
size="sm"
selected={selectedHall === hall.auditoriumId}
className="w-full"
>
{hall.auditoriumName}
</Button>
))}
</div>
)}
</div>
);
})}
</div>
<TheaterList
data={theaters ?? []}
selected={selectedAuditorium ? [selectedAuditorium] : []}
onSelect={(id) => setSelectedAuditorium(id)}
onAuditoriumClick={(auditoriumId) => {
navigate(`/theaters/${auditoriumId}`);
}}
/>
</div>
</div>
);
Expand Down