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
23 changes: 0 additions & 23 deletions .vscode/settings.json

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,9 @@ const Roulette = forwardRef<RouletteHandle, RouletteProps>(
{/* 고정된 그림자: 회전하지 않음 */}
<div className="absolute inset-0 rounded-full shadow-[0_0_24px_rgba(0,0,0,0.1),0_12px_12px_rgba(0,0,0,0.08)]" />

{/* 룰렛 이미지만 회전 */}
{/* 룰렛 이미지만 회전 (rounded-full과 will-change 추가로 회전 시 네 모서리 돌출 방지) */}
<div
className="absolute inset-0 transition-transform duration-[7000ms] ease-[cubic-bezier(0.12,0.9,0.08,1)]"
className="absolute inset-0 overflow-hidden rounded-full transition-transform duration-[7000ms] ease-[cubic-bezier(0.12,0.9,0.08,1)] will-change-transform"
style={{ transform: `rotate(${rotation}deg)` }}
>
<Image
Expand All @@ -160,7 +160,7 @@ const Roulette = forwardRef<RouletteHandle, RouletteProps>(
priority
fill
sizes="(max-width: 356px) 100vw, 356px"
className="block object-contain"
className="block rounded-full object-contain"
/>
</div>
</div>
Expand Down
141 changes: 141 additions & 0 deletions app/roulette/_components/RouletteCards.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
"use client";

import React from "react";
import Link from "next/link";
import Image from "next/image";
import { Check } from "lucide-react";

// ==========================================
// 1. 무료 룰렛 카드
// ==========================================
type FreeRouletteCardProps = {
remainingChances?: number;
};

export const FreeRouletteCard = ({
remainingChances = 1,
}: FreeRouletteCardProps) => {
return (
<div className="relative flex w-full flex-col justify-between gap-4 rounded-[24px] border border-white/30 bg-white/80 p-6 shadow-sm backdrop-blur-[15px]">
{/* Upper Content */}
<div className="flex w-full items-center justify-between gap-2">
{/* Left Info */}
<div className="flex flex-col justify-between gap-6">
<div className="flex flex-col gap-2">
<h3 className="typo-20-600 text-[#373737]">무료 룰렛</h3>
<p className="typo-14-500 h-[34px] leading-[17px] text-[#858585]">
매일 1회 참여 가능
<br />
행운을 확인해 보세요!
</p>
</div>

{/* Status Checkbox */}
<div className="flex items-center gap-2">
<div className="flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-gradient-to-br from-[#FF775E] via-[#FF4D61] to-[#E83ABC]">
<Check size={12} className="stroke-[3] text-white" />
</div>
<span className="typo-14-500 leading-[20px] text-[#858585]">
오늘 남은 횟수
<br />
<span className="text-color-brand-primary-flame">
{remainingChances}회
</span>
</span>
</div>
</div>

{/* Right Image Area */}
<div className="relative flex h-[130px] w-[130px] shrink-0 items-center justify-center">
{/* Shadow Ellipse */}
<div className="absolute bottom-1 h-[11px] w-[112px] rounded-full bg-black/30 blur-[6px]" />
{/* Free Roulette Graphic */}
<Image
src="/roulette/free.png"
alt="무료 룰렛"
width={120}
height={120}
className="relative z-10 object-contain drop-shadow-md"
/>
</div>
</div>

{/* Button */}
<Link
href="/roulette/free"
className="flex h-14 w-full items-center justify-center rounded-[16px] border border-white/30 bg-[#FF4D61] transition-transform hover:opacity-95 active:scale-[0.98]"
>
<span className="typo-20-600 text-white">무료 룰렛 입장</span>
</Link>
</div>
);
};

// ==========================================
// 2. 스페셜 룰렛 카드
// ==========================================
type SpecialRouletteCardProps = {
currentAmount?: number;
targetAmount?: number;
};

export const SpecialRouletteCard = ({
currentAmount = 2000,
targetAmount = 3000,
}: SpecialRouletteCardProps) => {
return (
<div className="relative flex w-full flex-col justify-between gap-4 rounded-[24px] border border-white/30 bg-white/80 p-6 shadow-sm backdrop-blur-[15px]">
{/* Upper Content */}
<div className="flex w-full items-center justify-between gap-2">
{/* Left Info */}
<div className="flex flex-col justify-between gap-6">
<div className="flex flex-col gap-2">
<h3 className="typo-20-600 text-[#373737]">스페셜 룰렛</h3>
<p className="typo-14-500 h-[34px] leading-[17px] text-[#858585]">
누적 {targetAmount.toLocaleString()}원 이상
<br />
결제 시 참여 가능
</p>
</div>

{/* Status Checkbox */}
<div className="flex items-center gap-2">
<div className="flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-[#E5E5E5]">
<Check size={12} className="stroke-[3] text-[#B3B3B3]" />
</div>
<span className="typo-14-500 leading-[20px] text-[#858585]">
현재 누적
<br />
<span className="text-color-brand-primary-flame">
{currentAmount.toLocaleString()}원
</span>{" "}
/ {targetAmount.toLocaleString()}원
</span>
</div>
</div>

{/* Right Image Area */}
<div className="relative flex h-[130px] w-[130px] shrink-0 items-center justify-center">
{/* Shadow Ellipse */}
<div className="absolute bottom-1 h-[11px] w-[112px] rounded-full bg-black/30 blur-[6px]" />
{/* Special Roulette Graphic */}
<Image
src="/roulette/special.png"
alt="스페셜 룰렛"
width={120}
height={120}
className="relative z-10 object-contain drop-shadow-md"
/>
</div>
</div>

{/* Button */}
<Link
href="/roulette/special"
className="flex h-14 w-full items-center justify-center rounded-[16px] border border-white/30 bg-gradient-to-r from-[#FB5E53] to-[#E53BAE] transition-transform hover:opacity-95 active:scale-[0.98]"
>
<span className="typo-20-600 text-white">스페셜 룰렛 입장</span>
</Link>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
"use client";
"use client";

import { BackButton } from "@/components/ui/BackButton";
import React from "react";

type PageHeaderProps = {
type RouletteHeaderProps = {
title?: React.ReactNode;
sidebar?: React.ReactNode;
};

const PageHeader = ({ title, sidebar }: PageHeaderProps) => {
const RouletteHeader = ({ title, sidebar }: RouletteHeaderProps) => {
return (
<header className="flex h-[64px] w-full items-center justify-between py-2">
<BackButton className="shrink-0" />
Expand All @@ -30,4 +30,4 @@ const PageHeader = ({ title, sidebar }: PageHeaderProps) => {
);
};

export default PageHeader;
export default RouletteHeader;
145 changes: 145 additions & 0 deletions app/roulette/_components/RouletteProbabilityBottomSheet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
"use client";

import React, { useState } from "react";
import { X } from "lucide-react";
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from "@/components/ui/drawer";
import { cn } from "@/lib/utils";

// 컴포넌트 내부에 직접 데이터 정의
const FREE_ROULETTE_ITEMS = [
{ reward: "옵션권 2장", probability: "39%" },
{ reward: "옵션권 5장", probability: "25%" },
{ reward: "풀세트", probability: "18%" },
{ reward: "뽑기권 5장", probability: "10%" },
];
Comment thread
dasosann marked this conversation as resolved.

const SPECIAL_ROULETTE_ITEMS = [
{ reward: "스타벅스 쿠폰", probability: "5%" },
{ reward: "10,000 코인", probability: "15%" },
{ reward: "5,000 코인", probability: "30%" },
{ reward: "1,000 코인", probability: "50%" },
];

export interface RouletteProbabilityBottomSheetProps {
/** 바텀시트를 열 트리거 엘리먼트 */
trigger: React.ReactNode;
/** 처음에 보여줄 기본 탭 (기본값: "free") */
defaultTab?: "free" | "special";
/** DrawerContent 추가 클래스명 */
className?: string;
}

export default function RouletteProbabilityBottomSheet({
trigger,
defaultTab = "free",
className,
}: RouletteProbabilityBottomSheetProps) {
const [activeTab, setActiveTab] = useState<"free" | "special">(defaultTab);

const currentItems =
activeTab === "free" ? FREE_ROULETTE_ITEMS : SPECIAL_ROULETTE_ITEMS;

return (
<Drawer>
<DrawerTrigger asChild>{trigger}</DrawerTrigger>

<DrawerContent
showHandle={false}
className={cn(
"z-[100] mx-auto w-full max-w-[430px] rounded-t-[16px] border-none bg-white p-0 outline-none",
className,
)}
>
{/* Header Section (DirectChargeDrawer 계좌이체 모달 헤더 구조 + 중앙 정렬) */}
<DrawerHeader className="px-6 pt-6 pb-0">
<div className="flex items-center justify-between">
<div className="w-5" />
<DrawerTitle className="typo-16-600 flex-1 text-center font-sans text-black">
당첨 확률
</DrawerTitle>
<DrawerClose aria-label="닫기" className="cursor-pointer">
<X size={20} className="text-color-gray-400" />
</DrawerClose>
</div>
<DrawerDescription className="sr-only">
룰렛 보상 및 당첨 확률 안내 바텀시트입니다.
</DrawerDescription>
</DrawerHeader>

{/* Content Section */}
<div className="flex max-h-[60vh] flex-col overflow-y-auto px-5 pt-8 pb-25">
{/* Tabs (Chips) - Frame 2612797 */}
<div className="mb-6 flex flex-row items-start gap-2">
<button
onClick={() => setActiveTab("free")}
className={cn(
"flex h-[33px] flex-row items-center justify-center gap-2 rounded-[99px] px-4 py-2 transition-colors",
activeTab === "free"
? "bg-[#1A1A1A] text-[#FFFFFF]"
: "box-border border border-[#E5E5E5] bg-[#F5F5F5] text-[#666666]",
)}
>
<span className="typo-14-500">무료 룰렛</span>
</button>
<button
onClick={() => setActiveTab("special")}
className={cn(
"flex h-[33px] flex-row items-center justify-center gap-2 rounded-[99px] px-4 py-2 transition-colors",
activeTab === "special"
? "bg-[#1A1A1A] text-[#FFFFFF]"
: "box-border border border-[#E5E5E5] bg-[#F5F5F5] text-[#666666]",
)}
>
<span className="typo-14-500">스페셜 룰렛</span>
</button>
</div>

{/* List Container - Frame 1171276922 */}
<div className="flex w-full flex-col items-start gap-2">
<span className="typo-16-700 text-[#373737]">
{activeTab === "free" ? "무료 룰렛" : "스페셜 룰렛"}
</span>

{/* Frame 1171276921 */}
<div className="flex w-full flex-col items-start p-2">
{/* Header - Frame 1171276915 */}
<div className="flex w-full flex-row items-center justify-between border-b-[2px] border-[#1A1A1A] py-2">
<span className="typo-14-600 text-[#1A1A1A]">보상</span>
<span className="typo-14-600 w-[44px] text-center text-[#1A1A1A]">
확률
</span>
</div>

{/* Items List - Frame 1171276920 */}
<div className="flex w-full flex-col items-start py-2">
{currentItems.map((item, index) => (
<div
key={index}
className="flex w-full flex-row items-center justify-between py-2"
>
<span className="typo-14-500 text-[#808080]">
{item.reward}
</span>
<span className="typo-14-500 w-[44px] text-center text-[#808080]">
{item.probability}
</span>
</div>
))}
</div>
</div>
</div>
</div>
</DrawerContent>
</Drawer>
);
}

export { RouletteProbabilityBottomSheet as RouletteProbabilityDrawer };
Loading
Loading