From 5d33ba74d56c5db369b1831868f3abb4c5a76336 Mon Sep 17 00:00:00 2001 From: yeun38 Date: Tue, 6 Jan 2026 21:29:14 +0900 Subject: [PATCH 1/4] =?UTF-8?q?layout=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 3 ++- src/app/(service)/home/page.tsx | 5 +++-- src/app/(service)/insights/page.tsx | 5 +++-- src/app/(service)/payment/complete/page.tsx | 7 ++++--- src/components/contents/payment-page-content.tsx | 3 +-- src/components/layout/page-container.tsx | 15 +++++++++++++++ src/components/pages/group-study-list-page.tsx | 9 +++++---- src/components/pages/premium-study-list-page.tsx | 13 +++++++------ src/components/payment/paymentActionClient.tsx | 7 ++++++- src/components/summary/order-summary.tsx | 16 +++++++++++++++- 10 files changed, 61 insertions(+), 22 deletions(-) create mode 100644 src/components/layout/page-container.tsx diff --git a/.env.example b/.env.example index 26b15ac4..a1e86cf2 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,4 @@ NEXT_PUBLIC_API_BASE_URL= NEXT_PUBLIC_KAKAO_CLIENT_ID= -NEXT_PUBLIC_GOOGLE_CLIENT_ID= \ No newline at end of file +NEXT_PUBLIC_GOOGLE_CLIENT_ID= +NEXT_PUBLIC_TOSS_CLIENT_KEY= \ No newline at end of file diff --git a/src/app/(service)/home/page.tsx b/src/app/(service)/home/page.tsx index f2f26da0..59934ad0 100644 --- a/src/app/(service)/home/page.tsx +++ b/src/app/(service)/home/page.tsx @@ -1,4 +1,5 @@ import { Metadata } from 'next'; +import PageContainer from '@/components/layout/page-container'; import StudyCard from '@/features/study/schedule/ui/study-card'; import { generateMetadata as generateSEOMetadata } from '@/utils/seo'; import Banner from '@/widgets/home/banner'; @@ -15,7 +16,7 @@ export const metadata: Metadata = generateSEOMetadata({ export default async function Home() { return ( -
+
@@ -24,6 +25,6 @@ export default async function Home() { -
+
); } diff --git a/src/app/(service)/insights/page.tsx b/src/app/(service)/insights/page.tsx index 682f9bf7..c725e109 100644 --- a/src/app/(service)/insights/page.tsx +++ b/src/app/(service)/insights/page.tsx @@ -6,6 +6,7 @@ import { fetchArticles, fetchCategories, } from '@/api/strapi/api/fetch-articles'; +import PageContainer from '@/components/layout/page-container'; import { generateMetadata as generateSEOMetadata } from '@/utils/seo'; import { getServerCookie } from '@/utils/server-cookie'; import Sidebar from '@/widgets/home/sidebar'; @@ -58,7 +59,7 @@ export default async function BlogPage({ searchParams }: BlogPageProps) { const articles = articlesRes.data ?? []; return ( -
+
@@ -144,6 +145,6 @@ export default async function BlogPage({ searchParams }: BlogPageProps) { )}
{isLoggedIn && } -
+
); } diff --git a/src/app/(service)/payment/complete/page.tsx b/src/app/(service)/payment/complete/page.tsx index 2d07c9ed..2a6def22 100644 --- a/src/app/(service)/payment/complete/page.tsx +++ b/src/app/(service)/payment/complete/page.tsx @@ -2,6 +2,7 @@ import Image from 'next/image'; import { useRouter } from 'next/navigation'; +import PageContainer from '@/components/layout/page-container'; import Button from '@/components/ui/button'; interface PaymentResult { @@ -26,8 +27,8 @@ export default function PaymentCompletePage() { const formatKRW = (n: number) => `${n.toLocaleString('ko-KR')}원`; return ( -
-
+ +
{/* 아이콘 */}
@@ -93,7 +94,7 @@ export default function PaymentCompletePage() {
-
+ ); } diff --git a/src/components/contents/payment-page-content.tsx b/src/components/contents/payment-page-content.tsx index 15abf023..19791871 100644 --- a/src/components/contents/payment-page-content.tsx +++ b/src/components/contents/payment-page-content.tsx @@ -26,8 +26,6 @@ export default function PaymentPageContent({ id }: PaymentPageContentProps) { const data = result.data; - console.log(data); - return (
@@ -40,6 +38,7 @@ export default function PaymentPageContent({ id }: PaymentPageContentProps) { groupStudyTitle={data?.groupStudyTitle} amount={data.amount} description={data.groupStudyDescription} + thumbnailUrl={data?.groupStudyThumbnailUrl} /> diff --git a/src/components/layout/page-container.tsx b/src/components/layout/page-container.tsx new file mode 100644 index 00000000..60662d7e --- /dev/null +++ b/src/components/layout/page-container.tsx @@ -0,0 +1,15 @@ +import { ReactNode } from 'react'; +import { clsx } from 'clsx'; + +interface PageContainerProps { + children: ReactNode; + className?: string; +} + +export default function PageContainer({ children, className }: PageContainerProps) { + return ( +
+ {children} +
+ ); +} diff --git a/src/components/pages/group-study-list-page.tsx b/src/components/pages/group-study-list-page.tsx index 686d3305..79d064c7 100644 --- a/src/components/pages/group-study-list-page.tsx +++ b/src/components/pages/group-study-list-page.tsx @@ -12,6 +12,7 @@ import StudyFilter, { StudyFilterValues, } from '@/components/filtering/study-filter'; import StudySearch from '@/components/filtering/study-search'; +import PageContainer from '@/components/layout/page-container'; import Button from '@/components/ui/button'; import { useGetStudies } from '@/hooks/queries/study-query'; import GroupStudyFormModal from '../../features/study/group/ui/group-study-form-modal'; @@ -123,16 +124,16 @@ export default function GroupStudyListPage() { if (isLoading) { return ( -
+
로딩 중...
-
+ ); } return ( -
+ {/* 헤더 */}

@@ -170,6 +171,6 @@ export default function GroupStudyListPage() { totalPages={totalPages} /> )} -

+
); } diff --git a/src/components/pages/premium-study-list-page.tsx b/src/components/pages/premium-study-list-page.tsx index caa8bf2d..efaca947 100644 --- a/src/components/pages/premium-study-list-page.tsx +++ b/src/components/pages/premium-study-list-page.tsx @@ -8,12 +8,13 @@ import type { GetGroupStudiesTargetRolesEnum, GetGroupStudiesMethodEnum, } from '@/api/openapi/api/group-study-management-api'; -import PremiumStudyList from '@/components/premium/premium-study-list'; -import PremiumStudyPagination from '@/components/premium/premium-study-pagination'; import StudyFilter, { StudyFilterValues, } from '@/components/filtering/study-filter'; import StudySearch from '@/components/filtering/study-search'; +import PageContainer from '@/components/layout/page-container'; +import PremiumStudyList from '@/components/premium/premium-study-list'; +import PremiumStudyPagination from '@/components/premium/premium-study-pagination'; import Button from '@/components/ui/button'; import GroupStudyFormModal from '@/features/study/group/ui/group-study-form-modal'; import { useGetStudies } from '@/hooks/queries/study-query'; @@ -123,16 +124,16 @@ export default function PremiumStudyListPage() { if (isLoading) { return ( -
+
로딩 중...
-
+ ); } return ( -
+ {/* 헤더 */}

@@ -170,6 +171,6 @@ export default function PremiumStudyListPage() { totalPages={totalPages} /> )} -

+
); } diff --git a/src/components/payment/paymentActionClient.tsx b/src/components/payment/paymentActionClient.tsx index 0d863c66..4ac322ef 100644 --- a/src/components/payment/paymentActionClient.tsx +++ b/src/components/payment/paymentActionClient.tsx @@ -14,7 +14,7 @@ interface Props { } type PaymentMethod = 'CARD' | 'VIRTUAL_ACCOUNT'; -const clientKey = process.env.NEXT_PUBLIC_TOSS_CLIENT_KEY!; +const clientKey = process.env.NEXT_PUBLIC_TOSS_CLIENT_KEY ?? ''; const methods: { id: PaymentMethod; label: string }[] = [ { id: 'CARD', label: '신용카드 결제' }, @@ -103,6 +103,11 @@ export default function PaymentCheckoutPage({ study }: Props) { useEffect(() => { async function fetchPayment() { + if (!clientKey) { + console.error('NEXT_PUBLIC_TOSS_CLIENT_KEY 환경변수가 설정되지 않았습니다.'); + return; + } + try { const tossPayments = await loadTossPayments(clientKey); diff --git a/src/components/summary/order-summary.tsx b/src/components/summary/order-summary.tsx index f55100a9..029a8ad7 100644 --- a/src/components/summary/order-summary.tsx +++ b/src/components/summary/order-summary.tsx @@ -1,18 +1,32 @@ +import Image from 'next/image'; + interface Props { groupStudyTitle: string; description: string; amount: number; + thumbnailUrl?: string; } export default function OrderSummary({ groupStudyTitle, amount, description, + thumbnailUrl, }: Props) { return (
-
+ {thumbnailUrl ? ( + {groupStudyTitle} + ) : ( +
+ )}

{groupStudyTitle}

From d0f56d5234d7a24fb16e431dc2c27dab7abd61fd Mon Sep 17 00:00:00 2001 From: yeun38 Date: Tue, 6 Jan 2026 22:51:25 +0900 Subject: [PATCH 2/4] =?UTF-8?q?=EC=8B=A0=EC=B2=AD=ED=95=98=EA=B8=B0=20?= =?UTF-8?q?=EB=B2=84=ED=8A=BC=20=EC=A1=B0=EA=B1=B4=20=EB=B0=8F=20=EA=B2=80?= =?UTF-8?q?=EC=83=89=20=ED=95=84=ED=84=B0=20=EB=B2=84=EA=B7=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../contents/payment-page-content.tsx | 2 +- src/components/filtering/study-filter.tsx | 4 +- src/components/filtering/study-search.tsx | 45 +++++----- src/components/layout/header-nav.tsx | 39 +++++++++ .../pages/group-study-list-page.tsx | 56 +++++++----- .../pages/premium-study-list-page.tsx | 56 +++++++----- .../section/premium-study-info-section.tsx | 5 +- src/components/summary/study-info-summary.tsx | 85 ++++++++++++------- src/hooks/queries/study-query.ts | 3 +- src/widgets/home/header.tsx | 9 +- 10 files changed, 194 insertions(+), 110 deletions(-) create mode 100644 src/components/layout/header-nav.tsx diff --git a/src/components/contents/payment-page-content.tsx b/src/components/contents/payment-page-content.tsx index 19791871..1e66bff0 100644 --- a/src/components/contents/payment-page-content.tsx +++ b/src/components/contents/payment-page-content.tsx @@ -38,7 +38,7 @@ export default function PaymentPageContent({ id }: PaymentPageContentProps) { groupStudyTitle={data?.groupStudyTitle} amount={data.amount} description={data.groupStudyDescription} - thumbnailUrl={data?.groupStudyThumbnailUrl} + thumbnailUrl={data?.groupStudyImage.resizedImages[0].resizedImageUrl} /> diff --git a/src/components/filtering/study-filter.tsx b/src/components/filtering/study-filter.tsx index 75165a41..62f98e17 100644 --- a/src/components/filtering/study-filter.tsx +++ b/src/components/filtering/study-filter.tsx @@ -82,10 +82,10 @@ function FilterDropdown({ + diff --git a/src/components/layout/header-nav.tsx b/src/components/layout/header-nav.tsx new file mode 100644 index 00000000..05a6a613 --- /dev/null +++ b/src/components/layout/header-nav.tsx @@ -0,0 +1,39 @@ +'use client'; + +import Link from 'next/link'; +import { usePathname } from 'next/navigation'; +import { cn } from '@/components/ui/(shadcn)/lib/utils'; + +interface HeaderNavProps { + isLoggedIn: boolean; +} + +const NAV_ITEMS = [ + { href: '/home', loginRequired: true, label: '1:1 스터디' }, + { href: '/group-study', loginRequired: false, label: '그룹스터디' }, + { href: '/premium-study', loginRequired: false, label: '멘토스터디' }, + { href: '/insights', loginRequired: false, label: '인사이트' }, +]; + +export default function HeaderNav({ isLoggedIn }: HeaderNavProps) { + const pathname = usePathname(); + + return ( + + ); +} diff --git a/src/components/pages/group-study-list-page.tsx b/src/components/pages/group-study-list-page.tsx index 79d064c7..da167db6 100644 --- a/src/components/pages/group-study-list-page.tsx +++ b/src/components/pages/group-study-list-page.tsx @@ -2,7 +2,7 @@ import { Plus } from 'lucide-react'; import { useRouter, useSearchParams } from 'next/navigation'; -import { useCallback, useMemo } from 'react'; +import { useCallback, useMemo, useState } from 'react'; import type { GetGroupStudiesTypeEnum, GetGroupStudiesTargetRolesEnum, @@ -19,10 +19,15 @@ import GroupStudyFormModal from '../../features/study/group/ui/group-study-form- import GroupStudyPagination from '../../features/study/group/ui/group-study-pagination'; import GroupStudyList from '../lists/group-study-list'; +const PAGE_SIZE = 15; + export default function GroupStudyListPage() { const router = useRouter(); const searchParams = useSearchParams(); + // 로컬 검색 상태 + const [searchQuery, setSearchQuery] = useState(''); + // URL에서 필터 값 읽기 const filterValues = useMemo(() => { const type = searchParams.get('type')?.split(',').filter(Boolean) ?? []; @@ -34,15 +39,13 @@ export default function GroupStudyListPage() { return { type, targetRoles, method, inProgress }; }, [searchParams]); - // URL에서 검색어 읽기 - const searchQuery = searchParams.get('search') ?? ''; const currentPage = Number(searchParams.get('page')) || 1; - // API 호출 + // 검색어가 있으면 전체 데이터를 가져오고, 없으면 페이지네이션된 데이터를 가져옴 const { data, isLoading } = useGetStudies({ classification: 'GROUP_STUDY', - page: currentPage, - pageSize: 15, + page: searchQuery ? 1 : currentPage, + pageSize: searchQuery ? 10000 : PAGE_SIZE, type: filterValues.type.length > 0 ? (filterValues.type as GetGroupStudiesTypeEnum[]) @@ -58,8 +61,7 @@ export default function GroupStudyListPage() { inProgress: filterValues.inProgress || undefined, }); - const studies = data?.content ?? []; - const totalPages = data?.totalPages ?? 1; + const allStudies = useMemo(() => data?.content ?? [], [data?.content]); // URL 파라미터 업데이트 함수 const updateSearchParams = useCallback( @@ -101,26 +103,34 @@ export default function GroupStudyListPage() { [updateSearchParams], ); - // 검색 핸들러 - const handleSearch = useCallback( - (query: string) => { - updateSearchParams({ search: query || undefined }); - }, - [updateSearchParams], - ); + // 검색 핸들러 (로컬 상태만 업데이트) + const handleSearch = useCallback((query: string) => { + setSearchQuery(query); + }, []); - // 클라이언트 사이드 검색 필터링 (API에서 검색을 지원하지 않는 경우) + // 클라이언트 사이드 검색 필터링 (스터디명만 검색) const filteredStudies = useMemo(() => { - if (!searchQuery) return studies; + if (!searchQuery) return allStudies; const lowerQuery = searchQuery.toLowerCase(); - return studies.filter( - (study) => - study.simpleDetailInfo?.title?.toLowerCase().includes(lowerQuery) || - study.simpleDetailInfo?.summary?.toLowerCase().includes(lowerQuery), + return allStudies.filter((study) => + study.simpleDetailInfo?.title?.toLowerCase().includes(lowerQuery), ); - }, [studies, searchQuery]); + }, [allStudies, searchQuery]); + + // 검색어가 있을 때는 클라이언트에서 페이지네이션, 없으면 서버 페이지네이션 사용 + const totalPages = searchQuery + ? Math.ceil(filteredStudies.length / PAGE_SIZE) || 1 + : data?.totalPages ?? 1; + + const displayStudies = useMemo(() => { + if (!searchQuery) return filteredStudies; + + const startIndex = (currentPage - 1) * PAGE_SIZE; + + return filteredStudies.slice(startIndex, startIndex + PAGE_SIZE); + }, [filteredStudies, searchQuery, currentPage]); if (isLoading) { return ( @@ -162,7 +172,7 @@ export default function GroupStudyListPage() {
{/* 스터디 카드 그리드 */} - + {/* 페이지네이션 */} {totalPages > 1 && ( diff --git a/src/components/pages/premium-study-list-page.tsx b/src/components/pages/premium-study-list-page.tsx index efaca947..39f4f0ec 100644 --- a/src/components/pages/premium-study-list-page.tsx +++ b/src/components/pages/premium-study-list-page.tsx @@ -2,7 +2,7 @@ import { Plus } from 'lucide-react'; import { useRouter, useSearchParams } from 'next/navigation'; -import { useCallback, useMemo } from 'react'; +import { useCallback, useMemo, useState } from 'react'; import type { GetGroupStudiesTypeEnum, GetGroupStudiesTargetRolesEnum, @@ -19,10 +19,15 @@ import Button from '@/components/ui/button'; import GroupStudyFormModal from '@/features/study/group/ui/group-study-form-modal'; import { useGetStudies } from '@/hooks/queries/study-query'; +const PAGE_SIZE = 15; + export default function PremiumStudyListPage() { const router = useRouter(); const searchParams = useSearchParams(); + // 로컬 검색 상태 + const [searchQuery, setSearchQuery] = useState(''); + // URL에서 필터 값 읽기 const filterValues = useMemo(() => { const type = searchParams.get('type')?.split(',').filter(Boolean) ?? []; @@ -34,15 +39,13 @@ export default function PremiumStudyListPage() { return { type, targetRoles, method, inProgress }; }, [searchParams]); - // URL에서 검색어 읽기 - const searchQuery = searchParams.get('search') ?? ''; const currentPage = Number(searchParams.get('page')) || 1; - // API 호출 + // 검색어가 있으면 전체 데이터를 가져오고, 없으면 페이지네이션된 데이터를 가져옴 const { data, isLoading } = useGetStudies({ classification: 'PREMIUM_STUDY', - page: currentPage, - pageSize: 9, + page: searchQuery ? 1 : currentPage, + pageSize: searchQuery ? 10000 : PAGE_SIZE, type: filterValues.type.length > 0 ? (filterValues.type as GetGroupStudiesTypeEnum[]) @@ -58,8 +61,7 @@ export default function PremiumStudyListPage() { inProgress: filterValues.inProgress || undefined, }); - const studies = data?.content ?? []; - const totalPages = data?.totalPages ?? 1; + const allStudies = useMemo(() => data?.content ?? [], [data?.content]); // URL 파라미터 업데이트 함수 const updateSearchParams = useCallback( @@ -101,26 +103,34 @@ export default function PremiumStudyListPage() { [updateSearchParams], ); - // 검색 핸들러 - const handleSearch = useCallback( - (query: string) => { - updateSearchParams({ search: query || undefined }); - }, - [updateSearchParams], - ); + // 검색 핸들러 (로컬 상태만 업데이트) + const handleSearch = useCallback((query: string) => { + setSearchQuery(query); + }, []); - // 클라이언트 사이드 검색 필터링 (API에서 검색을 지원하지 않는 경우) + // 클라이언트 사이드 검색 필터링 (스터디명만 검색) const filteredStudies = useMemo(() => { - if (!searchQuery) return studies; + if (!searchQuery) return allStudies; const lowerQuery = searchQuery.toLowerCase(); - return studies.filter( - (study) => - study.simpleDetailInfo?.title?.toLowerCase().includes(lowerQuery) || - study.simpleDetailInfo?.summary?.toLowerCase().includes(lowerQuery), + return allStudies.filter((study) => + study.simpleDetailInfo?.title?.toLowerCase().includes(lowerQuery), ); - }, [studies, searchQuery]); + }, [allStudies, searchQuery]); + + // 검색어가 있을 때는 클라이언트에서 페이지네이션, 없으면 서버 페이지네이션 사용 + const totalPages = searchQuery + ? Math.ceil(filteredStudies.length / PAGE_SIZE) || 1 + : data?.totalPages ?? 1; + + const displayStudies = useMemo(() => { + if (!searchQuery) return filteredStudies; + + const startIndex = (currentPage - 1) * PAGE_SIZE; + + return filteredStudies.slice(startIndex, startIndex + PAGE_SIZE); + }, [filteredStudies, searchQuery, currentPage]); if (isLoading) { return ( @@ -162,7 +172,7 @@ export default function PremiumStudyListPage() {
{/* 스터디 카드 그리드 */} - + {/* 페이지네이션 */} {totalPages > 1 && ( diff --git a/src/components/section/premium-study-info-section.tsx b/src/components/section/premium-study-info-section.tsx index 022edfba..6fa5a355 100644 --- a/src/components/section/premium-study-info-section.tsx +++ b/src/components/section/premium-study-info-section.tsx @@ -51,7 +51,10 @@ export default function PremiumStudyInfoSection({
썸네일 ROLE_LABELS[role]).join(', '), }, + { + label: '스터디 기간', + value: `${dayjs(startDate).format('YYYY.MM.DD')} ~ ${dayjs(endDate).format('YYYY.MM.DD')}`, + }, + { + label: '참가비', + value: price === 0 ? '무료' : `${price.toLocaleString()}원`, + }, + { + label: '모집인원', + value: `${maxMembersCount}명`, + }, { label: '진행 방식', value: STUDY_METHOD_LABELS[method], @@ -103,14 +116,6 @@ export default function SummaryStudyInfo({ data }: Props) { label: '모집인원', value: `${maxMembersCount}명`, }, - { - label: '스터디 기간', - value: `${dayjs(startDate).format('YYYY.MM.DD')} ~ ${dayjs(endDate).format('YYYY.MM.DD')}`, - }, - { - label: '참가비', - value: price === 0 ? '무료' : `${price.toLocaleString()}원`, - }, ]; const visibleItems = isExpanded ? infoItems : infoItems.slice(0, 4); @@ -127,25 +132,32 @@ export default function SummaryStudyInfo({ data }: Props) { }; const isApplyDisabled = + !isLoggedIn || isLeader || myApplicationStatus?.status !== 'NONE' || - groupStudyStatus === 'IN_PROGRESS' || + groupStudyStatus !== 'RECRUITING' || approvedCount >= maxMembersCount; const getButtonText = () => { - if ( - myApplicationStatus?.status === 'APPROVED' && - groupStudyStatus === 'IN_PROGRESS' - ) { + if (myApplicationStatus?.status === 'APPROVED') { return '참여 중인 스터디'; } if (myApplicationStatus?.status === 'PENDING') { return '승인 대기중'; } + if (myApplicationStatus?.status === 'REJECTED') { + return '신청 거절됨'; + } return '신청하기'; }; + const handleApplyClick = () => { + if (!isLoggedIn) { + router.push('/login'); + } + }; + return (
{/* 제목 */} @@ -192,22 +204,33 @@ export default function SummaryStudyInfo({ data }: Props) {
{/* 스터디 신청 모달 (유료/무료 공통) */} - - {getButtonText()} - - } - /> + {isLoggedIn ? ( + + {getButtonText()} + + } + /> + ) : ( + + )}
- {/* 1차 MVP에선 사용하지 않아 제외 */} - + {accessTokenStr && } From 67f09dc7844b24c48c6d4bbccbe134f288f54a9b Mon Sep 17 00:00:00 2001 From: yeun38 Date: Wed, 7 Jan 2026 00:15:45 +0900 Subject: [PATCH 3/4] =?UTF-8?q?=EB=AF=B8=EC=85=98=20=EC=88=98=ED=96=89=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EC=9A=94=EC=B2=AD=20=EC=82=AC=ED=95=AD=20?= =?UTF-8?q?=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/card/mission-card.tsx | 25 ++++--- .../contents/payment-page-content.tsx | 6 +- src/components/layout/page-container.tsx | 7 +- .../modals/delete-mission-modal.tsx | 1 + src/components/modals/edit-mission-modal.tsx | 70 +++++++++++++++---- .../payment/paymentActionClient.tsx | 5 +- src/components/section/mission-section.tsx | 7 +- src/components/summary/order-summary.tsx | 7 +- 8 files changed, 89 insertions(+), 39 deletions(-) diff --git a/src/components/card/mission-card.tsx b/src/components/card/mission-card.tsx index a880ece8..17ebb103 100644 --- a/src/components/card/mission-card.tsx +++ b/src/components/card/mission-card.tsx @@ -123,24 +123,14 @@ export default function MissionCard({
  • - + ['color'] }; startDate?: string; endDate?: string; deadlineInfo: { text: string; isUrgent: boolean } | null; }) { + const displayTitle = weekNum ? `${weekNum}주차 미션 : ${title}` : title; + return (
    {deadlineInfo && ( @@ -243,7 +240,9 @@ function MissionCardContent({ )}
    - {title} + + {displayTitle} + {statusConfig.label}
    diff --git a/src/components/contents/payment-page-content.tsx b/src/components/contents/payment-page-content.tsx index 1e66bff0..359c32eb 100644 --- a/src/components/contents/payment-page-content.tsx +++ b/src/components/contents/payment-page-content.tsx @@ -28,7 +28,7 @@ export default function PaymentPageContent({ id }: PaymentPageContentProps) { return (
    -
    +
    {/* 선택한 스터디 */}
    @@ -38,7 +38,9 @@ export default function PaymentPageContent({ id }: PaymentPageContentProps) { groupStudyTitle={data?.groupStudyTitle} amount={data.amount} description={data.groupStudyDescription} - thumbnailUrl={data?.groupStudyImage.resizedImages[0].resizedImageUrl} + thumbnailUrl={ + data?.groupStudyImage.resizedImages[0].resizedImageUrl + } />
    diff --git a/src/components/layout/page-container.tsx b/src/components/layout/page-container.tsx index 60662d7e..034ba0a2 100644 --- a/src/components/layout/page-container.tsx +++ b/src/components/layout/page-container.tsx @@ -1,12 +1,15 @@ -import { ReactNode } from 'react'; import { clsx } from 'clsx'; +import { ReactNode } from 'react'; interface PageContainerProps { children: ReactNode; className?: string; } -export default function PageContainer({ children, className }: PageContainerProps) { +export default function PageContainer({ + children, + className, +}: PageContainerProps) { return (
    {children} diff --git a/src/components/modals/delete-mission-modal.tsx b/src/components/modals/delete-mission-modal.tsx index 82a3ddbe..50610add 100644 --- a/src/components/modals/delete-mission-modal.tsx +++ b/src/components/modals/delete-mission-modal.tsx @@ -18,6 +18,7 @@ export default function DeleteMissionModal({ onSuccess, }: DeleteMissionModalProps) { const [open, setOpen] = useState(false); + console.log('missionId', missionId); const { mutate: deleteMission } = useDeleteMission(); diff --git a/src/components/modals/edit-mission-modal.tsx b/src/components/modals/edit-mission-modal.tsx index 2d0a9d17..3b8090ae 100644 --- a/src/components/modals/edit-mission-modal.tsx +++ b/src/components/modals/edit-mission-modal.tsx @@ -1,6 +1,6 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { XIcon } from 'lucide-react'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import { Controller, FormProvider, useForm } from 'react-hook-form'; import { z } from 'zod'; import Button from '@/components/ui/button'; @@ -8,7 +8,7 @@ import DatePicker from '@/components/ui/date-picker'; import FormField from '@/components/ui/form/form-field'; import { BaseInput, TextAreaInput } from '@/components/ui/input'; import { Modal } from '@/components/ui/modal'; -import { useUpdateMission } from '@/hooks/queries/mission-api'; +import { useGetMission, useUpdateMission } from '@/hooks/queries/mission-api'; // Form Schema const EditMissionFormSchema = z.object({ @@ -29,15 +29,12 @@ const EditMissionFormSchema = z.object({ type EditMissionFormValues = z.infer; interface EditMissionModalProps { - defaultValue: EditMissionFormValues; missionId: number; } -export default function EditMissionModal({ - defaultValue, - missionId, -}: EditMissionModalProps) { +export default function EditMissionModal({ missionId }: EditMissionModalProps) { const [open, setOpen] = useState(false); + const { data: missionData, isLoading } = useGetMission(missionId); return ( @@ -63,11 +60,17 @@ export default function EditMissionModal({ - setOpen(false)} - /> + {isLoading ? ( + + 로딩 중... + + ) : missionData ? ( + setOpen(false)} + /> + ) : null} @@ -75,22 +78,59 @@ export default function EditMissionModal({ } interface EditMissionFormProps { - defaultValue: EditMissionFormValues; + missionData: { + missionTitle?: string; + missionDescription?: string; + weekNum?: number; + missionGuide?: string; + missionStartDate?: string; + missionEndDate?: string; + }; missionId: number; onClose: () => void; } function EditMissionForm({ - defaultValue, + missionData, missionId, onClose, }: EditMissionFormProps) { const methods = useForm({ resolver: zodResolver(EditMissionFormSchema), mode: 'onChange', - defaultValues: defaultValue, + defaultValues: { + title: missionData.missionTitle || '', + description: missionData.missionDescription || '', + weekNum: missionData.weekNum?.toString() || '', + guide: missionData.missionGuide || '', + dateRange: { + from: missionData.missionStartDate + ? new Date(missionData.missionStartDate) + : new Date(), + to: missionData.missionEndDate + ? new Date(missionData.missionEndDate) + : new Date(), + }, + }, }); + useEffect(() => { + methods.reset({ + title: missionData.missionTitle || '', + description: missionData.missionDescription || '', + weekNum: missionData.weekNum?.toString() || '', + guide: missionData.missionGuide || '', + dateRange: { + from: missionData.missionStartDate + ? new Date(missionData.missionStartDate) + : new Date(), + to: missionData.missionEndDate + ? new Date(missionData.missionEndDate) + : new Date(), + }, + }); + }, [missionData, methods]); + const { handleSubmit, formState, control } = methods; const { mutate: updateMission } = useUpdateMission(); diff --git a/src/components/payment/paymentActionClient.tsx b/src/components/payment/paymentActionClient.tsx index 4ac322ef..12a4accb 100644 --- a/src/components/payment/paymentActionClient.tsx +++ b/src/components/payment/paymentActionClient.tsx @@ -104,7 +104,10 @@ export default function PaymentCheckoutPage({ study }: Props) { useEffect(() => { async function fetchPayment() { if (!clientKey) { - console.error('NEXT_PUBLIC_TOSS_CLIENT_KEY 환경변수가 설정되지 않았습니다.'); + console.error( + 'NEXT_PUBLIC_TOSS_CLIENT_KEY 환경변수가 설정되지 않았습니다.', + ); + return; } diff --git a/src/components/section/mission-section.tsx b/src/components/section/mission-section.tsx index 49f10ab9..ee7b9e05 100644 --- a/src/components/section/mission-section.tsx +++ b/src/components/section/mission-section.tsx @@ -9,6 +9,7 @@ import { useUserStore } from '@/stores/useUserStore'; import MissionCard from '../card/mission-card'; import HomeworkDetailContent from '../contents/homework-detail-content'; import MissionDetailContent from '../contents/mission-detail-content'; +import PageContainer from '../layout/page-container'; import CreateMissionModal from '../modals/create-mission-modal'; import { cn } from '../ui/(shadcn)/lib/utils'; @@ -137,7 +138,7 @@ export default function MissionSection({ groupStudyId }: MissionSectionProps) { // 미션 상세 보기 if (missionId) { return ( -
    +
    + ); } // 미션 목록 (기본) return (
    -
    +
    미션 목록 {isLeader && } diff --git a/src/components/summary/order-summary.tsx b/src/components/summary/order-summary.tsx index 029a8ad7..f6d40744 100644 --- a/src/components/summary/order-summary.tsx +++ b/src/components/summary/order-summary.tsx @@ -16,9 +16,9 @@ export default function OrderSummary({ return (
    - {thumbnailUrl ? ( + {/* {thumbnailUrl ? ( {groupStudyTitle} ) : (
    - )} + )} */} +

    {groupStudyTitle}

    From f5aa45eaba057a4c5cfe6902d6139ec7c23b824b Mon Sep 17 00:00:00 2001 From: yeun38 Date: Wed, 7 Jan 2026 00:25:21 +0900 Subject: [PATCH 4/4] fix pretteir --- src/components/filtering/study-filter.tsx | 2 +- src/components/pages/group-study-list-page.tsx | 2 +- src/components/pages/premium-study-list-page.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/filtering/study-filter.tsx b/src/components/filtering/study-filter.tsx index 62f98e17..94b6522c 100644 --- a/src/components/filtering/study-filter.tsx +++ b/src/components/filtering/study-filter.tsx @@ -82,7 +82,7 @@ function FilterDropdown({