Skip to content

Commit f0063a7

Browse files
authored
Merge pull request #75 from StartUpLight/SRLT-104-수정사항-반영
[SRLT-104] 채점하기 플로우 변경 및 수정사항 반영
2 parents 8d09592 + c4f30c5 commit f0063a7

File tree

15 files changed

+445
-355
lines changed

15 files changed

+445
-355
lines changed
3.22 KB
Loading
14 KB
Loading
30.2 KB
Loading

public/images/video_thumbnail.png

673 KB
Loading

src/app/_components/common/BusinessHeader.tsx

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import Download from '@/assets/icons/download.svg';
1010
import { useBusinessStore } from '@/store/business.store';
1111
import { downloadPDF } from '@/lib/pdfDownload';
1212
import { patchBusinessPlanTitle } from '@/api/business';
13-
import { usePostGrade } from '@/hooks/mutation/usePostGrade';
1413

1514
const BusinessHeaderContent = () => {
1615
const router = useRouter();
@@ -27,7 +26,6 @@ const BusinessHeaderContent = () => {
2726
loadTitleFromAPI,
2827
} = useBusinessStore();
2928

30-
// URL의 planId 또는 store의 planId로 제목 조회
3129
useEffect(() => {
3230
const planIdParam = searchParams.get('planId');
3331
const targetPlanId = planIdParam ? parseInt(planIdParam, 10) : planId;
@@ -37,13 +35,13 @@ const BusinessHeaderContent = () => {
3735
return;
3836
}
3937

40-
// planId가 변경되었을 때만 제목 로드 (중복 요청 방지)
4138
let isCancelled = false;
4239
loadTitleFromAPI(targetPlanId)
4340
.then((loadedTitle) => {
4441
if (!isCancelled && loadedTitle) {
45-
// 제목이 로드되었을 때만 업데이트 (planId가 변경되지 않았는지 확인)
46-
const currentPlanId = planIdParam ? parseInt(planIdParam, 10) : planId;
42+
const currentPlanId = planIdParam
43+
? parseInt(planIdParam, 10)
44+
: planId;
4745
if (currentPlanId === targetPlanId) {
4846
setTitle(loadedTitle);
4947
}
@@ -60,7 +58,6 @@ const BusinessHeaderContent = () => {
6058
};
6159
}, [searchParams, planId, loadTitleFromAPI, setTitle]);
6260

63-
// 제목 변경 시 API 요청 (debounce 적용)
6461
useEffect(() => {
6562
const trimmedTitle = title.trim();
6663

@@ -87,7 +84,7 @@ const BusinessHeaderContent = () => {
8784
const [isModalOpen, setIsModalOpen] = useState(false);
8885
const isSaving = useBusinessStore((state) => state.isSaving);
8986

90-
const { mutate: postGradeMutate, isPending: isGrading } = usePostGrade();
87+
const [gradingPlanId, setGradingPlanId] = useState<number | null>(null);
9188

9289
const handleOpenModal = () => setIsModalOpen(true);
9390
const handleCloseModal = () => setIsModalOpen(false);
@@ -115,11 +112,10 @@ const BusinessHeaderContent = () => {
115112
if (id == null) throw new Error('planId 생성에 실패했습니다.');
116113

117114
await saveAllItems(id);
115+
setGradingPlanId(id);
118116
handleOpenModal();
119-
postGradeMutate(id, {
120-
onSuccess: () => router.push('/report'),
121-
onError: (e) => console.error('채점 실패:', e),
122-
});
117+
} catch (error) {
118+
console.error('채점 준비 중 오류:', error);
123119
} finally {
124120
setIsSaving(false);
125121
}
@@ -142,14 +138,13 @@ const BusinessHeaderContent = () => {
142138
<div className="flex w-full items-center justify-between px-8 pt-3">
143139
<div
144140
onClick={() => {
145-
// 미리보기 모드일 때는 작성 화면으로 전환
146141
if (isPreview) {
147142
setPreview(false);
148143
} else {
149144
router.back();
150145
}
151146
}}
152-
className="flex cursor-pointer items-center justify-center gap-1 rounded-[8px] px-4 py-[6px] active:bg-gray-200"
147+
className="flex cursor-pointer items-center justify-center gap-1 rounded-lg px-4 py-1.5 active:bg-gray-200"
153148
>
154149
<Back />
155150
<span className="ds-text font-medium whitespace-nowrap text-gray-600">
@@ -176,7 +171,7 @@ const BusinessHeaderContent = () => {
176171
placeholder="제목을 입력하세요."
177172
aria-label="문서 제목"
178173
style={{ width: inputWidth }}
179-
className="ds-text hover:border-primary-200 rounded-[8px] bg-white px-3 py-[6px] text-start font-medium overflow-ellipsis transition-[width] duration-200 ease-out placeholder:text-gray-400 hover:border-[1.2px] focus:outline-none"
174+
className="ds-text hover:border-primary-200 rounded-lg bg-white px-3 py-1.5 text-start font-medium overflow-ellipsis transition-[width] duration-200 ease-out placeholder:text-gray-400 hover:border-[1.2px] focus:outline-none"
180175
/>
181176
</>
182177
)}
@@ -188,17 +183,16 @@ const BusinessHeaderContent = () => {
188183
<button
189184
type="button"
190185
onClick={handleDownloadPDF}
191-
className="flex h-[33px] w-[33px] cursor-pointer items-center justify-center rounded-[8px] border-[1.2px] border-gray-200 transition-colors hover:bg-gray-100 focus:outline-none"
186+
className="flex h-[33px] w-[33px] cursor-pointer items-center justify-center rounded-lg border-[1.2px] border-gray-200 transition-colors hover:bg-gray-100 focus:outline-none"
192187
>
193188
<Download />
194189
</button>
195-
<div className="h-[32px] w-[1.6px] bg-gray-200" />
190+
<div className="h-8 w-[1.6px] bg-gray-200" />
196191
<Button
197-
text={isGrading ? '채점 중...' : '채점하기'}
192+
text="채점하기"
198193
size="M"
199194
color="primary"
200-
className={`ds-subtext h-[33px] rounded-[8px] px-4 py-[6px] ${isGrading ? 'pointer-events-none opacity-50' : ''}`}
201-
disabled={isGrading}
195+
className="ds-subtext h-[33px] rounded-lg px-4 py-1.5"
202196
onClick={handleGrade}
203197
/>
204198
</>
@@ -208,7 +202,6 @@ const BusinessHeaderContent = () => {
208202
<button
209203
type="button"
210204
onClick={() => {
211-
// window에 등록된 토글 함수 호출
212205
if (typeof window !== 'undefined') {
213206
const win = window as Window & {
214207
togglePreview?: () => void;
@@ -218,12 +211,12 @@ const BusinessHeaderContent = () => {
218211
}
219212
}
220213
}}
221-
className="flex h-[33px] w-[33px] cursor-pointer items-center justify-center rounded-[8px] border-[1.2px] border-gray-200 transition-colors hover:bg-gray-100 focus:outline-none"
214+
className="flex h-[33px] w-[33px] cursor-pointer items-center justify-center rounded-lg border-[1.2px] border-gray-200 transition-colors hover:bg-gray-100 focus:outline-none"
222215
>
223216
<Eye />
224217
</button>
225218
<div className="pointer-events-none absolute top-10 left-1/2 hidden -translate-x-1/2 group-hover:block">
226-
<div className="relative h-[44px] w-[73px] select-none">
219+
<div className="relative h-11 w-[73px] select-none">
227220
<Image
228221
src="/images/bubble.png"
229222
alt="미리보기 호버 말풍선"
@@ -243,24 +236,30 @@ const BusinessHeaderContent = () => {
243236
type="button"
244237
onClick={handleSave}
245238
disabled={isSaving}
246-
className={`text-primary-500 border-primary-500 ds-subtext flex h-[33px] items-center justify-center rounded-[8px] border-[1.2px] px-3 py-2 font-medium transition ${isSaving ? 'cursor-not-allowed opacity-50' : 'hover:bg-primary-50 cursor-pointer'}`}
239+
className={`text-primary-500 border-primary-500 ds-subtext flex h-[33px] items-center justify-center rounded-[8px] border-[1.2px] px-3 py-2 font-medium transition ${
240+
isSaving
241+
? 'cursor-not-allowed opacity-50'
242+
: 'hover:bg-primary-50 cursor-pointer'
243+
}`}
247244
>
248245
{isSaving ? '저장 중...' : '임시 저장'}
249246
</button>
250247
<Button
251-
text={isGrading ? '채점 중...' : '채점하기'}
248+
text="채점하기"
252249
size="M"
253250
color="primary"
254-
className={`ds-subtext h-[33px] rounded-[8px] px-4 py-[6px] ${isSaving || isGrading ? 'pointer-events-none opacity-50' : ''}`}
255-
disabled={isSaving || isGrading}
251+
className={`ds-subtext h-[33px] rounded-lg px-4 py-1.5 ${
252+
isSaving ? 'pointer-events-none opacity-50' : ''
253+
}`}
254+
disabled={isSaving}
256255
onClick={handleGrade}
257256
/>
258257
</div>
259258
</>
260259
)}
261260
</div>
262261

263-
{isModalOpen && (
262+
{isModalOpen && gradingPlanId && (
264263
<CreateModal
265264
title="AI로 사업계획서 채점하기"
266265
imageSrc="/images/grading_Image.png"
@@ -269,9 +268,9 @@ const BusinessHeaderContent = () => {
269268
imageHeight={202}
270269
subtitle={`방금 작성하신 사업계획서를 항목별로 분석해 점수·강점·리스크를 즉시 제공해드려요.\n70점 이상이면, 아이템에 맞는 전문가 추천까지 제공해드려요.`}
271270
onClose={handleCloseModal}
272-
buttonText={isGrading ? '채점 중...' : '결과 보기'}
271+
buttonText="채점하기"
273272
onClick={() => {
274-
if (!isGrading) router.push('/report');
273+
router.push(`/loading?planId=${gradingPlanId}`);
275274
}}
276275
/>
277276
)}

src/app/expert/components/MentorCard.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ const MentorCard = ({
2323
image,
2424
workingperiod,
2525
id,
26-
onApplied,
2726
}: MentorCardProps & ExtraProps) => {
2827
const router = useRouter();
2928
const planId = useBusinessStore((s) => s.planId);
@@ -33,10 +32,9 @@ const MentorCard = ({
3332

3433
const { setSelectedMentor } = useExpertStore();
3534

36-
const [didApply, setDidApply] = useState(false);
3735
const [uploading, setUploading] = useState(false);
3836

39-
const isDone = status === 'done' || didApply;
37+
const isDone = status === 'done';
4038
const canUseExpert = isMember && hasExpertUnlocked;
4139

4240
const disabled = !canUseExpert || isDone || uploading || planId == null;

src/app/loading/page.tsx

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,46 @@
11
'use client';
22
import Lottie from 'lottie-react';
33
import loadingAnimation from '@/assets/lotties/loading.json';
4+
import { useRouter, useSearchParams } from 'next/navigation';
5+
import { usePostGrade } from '@/hooks/mutation/usePostGrade';
6+
import { useEffect, useRef, Suspense } from 'react';
7+
8+
const LoadingInner = () => {
9+
const router = useRouter();
10+
const searchParams = useSearchParams();
11+
const planIdParam = searchParams.get('planId');
12+
13+
const { mutateAsync: postGradeMutateAsync } = usePostGrade();
14+
const sendOnceRef = useRef(false);
15+
16+
useEffect(() => {
17+
const run = async () => {
18+
if (sendOnceRef.current) return;
19+
sendOnceRef.current = true;
20+
21+
if (!planIdParam) {
22+
router.back();
23+
return;
24+
}
25+
26+
const id = Number(planIdParam);
27+
if (Number.isNaN(id)) {
28+
router.back();
29+
return;
30+
}
31+
32+
try {
33+
await postGradeMutateAsync(id);
34+
router.push('/report');
35+
} catch (error) {
36+
console.error('채점에 실패했습니다.', error);
37+
router.back();
38+
}
39+
};
40+
41+
run();
42+
}, [planIdParam, postGradeMutateAsync, router]);
443

5-
const Page = () => {
644
return (
745
<div className="flex justify-center bg-white">
846
<div className="mt-[220px] text-center">
@@ -30,4 +68,12 @@ const Page = () => {
3068
);
3169
};
3270

33-
export default Page;
71+
const LoadingPage = () => {
72+
return (
73+
<Suspense fallback={null}>
74+
<LoadingInner />
75+
</Suspense>
76+
);
77+
};
78+
79+
export default LoadingPage;

src/app/page.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const page = () => {
1010
<video
1111
className="absolute inset-0 h-full w-full object-cover"
1212
src="/images/landing/home.mp4"
13+
poster="/images/video_thumbnail.png"
1314
autoPlay
1415
muted
1516
playsInline

src/app/pay/components/OrderDetails.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
'use client';
2-
import SmallLogo from '@/assets/icons/small_logo.svg';
32
import { SelectedMentor } from '@/store/expert.store';
43
import Image from 'next/image';
54

src/app/price/complete/page.tsx

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ function PayComplete() {
2121
useEffect(() => {
2222
const paymentKey = searchParams.get('paymentKey');
2323
const orderId = searchParams.get('orderId');
24-
// const code = searchParams.get('code');
25-
// const message = searchParams.get('message');
24+
const code = searchParams.get('code');
2625

2726
if (paymentKey && orderId) {
2827
const payload: OrderConfirmRequestPayload = {
@@ -38,24 +37,20 @@ function PayComplete() {
3837
setState('SUCCESS');
3938
})
4039
.catch((error: unknown) => {
41-
const msg =
42-
error instanceof Error
43-
? error.message
44-
: '결제 승인 처리 중 오류가 발생했습니다.';
40+
error instanceof Error
41+
? error.message
42+
: '결제 승인 처리 중 오류가 발생했습니다.';
4543

4644
setState('FAIL');
4745
});
4846

4947
return;
5048
}
5149

52-
// if (code) {
53-
// const decoded =
54-
// message != null ? decodeURIComponent(message) : '결제에 실패했습니다.';
55-
56-
// setState('FAIL');
57-
// return;
58-
// }
50+
if (code) {
51+
setState('FAIL');
52+
return;
53+
}
5954

6055
setState('FAIL');
6156
}, [searchParams]);

0 commit comments

Comments
 (0)