-
-
-
-
-
+ {summary ? (
+
+
+
+
+
+
+
-
+
-
- {records.length === 0 ? (
-
- 출석 기록이 없습니다.
-
- ) : (
- records.map((record) => (
-
-
-
- {record.statusLabel}
-
-
{record.title}
+
+ {records.length === 0 ? (
+
+ 출석 기록이 없습니다.
+
+ ) : (
+ records.map((record) => (
+
+
+
+ {record.statusLabel}
+
+ {record.title}
+
+
+ 날짜 : {record.date}
+ 장소 : {record.location}
+
-
- 날짜 : {record.date}
- 장소 : {record.location}
-
-
- ))
- )}
+ ))
+ )}
+
-
+ ) : (
+
+ 출석 정보를 불러올 수 없습니다.
+
+ )}
);
}
diff --git a/src/components/attendance/AttendanceQRContent.tsx b/src/components/attendance/AttendanceQRContent.tsx
index 43789e8f..a669e385 100644
--- a/src/components/attendance/AttendanceQRContent.tsx
+++ b/src/components/attendance/AttendanceQRContent.tsx
@@ -1,9 +1,7 @@
'use client';
-import Image from 'next/image';
import Link from 'next/link';
-import { AttendanceQRIcon } from '@/assets/icons';
import {
Breadcrumb,
BreadcrumbList,
@@ -12,16 +10,19 @@ import {
BreadcrumbPage,
BreadcrumbSeparator,
} from '@/components/ui';
-import { useRemainingTime } from '@/hooks/useRemainingTime';
+import { useAttendanceQR } from '@/hooks/useAttendanceQR';
+import { useClubId } from '@/stores/useClubStore';
interface AttendanceQRContentProps {
- title: string;
- code: string;
- endTime: string;
+ sessionId: number;
}
-function AttendanceQRContent({ title, code, endTime }: AttendanceQRContentProps) {
- const { minutes, seconds, isExpired } = useRemainingTime(endTime);
+function AttendanceQRContent({ sessionId }: AttendanceQRContentProps) {
+ const clubId = useClubId();
+ const { qrRef, qrData, isLoading, minutes, seconds, isExpired } = useAttendanceQR(
+ clubId,
+ sessionId,
+ );
return (
@@ -55,19 +56,27 @@ function AttendanceQRContent({ title, code, endTime }: AttendanceQRContentProps)
-
-
-
-
-
출석 가능 시간
-
- {isExpired ? '마감' : `${minutes}:${seconds}`}
-
+ {isLoading ? (
+
-
QR코드는 모바일만 제공하고 있어요.
-
+ ) : (
+ <>
+
+
+
+
+ 출석 가능 시간
+
+ {isExpired ? '마감' : `${minutes}:${seconds}`}
+
+
+
QR코드는 모바일만 제공하고 있어요.
+
-
{code}
+
{qrData?.code}
+ >
+ )}
diff --git a/src/components/attendance/AttendanceTodayCard.tsx b/src/components/attendance/AttendanceTodayCard.tsx
index 84e33614..3fdc7cff 100644
--- a/src/components/attendance/AttendanceTodayCard.tsx
+++ b/src/components/attendance/AttendanceTodayCard.tsx
@@ -7,7 +7,6 @@ import { useState } from 'react';
import { CompleteIcon } from '@/assets/icons';
import { Card } from '@/components/ui';
import { AttendanceCodeModal } from '@/components/attendance/AttendanceCodeModal';
-import { AttendanceCompleteModal } from '@/components/attendance/AttendanceCompleteModal';
import { toastError } from '@/stores/useToastStore';
interface AttendanceTodayCardProps {
@@ -17,8 +16,10 @@ interface AttendanceTodayCardProps {
start: string;
endTime: string;
location: string;
+ sessionId?: number | null;
isAdmin?: boolean;
isChecked?: boolean;
+ disabled?: boolean;
onAttendanceComplete?: (code: string) => void;
}
@@ -41,17 +42,22 @@ function AttendanceTodayCard({
start,
endTime,
location,
+ sessionId,
isAdmin = false,
isChecked = false,
+ disabled = false,
onAttendanceComplete,
}: AttendanceTodayCardProps) {
const router = useRouter();
const [codeModalOpen, setCodeModalOpen] = useState(false);
- const [completeModalOpen, setCompleteModalOpen] = useState(false);
- function handleCodeConfirm(code: string) {
- onAttendanceComplete?.(code);
- setCompleteModalOpen(true);
+ function handleSecondaryClick() {
+ if (!isAdmin) {
+ toastError('관리자만 사용할 수 있는 기능입니다.');
+ return;
+ }
+ if (sessionId == null) return;
+ router.push(`/attendance/qr?sessionId=${sessionId}`);
}
return (
@@ -62,14 +68,12 @@ function AttendanceTodayCard({
title={title}
description={description}
showArrow={false}
- onPrimaryClick={isChecked ? () => setCompleteModalOpen(true) : () => setCodeModalOpen(true)}
+ onPrimaryClick={() => setCodeModalOpen(true)}
primaryButtonText={isChecked ? '출석 완료' : '출석하기'}
- onSecondaryClick={
- isAdmin
- ? () => router.push('/attendance/qr')
- : () => toastError('관리자만 사용할 수 있는 기능입니다.')
- }
+ primaryButtonDisabled={disabled || isChecked}
+ onSecondaryClick={handleSecondaryClick}
secondaryButtonText="출석코드 확인"
+ secondaryButtonDisabled={disabled || sessionId == null}
>
{isChecked &&
}
@@ -77,14 +81,12 @@ function AttendanceTodayCard({
onAttendanceComplete?.(code)}
title={title}
start={start}
endTime={endTime}
location={location}
/>
-
-
>
);
}
diff --git a/src/components/home/HomePageSections.tsx b/src/components/home/HomePageSections.tsx
index 407d6990..87a3012a 100644
--- a/src/components/home/HomePageSections.tsx
+++ b/src/components/home/HomePageSections.tsx
@@ -1,6 +1,6 @@
'use client';
-import { Suspense, useEffect } from 'react';
+import { Suspense } from 'react';
import {
BannerSkeleton,
LeftContainerSkeleton,
@@ -13,21 +13,9 @@ import {
MainContainer,
RightContainer,
} from '@/components/home/DynamicSections';
-import { useHomeQuery } from '@/hooks/home';
import { Header } from '@/components/layout';
-import { useUserActions } from '@/stores';
export function HomePageSections() {
- const { setUser } = useUserActions();
- const { data: myUserInfo } = useHomeQuery({
- select: (data) => data.myInfo.userInfo,
- });
-
- useEffect(() => {
- if (!myUserInfo) return;
- setUser(myUserInfo);
- }, [myUserInfo, setUser]);
-
return (
<>
}>
diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx
index b1933890..09e9430d 100644
--- a/src/components/ui/card.tsx
+++ b/src/components/ui/card.tsx
@@ -30,6 +30,8 @@ interface CardProps extends React.ComponentProps<'div'>, VariantProps void;
secondaryButtonText?: string;
onSecondaryClick?: () => void;
+ primaryButtonDisabled?: boolean;
+ secondaryButtonDisabled?: boolean;
/** arrow 아이콘 표시 여부 (기본값: true) */
showArrow?: boolean;
}
@@ -45,6 +47,8 @@ function Card({
onPrimaryClick,
secondaryButtonText = '출석코드 확인',
onSecondaryClick,
+ primaryButtonDisabled,
+ secondaryButtonDisabled,
showArrow = true,
children,
...props
@@ -94,12 +98,24 @@ function Card({
{onPrimaryClick && (
-