Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c24ef6e
feat: shadcn calendar 설치
JichanPark12 Jan 14, 2026
c269aa1
feat: shadcn calendar 사용
JichanPark12 Jan 14, 2026
b1b41f4
pnpm.lock merge conflict
JichanPark12 Jan 15, 2026
4b5c212
Merge branch 'dev' of https://github.com/boostcampwm2025/web10-beastc…
JichanPark12 Jan 15, 2026
77fc982
feat: pnpm.lock 변경
JichanPark12 Jan 15, 2026
4e5f461
chore: 사용하지 않는 import 제거
JichanPark12 Jan 15, 2026
556eafa
feat: 안쓰는 mock old로 변경 및 새로운 mock들 추가
JichanPark12 Jan 15, 2026
45e2dee
feat: svg 추가
JichanPark12 Jan 15, 2026
0ffa3a9
feat: 좌석 및 공연관련 타입 수정 추가
JichanPark12 Jan 15, 2026
1db6809
feat: 좌석관련 안쓰는 코드들 주석처리 및 데이터변경
JichanPark12 Jan 15, 2026
f4fcc76
feat: 공연정보. 공연장 정보 가져오는 함수 구현
JichanPark12 Jan 15, 2026
f033e9c
feat: 예매 섹션 백엔드 타입과 연동
JichanPark12 Jan 15, 2026
4a5ec6f
feat: 공연장 좌석 선택 백엔드 타입과 연동 및 사용 안하게 된 zoom관련 코드들 제외
JichanPark12 Jan 15, 2026
8f8fa10
Merge branch 'dev' of https://github.com/boostcampwm2025/web10-beastc…
JichanPark12 Jan 16, 2026
1f94318
Merge branch 'dev' into frontend-60
JichanPark12 Jan 16, 2026
832a95e
feat: ReservationTimeTracker 추가
JichanPark12 Jan 16, 2026
7847a71
chore: 잘못 들어간 파일 제거
JichanPark12 Jan 16, 2026
8d1e624
chore: pnpm-lock 변경
JichanPark12 Jan 19, 2026
44afebb
chore: ci 통과용 force-dynamic 설정
JichanPark12 Jan 19, 2026
86e52e7
chore: 임시 force-dynamic 설정
JichanPark12 Jan 19, 2026
78d00b5
chore: seatNum => colNum 타입 변경
JichanPark12 Jan 19, 2026
9b6c75f
Merge branch 'dev' into frontend-60
viixix Jan 19, 2026
8c4fc04
fix: pnpm-lock.yaml 수정정
viixix Jan 19, 2026
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
Empty file modified .github/scripts/detect-changes.sh
100755 → 100644
Empty file.
Empty file modified .github/scripts/test-detect-changes.sh
100755 → 100644
Empty file.
201 changes: 42 additions & 159 deletions frontend/app/_source/components/ticketing/DateSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,92 +1,34 @@
import { Calendar } from "@/components/ui/calendar";
import { isSunday } from "date-fns";
import { ko } from "date-fns/locale";
import { ChevronUp } from "lucide-react";
import { useState } from "react";
import { ChevronLeft, ChevronRight, ChevronUp } from "lucide-react";
import { Session } from "@/types/performance";

interface DateSelectorProps {
selectedDate: number | null;
onDateSelect: (day: number) => void;
performanceDate?: string;
selectedDate: Date | undefined;
onDateSelect: (date: Date | undefined) => void;
sessions?: Session[];
}

const weekDays = ["일", "월", "화", "수", "목", "금", "토"];

const getDaysInMonth = (date: Date) => {
const year = date.getFullYear();
const month = date.getMonth();
const firstDay = new Date(year, month, 1).getDay();
const daysInMonth = new Date(year, month + 1, 0).getDate();
return { firstDay, daysInMonth };
};

