Skip to content

Commit cf67945

Browse files
authored
Merge pull request #9 from Souf-Project/feat/inquiry
FEAT : 회원 인증 정보 띄우기
2 parents 66a32fd + f4c5b32 commit cf67945

2 files changed

Lines changed: 189 additions & 5 deletions

File tree

src/api/member.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,12 @@ export const patchMemberStatus = async ({ memberId, approvedStatus, requestBody
5050
}
5151
}
5252

53+
export const getMemberAuthFile = async ({ memberId }) => {
54+
try {
55+
const response = await client.get(`/api/v1/admin/member/${memberId}`);
56+
return response.data;
57+
} catch (error) {
58+
console.error(error);
59+
throw error;
60+
}
61+
}

src/pages/members.jsx

Lines changed: 180 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useEffect, useState } from "react";
2-
import { getMember, patchMemberStatus } from "../api/member";
2+
import { getMember, patchMemberStatus, getMemberAuthFile } from "../api/member";
33
import AdminLayout from "../components/layout/adminLayout";
44
import Table from "../components/common/table";
55
import Pagination from "../components/common/pagination";
@@ -16,6 +16,8 @@ export default function Members() {
1616
const [showModal, setShowModal] = useState(false);
1717
const [selectedStatus, setSelectedStatus] = useState(null);
1818
const [rejectReason, setRejectReason] = useState("");
19+
const [authFile, setAuthFile] = useState(null);
20+
const [loadingAuthFile, setLoadingAuthFile] = useState(false);
1921
const pageSize = 10;
2022
const columns = [
2123
{ key: "타입", value: "타입" },
@@ -26,6 +28,8 @@ export default function Members() {
2628
{ key: "처리상태", value: "처리 상태" },
2729
];
2830

31+
const VITE_S3_BUCKET_URL = import.meta.env.VITE_S3_BUCKET_URL;
32+
2933
const getMemberData = async () => {
3034
try {
3135
const params = {
@@ -122,19 +126,45 @@ useEffect(() => {
122126
setCurrentPage(0);
123127
};
124128

125-
const handleRowClick = (member, originalData) => {
129+
const handleRowClick = async (member, originalData) => {
126130
// originalData가 있으면 사용, 없으면 member 자체 사용
127-
setSelectedMember(originalData || member);
131+
const memberData = originalData || member;
132+
setSelectedMember(member);
128133
setShowModal(true);
129134
setSelectedStatus(null);
130135
setRejectReason("");
136+
setAuthFile(null);
137+
138+
// 인증 파일 조회 - memberId는 mapped data나 originalData에서 가져올 수 있음
139+
const memberId = member.memberId || (originalData && originalData.memberId);
140+
if (memberId) {
141+
setLoadingAuthFile(true);
142+
try {
143+
const response = await getMemberAuthFile({ memberId: memberId });
144+
console.log("인증 파일 응답:", response);
145+
// API 응답 구조에 따라 수정 필요
146+
if (response && response.result) {
147+
setAuthFile(response.result);
148+
} else if (response && response.data) {
149+
setAuthFile(response.data);
150+
} else {
151+
setAuthFile(response);
152+
}
153+
} catch (error) {
154+
console.error("인증 파일 조회 실패:", error);
155+
setAuthFile(null);
156+
} finally {
157+
setLoadingAuthFile(false);
158+
}
159+
}
131160
};
132161

133162
const handleCloseModal = () => {
134163
setShowModal(false);
135164
setSelectedMember(null);
136165
setSelectedStatus(null);
137166
setRejectReason("");
167+
setAuthFile(null);
138168
};
139169

140170
const handleStatusChange = (newStatus) => {
@@ -235,7 +265,7 @@ useEffect(() => {
235265
{showModal && selectedMember && (
236266
<div className="fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center" onClick={handleCloseModal}>
237267
<div
238-
className="bg-white rounded-lg shadow-xl w-full max-w-md p-6 max-h-[80vh] overflow-y-auto"
268+
className="bg-white rounded-lg shadow-xl w-full max-w-5xl p-6 max-h-[90vh] overflow-y-auto"
239269
onClick={(e) => e.stopPropagation()}
240270
>
241271
<div className="flex justify-between items-center mb-6">
@@ -248,7 +278,9 @@ useEffect(() => {
248278
</button>
249279
</div>
250280

251-
<div className="space-y-4">
281+
<div className="flex gap-6">
282+
{/* 왼쪽: 회원 정보 */}
283+
<div className="flex-1 space-y-4">
252284
<div>
253285
<label className="block text-sm font-medium text-gray-700 mb-2">이름</label>
254286
<div className="p-3 bg-gray-50 rounded border">{selectedMember.이름}</div>
@@ -345,6 +377,149 @@ useEffect(() => {
345377
</button>
346378
</div>
347379
)}
380+
</div>
381+
382+
{/* 오른쪽: 인증 파일 및 상세 정보 */}
383+
<div className="flex-1 border-l pl-6">
384+
<h3 className="text-lg font-semibold text-gray-800 mb-4">인증 정보</h3>
385+
{loadingAuthFile ? (
386+
<div className="flex items-center justify-center py-8">
387+
<div className="text-gray-500">로딩 중...</div>
388+
</div>
389+
) : authFile ? (
390+
<div className="space-y-6">
391+
{/* 인증 파일 */}
392+
<div>
393+
<label className="block text-sm font-medium text-gray-700 mb-2">인증 파일</label>
394+
{authFile.authenticationFileUrl ? (() => {
395+
const fileUrl = VITE_S3_BUCKET_URL + authFile.authenticationFileUrl;
396+
const fileExtension = authFile.authenticationFileUrl.split('.').pop()?.toLowerCase();
397+
const isImage = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'bmp'].includes(fileExtension);
398+
const isPdf = fileExtension === 'pdf';
399+
400+
return (
401+
<div className="space-y-2">
402+
{isImage ? (
403+
<img
404+
src={fileUrl}
405+
alt="인증 파일"
406+
className="w-full rounded border max-h-[500px] object-contain"
407+
onError={(e) => {
408+
e.target.style.display = 'none';
409+
e.target.nextSibling.style.display = 'block';
410+
}}
411+
/>
412+
) : isPdf ? (
413+
<iframe
414+
src={fileUrl}
415+
className="w-full rounded border"
416+
style={{ height: '500px' }}
417+
title="인증 파일 PDF"
418+
/>
419+
) : (
420+
<div className="p-4 bg-gray-50 rounded border text-gray-500 text-center">
421+
미리보기를 지원하지 않는 파일 형식입니다.
422+
</div>
423+
)}
424+
<div style={{ display: 'none' }} className="text-gray-500 text-center py-4">
425+
파일을 불러올 수 없습니다.
426+
</div>
427+
<a
428+
href={fileUrl}
429+
target="_blank"
430+
rel="noopener noreferrer"
431+
className="text-blue-main hover:text-blue-point underline text-sm"
432+
>
433+
원본 파일 보기
434+
</a>
435+
</div>
436+
);
437+
})() : (
438+
<div className="p-3 bg-gray-50 rounded border text-gray-500 text-sm">
439+
인증 파일이 없습니다.
440+
</div>
441+
)}
442+
</div>
443+
444+
{/* 전화번호 */}
445+
{authFile.resDto?.phoneNumber && (
446+
<div>
447+
<label className="block text-sm font-medium text-gray-700 mb-2">전화번호</label>
448+
<div className="p-3 bg-gray-50 rounded border">{authFile.resDto.phoneNumber}</div>
449+
</div>
450+
)}
451+
452+
{/* 상세 정보 (detail이 있는 경우에만 표시) */}
453+
{authFile.resDto?.detail && (
454+
<div>
455+
<label className="block text-sm font-medium text-gray-700 mb-2">사업자 정보</label>
456+
<div className="space-y-3 p-4 bg-gray-50 rounded border">
457+
{authFile.resDto.detail.companyName && (
458+
<div>
459+
<span className="text-xs text-gray-500">회사명</span>
460+
<div className="text-sm font-medium text-gray-800 mt-1">
461+
{authFile.resDto.detail.companyName}
462+
</div>
463+
</div>
464+
)}
465+
{authFile.resDto.detail.businessClassification && (
466+
<div>
467+
<span className="text-xs text-gray-500">사업자 분류</span>
468+
<div className="text-sm font-medium text-gray-800 mt-1">
469+
{authFile.resDto.detail.businessClassification}
470+
</div>
471+
</div>
472+
)}
473+
{authFile.resDto.detail.businessRegistrationNumber && (
474+
<div>
475+
<span className="text-xs text-gray-500">사업자 등록번호</span>
476+
<div className="text-sm font-medium text-gray-800 mt-1">
477+
{authFile.resDto.detail.businessRegistrationNumber}
478+
</div>
479+
</div>
480+
)}
481+
{authFile.resDto.detail.businessStatus && (
482+
<div>
483+
<span className="text-xs text-gray-500">업태</span>
484+
<div className="text-sm font-medium text-gray-800 mt-1">
485+
{authFile.resDto.detail.businessStatus}
486+
</div>
487+
</div>
488+
)}
489+
{authFile.resDto.detail.zipCode && (
490+
<div>
491+
<span className="text-xs text-gray-500">우편번호</span>
492+
<div className="text-sm font-medium text-gray-800 mt-1">
493+
{authFile.resDto.detail.zipCode}
494+
</div>
495+
</div>
496+
)}
497+
{authFile.resDto.detail.roadNameAddress && (
498+
<div>
499+
<span className="text-xs text-gray-500">도로명 주소</span>
500+
<div className="text-sm font-medium text-gray-800 mt-1">
501+
{authFile.resDto.detail.roadNameAddress}
502+
</div>
503+
</div>
504+
)}
505+
{authFile.resDto.detail.detailedAddress && (
506+
<div>
507+
<span className="text-xs text-gray-500">상세 주소</span>
508+
<div className="text-sm font-medium text-gray-800 mt-1">
509+
{authFile.resDto.detail.detailedAddress}
510+
</div>
511+
</div>
512+
)}
513+
</div>
514+
</div>
515+
)}
516+
</div>
517+
) : (
518+
<div className="text-gray-500 py-8 text-center">
519+
인증 정보를 불러올 수 없습니다.
520+
</div>
521+
)}
522+
</div>
348523
</div>
349524
</div>
350525
</div>

0 commit comments

Comments
 (0)