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
50 changes: 23 additions & 27 deletions apps/nowait-user/src/assets/icon/banner_img.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions apps/nowait-user/src/assets/icon/refresh.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions apps/nowait-user/src/assets/icon/search_black.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 10 additions & 4 deletions apps/nowait-user/src/pages/home/HomeHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import LogoBlack from "../../assets/logo-black.svg?react";
import menu from "../../assets/icon/menu.svg";
import Menu from "../../assets/icon/menu.svg?react";
import Search from "../../assets/icon/search_black.svg?react";

const HomeHeader = () => {
return (
<div className="flex justify-between items-center py-4">
<LogoBlack className="w-14.5 h-6" />
<button onClick={() => {}} className="cursor-pointer">
<img src={menu} alt="menu" className="icon-m" />
</button>
<div className="flex flex-row gap-3">
<button onClick={() => {}} className="cursor-pointer">
<Search className="icon-m" />
</button>
<button onClick={() => {}} className="cursor-pointer">
<Menu className="icon-m" />
</button>
</div>
</div>
);
};
Expand Down
142 changes: 58 additions & 84 deletions apps/nowait-user/src/pages/home/HomePage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { useRef, useState, useEffect } from "react";
import HomeHeader from "./HomeHeader";
import Search from "../../assets/icon/search.svg?react";
import ArrowDown from "../../assets/icon/arrow_down.svg?react";
import MainCard from "./components/MainCard";
import InfiniteStoreList from "./components/InfiniteStoreList";
Expand Down Expand Up @@ -60,102 +59,77 @@ const HomePage = () => {
};