export default function DateSelector({
selectedDate,
onDateSelect,
performanceDate,
sessions,
}: DateSelectorProps) {
// performanceDate가 있으면 그 월로, 없으면 2026년 1월
const getInitialMonth = () => {
if (performanceDate) {
const date = new Date(performanceDate);
return new Date(date.getFullYear(), date.getMonth());
}
return new Date(2026, 0);
};

const [currentMonth, setCurrentMonth] = useState(getInitialMonth());
const [isOpen, setIsOpen] = useState(true);

const { firstDay, daysInMonth } = getDaysInMonth(currentMonth);

// performanceDate에서 날짜 추출
const performanceDay = performanceDate
? new Date(performanceDate).getDate()
: null;
const performanceMonth = performanceDate
? new Date(performanceDate).getMonth()
: null;
const performanceYear = performanceDate
? new Date(performanceDate).getFullYear()
: null;

const handlePrevMonth = () => {
setCurrentMonth(
new Date(currentMonth.getFullYear(), currentMonth.getMonth() - 1)
);
};

const handleNextMonth = () => {
setCurrentMonth(
new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1)
);
};

const handleDateClick = (day: number) => {
// 현재 달력의 년/월이 performance 년/월과 같고, 날짜도 일치할 때만 선택 가능
const currentYear = currentMonth.getFullYear();
const currentMonthIndex = currentMonth.getMonth();

// performanceDate가 없으면 선택 불가
if (
performanceYear === null ||
performanceMonth === null ||
performanceDay === null
) {
return;
}

// 년, 월, 일이 모두 일치해야만 선택 가능
if (
performanceYear === currentYear &&
performanceMonth === currentMonthIndex &&
performanceDay === day
) {
onDateSelect(day);
}
};
const availableDates =
sessions?.map((s) => new Date(s.sessionDate).toDateString()) || [];
const defaultMonth =
sessions && sessions.length > 0
? new Date(sessions[0].sessionDate)
: undefined;

return (
<div className="mb-4 bg-white rounded-xl p-4 text-gray-900">
<div className="mb-4 bg-white rounded-xl p-4 text-gray-900 min-w-80">
<div
className="flex items-center justify-between mb-3 cursor-pointer"
className="flex items-center justify-between cursor-pointer"
onClick={() => setIsOpen(!isOpen)}
>
<h3 className="text-base">관람일</h3>
Expand All @@ -98,90 +40,31 @@ export default function DateSelector({
</div>

{isOpen && (
<>
<div className="flex items-center justify-between mb-3">
<button
onClick={handlePrevMonth}
className="p-1 hover:bg-gray-100 rounded-lg transition-colors"
>
<ChevronLeft className="w-4 h-4" />
</button>
<div className="text-sm">
{currentMonth.getFullYear()}.{" "}
{String(currentMonth.getMonth() + 1).padStart(2, "0")}
</div>
<button
onClick={handleNextMonth}
className="p-1 hover:bg-gray-100 rounded-lg transition-colors"
>
<ChevronRight className="w-4 h-4" />
</button>
</div>

<div className="grid grid-cols-7 gap-1 mb-1">
{weekDays.map((day, index) => (
<div
key={day}
className={`text-center text-xs py-1 ${
index === 0
? "text-red-500"
: index === 6
? "text-blue-500"
: "text-gray-600"
}`}
>
{day}
</div>
))}
</div>

<div className="grid grid-cols-7 gap-1">
{Array.from({ length: firstDay }, (_, i) => (
<div key={`empty-${i}`} className="aspect-square" />
))}
{Array.from({ length: daysInMonth }, (_, i) => {
const day = i + 1;
const currentYear = currentMonth.getFullYear();
const currentMonthIndex = currentMonth.getMonth();

const isAvailable =
performanceYear !== null &&
performanceMonth !== null &&
performanceDay !== null &&
performanceYear === currentYear &&
performanceMonth === currentMonthIndex &&
performanceDay === day;

const isSelected = selectedDate === day;
const dayOfWeek = (firstDay + i) % 7;

return (
<button
key={day}
onClick={() => handleDateClick(day)}
disabled={!isAvailable}
className={`aspect-square rounded-lg flex items-center justify-center text-xs transition-all ${
isSelected
? "bg-blue-600 text-white scale-110"
: isAvailable
? "hover:bg-blue-50 cursor-pointer bg-blue-100"
: "cursor-not-allowed"
} ${
!isAvailable
? "text-gray-300"
: dayOfWeek === 0
? "text-red-500"
: dayOfWeek === 6
? "text-blue-500"
: "text-gray-900"
}`}
>
{day}
</button>
);
})}
</div>
</>
<div className="flex justify-center mt-3">
<Calendar
mode="single"
selected={selectedDate}
onSelect={(date) => onDateSelect(date)}
className="rounded-md border shadow-sm border-"
defaultMonth={defaultMonth}
locale={ko}
classNames={{
weekdays: "flex bg-gray-50 p-1 rounded-r-full rounded-l-full",
day_button:
"!bg-transparent focus:!ring-0 focus:!outline-none transition-none",
selected: "!bg-blue-500 !rounded-full ",
}}
modifiers={{
isSunday: (date) => isSunday(date),
}}
modifiersClassNames={{
isSunday: "text-red-500",
}}
disabled={(date) => {
return !availableDates.includes(date.toDateString());
}}
/>
</div>
)}
</div>
);
Expand Down
67 changes: 39 additions & 28 deletions frontend/app/_source/components/ticketing/PerformanceInfo.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,40 @@
import { Performance } from "@/types/performance";
import { Calendar, MapPin, DollarSign, TrendingUp } from "lucide-react";

const getDifficultyColor = (difficulty: string) => {
switch (difficulty) {
case "상":
return "bg-red-100 text-red-700";
case "중":
return "bg-yellow-100 text-yellow-700";
case "하":
return "bg-green-100 text-green-700";
default:
return "bg-gray-100 text-gray-700";
}
};
import { Performance, Session } from "@/types/performance";
import { Calendar, MapPin } from "lucide-react";

interface PerformanceInfoProps {
performance: Performance;
sessions?: Session[];
venueName?: string;
}

export default function PerformanceInfo({ performance }: PerformanceInfoProps) {
const performanceDate = new Date(performance.performance_date);
export default function PerformanceInfo({
performance,
sessions,
venueName,
}: PerformanceInfoProps) {
let dateDisplay = "";

if (sessions && sessions.length > 0) {
const dates = sessions.map((s) => new Date(s.sessionDate).getTime());
const minDate = new Date(Math.min(...dates));
const maxDate = new Date(Math.max(...dates));

console.log(performanceDate);
const formattedDate = performanceDate.toLocaleDateString("ko-KR", {
year: "numeric",
month: "long",
day: "numeric",
hour: "numeric",
minute: "numeric",
});
const formatDate = (d: Date) =>
d.toLocaleDateString("ko-KR", {
year: "numeric",
month: "long",
day: "numeric",
});

if (minDate.getTime() === maxDate.getTime()) {
dateDisplay = formatDate(minDate);
} else {
dateDisplay = `${formatDate(minDate)} ~ ${formatDate(maxDate)}`;
}
}

return (
<div>
<div className="h-full flex flex-col justify-center w-full">
<div className="inline-block bg-white/20 backdrop-blur-sm px-4 py-2 rounded-full mb-4">
<span className="text-sm">다음 티켓팅</span>
</div>
Expand All @@ -43,11 +46,19 @@ export default function PerformanceInfo({ performance }: PerformanceInfoProps) {
<div className="space-y-3 mb-6">
<div className="flex items-center gap-3">
<Calendar className="w-5 h-5 text-white/80" />
<span>{formattedDate}</span>
<span>
{dateDisplay || (
<span className="animate-pulse bg-white/20 rounded h-5 w-48 inline-block" />
)}
</span>
</div>
<div className="flex items-center gap-3">
<MapPin className="w-5 h-5 text-white/80" />
<span>{performance.venue_name}</span>
<span>
{venueName || (
<span className="animate-pulse bg-white/20 rounded h-5 w-32 inline-block" />
)}
</span>
</div>
</div>
</div>
Expand Down
Loading