return (
<div className="flex flex-col px-5">
<HomeHeader />
<div>
{/* 주점명, 메뉴, 학과 검색 기능 */}
<div className="mt-1 flex flex-row justify-start items-center px-4 py-3.5 bg-black-10 gap-2 rounded-lg">
<Search />
<div className="text-black-50 text-16-regular">
주점명, 메뉴, 학과 검색
<div className="flex flex-col gap-10">
<div className="flex flex-col bg-[#EEF1F9] px-5 pb-8">
<HomeHeader />
{/* 내 대기 순서 */}
<div className="flex flex-col">
<div className="flex flex-row mt-4.5 gap-1.5 text-title-20-bold text-black-100">
<div className="flex">나의 대기카드</div>
<div className="flex">2</div>
</div>
</div>

<div className="flex flex-col gap-10">
{/* 주점 순서 */}
<div className="mt-3">
<div
ref={scrollRef}
className="overflow-x-auto scrollbar-hide snap-x snap-mandatory rounded-2xl"
>
<div className="flex">
{Array.from({ length: 3 }, (_, index) => (
<div
key={index}
className="w-full flex-shrink-0 snap-start px-0"
>
<MainCard
type="homeWaiting"
storeName={`주점주점주점명${index + 1}`}
queueNumber={13 + index}
onClick={handleWaitingCardClick}
/>
</div>
))}
</div>
</div>
<MainCard
type="myWaitingCard"
storeName="스페이시스"
waitingTeams={5}
onClick={handleWaitingCardClick}
/>

{/* 페이지 인디케이터 */}
<div className="flex justify-center mt-2.5 gap-1.25">
{Array.from({ length: 3 }, (_, index) => (
<div
key={index}
className={`h-1.5 transition-all duration-300 ${
index === activeIndex
? "w-3.5 rounded-md bg-[#000000]"
: "w-1.5 rounded-sm bg-[#000000] opacity-20"
}`}
/>
))}
<div className="flex flex-row justify-between items-center bg-white-100 rounded-2xl px-4 py-4 h-15">
<div className="flex text-14-medium text-[#787878] leading-[130%]">
10분 안에 입력해주세요!
</div>
</div>

{/* 바로 입장 가능한 주점 */}
<div className="flex flex-col">
<div className="flex flex-row gap-1.5 items-center mb-3.5">
<div className="flex text-start text-headline-22-bold text-black-90">
{getSectionTitle()}
</div>
<div
onClick={handleModalOpen}
className="flex w-6 h-6 bg-black-15 rounded-full items-center justify-center cursor-pointer"
>
<ArrowDown className="text-black-60 icon-s" />
</div>
<div className="flex text-14-bold text-[#1A3149]">10:00</div>
</div>
</div>
</div>
<div className="flex flex-col px-5">
{/* 바로 입장 가능한 주점 */}
<div className="flex flex-col">
<div className="flex flex-row gap-1.5 items-center mb-3.5">
<div className="flex text-start text-headline-22-bold text-black-90">
{getSectionTitle()}
</div>
<div className="flex gap-1.5 overflow-x-auto scrollbar-hide">
{mockWaitingItems.map((store) => (
<div key={store.id} className="flex-shrink-0">
<MainCard
type="homeCard"
imageUrl={store.imageUrl}
storeName={store.storeName}
departmentId={store.departmentId}
waitingCount={store.waitingCount}
/>
</div>
))}
<div
onClick={handleModalOpen}
className="flex w-6 h-6 bg-black-15 rounded-full items-center justify-center cursor-pointer"
>
<ArrowDown className="text-black-60 icon-s" />
</div>
</div>

{/* 축제 부스 찾기 안내 */}
<div className="flex flex-row rounded-2xl bg-black-15 gap-3.75 justify-between pl-5 items-center">
<div className="flex flex-col py-5">
<div className="flex text-18-bold text-black-90">
축제 부스 한눈에 찾기
</div>
<div className="flex text-13-regular text-black-80 leading-[130%]">
지도로 부스의 위치를 확인해 보세요!
<div className="flex gap-1.5 overflow-x-auto scrollbar-hide">
{mockWaitingItems.map((store) => (
<div key={store.id} className="flex-shrink-0">
<MainCard
type="homeCard"
imageUrl={store.imageUrl}
storeName={store.storeName}
departmentId={store.departmentId}
waitingCount={store.waitingCount}
/>
</div>
</div>
<BannerMap className="w-25 h-25" />
))}
</div>
</div>

{/* 무한 스크롤 주점 목록 */}
<InfiniteStoreList />
{/* 축제 부스 찾기 안내 */}
<div className="flex flex-row rounded-2xl bg-black-15 gap-3.75 justify-between pl-5 items-center">
<div className="flex flex-col py-5">
<div className="flex text-18-bold text-black-90">
축제 부스 한눈에 찾기
</div>
<div className="flex text-13-regular text-black-80 leading-[130%]">
지도로 부스의 위치를 확인해 보세요!
</div>
</div>
<BannerMap className="w-25 h-25" />
</div>
</div>

{/* 무한 스크롤 주점 목록 */}
<InfiniteStoreList />
</div>
{/* 정렬 모달 */}
<WaitingListModal
isOpen={isModalOpen}
Expand Down
57 changes: 56 additions & 1 deletion apps/nowait-user/src/pages/home/components/MainCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { getDepartmentName } from "../../../constants/departments";
import { NotOpenIcon, WaitingIcon, WaitingCardIcon } from "./HomeIcon";
import type { WaitingItem } from "../../../types/WaitingItem";
import block from "../../../assets/block.png";
import Refresh from "../../../assets/icon/refresh.svg?react";

// WaitingCard Props
interface WaitingCardProps {
Expand Down Expand Up @@ -40,11 +41,20 @@ interface HomeCardProps {
departmentId?: number;
}

// MyWaitingCard Props
interface MyWaitingCardProps {
type: "myWaitingCard";
storeName: string;
waitingTeams: number;
onClick?: () => void;
}

type MainCardProps =
| WaitingCardProps
| StoreCardProps
| HomeWaitingCardProps
| HomeCardProps;
| HomeCardProps
| MyWaitingCardProps;

// 대기 카드 컴포넌트
const WaitingCard = ({ item }: { item: WaitingItem }) => {
Expand Down Expand Up @@ -277,6 +287,43 @@ const HomeCardComponent = ({
);
};

// 내 대기 카드 컴포넌트
const MyWaitingCardComponent = ({
storeName,
waitingTeams,
onClick,
}: Omit<MyWaitingCardProps, "type">) => {
return (
<div
className="flex flex-col mt-4.5 mb-2.5 pb-9 w-full bg-[#D8E6FF] rounded-2xl items-center justify-center cursor-pointer"
onClick={onClick}
>
<div className="flex mt-6.5 text-14-semibold text-[#1A3149] leading-[130%] tracking-[0em]">
{storeName}
</div>
<div className="flex flex-row mt-1">
<div className="flex mr-1 text-title-20-bold leading-[130%] tracking-[0em] text-[#1A3149]">
내 앞에 대기
</div>
<div className="flex text-title-20-bold leading-[130%] tracking-[0em] text-primary mr-1.5">
{waitingTeams}팀
</div>
<div className="flex items-center">
<Refresh className="icon-s" />
</div>
</div>

<div className="flex flex-row mt-8">
<div className="flex flex-row">
<div className="flex w-17.5 h-17.5 rounded-full bg-amber-50 border-[3.5px] border-white z-30"></div>
<div className="flex w-17.5 h-17.5 rounded-full bg-gray-200 border-[3.5px] border-white z-20 -ml-8 opacity-30"></div>
<div className="flex w-17.5 h-17.5 rounded-full bg-blue-100 border-[3.5px] border-white z-10 -ml-8 opacity-30"></div>
</div>
</div>
</div>
);
};

// 메인 카드 컴포넌트
const MainCard = (props: MainCardProps) => {
if (props.type === "waiting") {
Expand Down Expand Up @@ -310,6 +357,14 @@ const MainCard = (props: MainCardProps) => {
departmentId={props.departmentId}
/>
);
} else if (props.type === "myWaitingCard") {
return (
<MyWaitingCardComponent
storeName={props.storeName}
waitingTeams={props.waitingTeams}
onClick={props.onClick}
/>
);
}

return null;
Expand Down
39 changes: 33 additions & 6 deletions apps/nowait-user/src/pages/home/components/MyWaitingDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const MyWaitingDetail = ({

const handleScroll = () => {
const scrollLeft = container.scrollLeft;
const cardWidth = 288 + 6; // w-72 (288px) + gap-1.5 (6px)
const cardWidth = 288 + 24; // w-72 (288px) + gap-6 (24px)
const newIndex = Math.round(scrollLeft / cardWidth);
const boundedIndex = Math.max(0, Math.min(newIndex, items.length - 1));

Expand Down Expand Up @@ -147,18 +147,45 @@ const MyWaitingDetail = ({
{/* 카드 영역 */}
<div className="w-full flex-1 flex flex-col mb-7.5">
{/* 가로 스크롤 카드 컨테이너 */}
<div className="flex-1 flex">
<div className="flex-1 flex overflow-visible">
<div
ref={scrollContainerRef}
className="flex gap-1.5 overflow-x-auto scrollbar-hide w-full snap-x snap-mandatory"
className="flex gap-6 overflow-x-auto overflow-y-visible scrollbar-hide w-full snap-x snap-mandatory"
style={{
paddingLeft: "calc(50vw - 144px)",
paddingRight: "calc(50vw - 144px)",
paddingTop: "20px",
paddingBottom: "40px",
}}
>
{items.map((item, index) => (
<MainCard key={item.id} type="waiting" item={item} />
))}
{items.map((item, index) => {
// 현재 활성 카드 기준으로 회전 각도, 이동 거리, 높이 위치 계산
let rotation = 0;
let translateX = 0;
let translateY = 0;
if (index < currentIndex) {
rotation = -3; // 왼쪽 카드들은 -3도 회전 (반시계 방향)
translateX = -8; // 왼쪽으로 8px 이동
translateY = 8; // 아래로 8px 이동
} else if (index > currentIndex) {
rotation = 3; // 오른쪽 카드들은 3도 회전 (시계 방향)
translateX = 8; // 오른쪽으로 8px 이동
translateY = 8; // 아래로 8px 이동
}

return (
<div
key={item.id}
className="transition-transform duration-300 ease-out"
style={{
transform: `translateX(${translateX}px) translateY(${translateY}px) rotate(${rotation}deg)`,
transformOrigin: "center center",
}}
>
<MainCard type="waiting" item={item} />
</div>
);
})}
</div>
</div>
</div>
Expand Down
7 changes: 7 additions & 0 deletions packages/tailwind-config/shared-styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,13 @@ pre {
font-weight: 600;
}

.text-14-bold {
font-size: 14px;
line-height: 130%;
letter-spacing: 0em;
font-weight: 700;
}

.text-13-bold {
font-size: 13px;
line-height: 136%;
Expand Down