summaryrefslogtreecommitdiff
path: root/components/knox
diff options
context:
space:
mode:
authorjoonhoekim <26rote@gmail.com>2025-07-18 16:32:22 +0900
committerjoonhoekim <26rote@gmail.com>2025-07-18 16:32:22 +0900
commit5cb225e9cd6b0ba2f52572a3afa0de6e5b2a2ece (patch)
treec8a7c6c9c81e766e823fc4349f7723a39974de1a /components/knox
parent05e0917990bcbcd1732f8fe14abec341996a3dc9 (diff)
파일첨부 API 처리, 텍스트 변경
Diffstat (limited to 'components/knox')
-rw-r--r--components/knox/approval/ApprovalDetail.tsx54
-rw-r--r--components/knox/approval/ApprovalSubmit.tsx46
2 files changed, 91 insertions, 9 deletions
diff --git a/components/knox/approval/ApprovalDetail.tsx b/components/knox/approval/ApprovalDetail.tsx
index 034bde7d..6db43cbe 100644
--- a/components/knox/approval/ApprovalDetail.tsx
+++ b/components/knox/approval/ApprovalDetail.tsx
@@ -28,6 +28,15 @@ interface ApprovalDetailData {
content: ApprovalContentResponse['data'];
}
+// 첨부파일 타입 (가이드에 명확히 정의되어 있지 않아 필드 일부 추정)
+interface ApprovalAttachment {
+ fileName?: string;
+ fileSize?: string;
+ downloadUrl?: string;
+ fileId?: string;
+ [key: string]: unknown; // 기타 필드 허용
+}
+
export default function ApprovalDetail({
useFakeData = false,
systemId = 'EVCP_SYSTEM',
@@ -126,6 +135,43 @@ export default function ApprovalDetail({
}
};
+ // 첨부파일 다운로드 헬퍼
+ const handleDownload = async (attachment: ApprovalAttachment) => {
+ try {
+ // 1) downloadUrl 이 이미 포함된 경우
+ if (attachment.downloadUrl) {
+ window.open(attachment.downloadUrl, '_blank');
+ return;
+ }
+
+ // 2) fileId + 별도 엔드포인트 조합 (가이드에 명시되지 않았으므로 best-effort 처리)
+ if (attachment.fileId) {
+ const url = `${process.env.KNOX_API_BASE_URL}/approval/api/v2.0/attachments/${attachment.fileId}`;
+ const resp = await fetch(url, {
+ method: 'GET',
+ headers: {
+ 'System-ID': systemId,
+ },
+ });
+ if (!resp.ok) throw new Error('다운로드 실패');
+
+ // blob 생성 후 브라우저 다운로드
+ const blob = await resp.blob();
+ const link = document.createElement('a');
+ link.href = URL.createObjectURL(blob);
+ link.download = attachment.fileName || 'attachment';
+ link.click();
+ URL.revokeObjectURL(link.href);
+ return;
+ }
+
+ toast.error('다운로드 URL 정보를 찾을 수 없습니다.');
+ } catch (err) {
+ console.error('첨부파일 다운로드 오류:', err);
+ toast.error('첨부파일 다운로드 중 오류가 발생했습니다.');
+ }
+ };
+
// 초기 로딩 (initialApInfId가 있는 경우)
useEffect(() => {
if (initialApInfId) {
@@ -338,14 +384,18 @@ export default function ApprovalDetail({
<h3 className="text-lg font-semibold">첨부파일</h3>
<div className="space-y-2">
- {approvalData.detail.attachments.map((attachment: any, index: number) => (
+ {approvalData.detail.attachments.map((attachment: ApprovalAttachment, index: number) => (
<div key={index} className="flex items-center gap-3 p-3 bg-gray-50 rounded-lg">
<FileText className="w-4 h-4 text-gray-500" />
<div className="flex-1">
<p className="text-sm font-medium">{attachment.fileName || `첨부파일 ${index + 1}`}</p>
<p className="text-xs text-gray-500">{attachment.fileSize || '크기 정보 없음'}</p>
</div>
- <Button variant="outline" size="sm">
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={() => handleDownload(attachment)}
+ >
다운로드
</Button>
</div>
diff --git a/components/knox/approval/ApprovalSubmit.tsx b/components/knox/approval/ApprovalSubmit.tsx
index 37779ce3..526a87f3 100644
--- a/components/knox/approval/ApprovalSubmit.tsx
+++ b/components/knox/approval/ApprovalSubmit.tsx
@@ -17,7 +17,7 @@ import { toast } from 'sonner';
import { Loader2, Plus, Trash2, FileText, AlertCircle } from 'lucide-react';
// API 함수 및 타입
-import { submitApproval, createSubmitApprovalRequest, createApprovalLine } from '@/lib/knox-api/approval/approval';
+import { submitApproval, submitSecurityApproval, createSubmitApprovalRequest, createApprovalLine } from '@/lib/knox-api/approval/approval';
import type { ApprovalLine, SubmitApprovalRequest } from '@/lib/knox-api/approval/approval';
// Mock 데이터
@@ -40,7 +40,9 @@ const formSchema = z.object({
role: z.enum(['0', '1', '2', '3', '4', '7', '9']),
seq: z.string(),
opinion: z.string().optional()
- })).min(1, '최소 1개의 결재 경로가 필요합니다')
+ })).min(1, '최소 1개의 결재 경로가 필요합니다'),
+ // 첨부파일 (선택)
+ attachments: z.any().optional()
});
type FormData = z.infer<typeof formSchema>;
@@ -80,7 +82,8 @@ export default function ApprovalSubmit({
seq: '1',
opinion: ''
}
- ]
+ ],
+ attachments: undefined
}
});
@@ -137,6 +140,8 @@ export default function ApprovalSubmit({
);
// 상신 요청 생성
+ const attachmentsArray = data.attachments ? Array.from(data.attachments as FileList) : undefined;
+
const submitRequest: SubmitApprovalRequest = useFakeData
? {
...data,
@@ -144,7 +149,8 @@ export default function ApprovalSubmit({
importantYn: data.importantYn ? 'Y' : 'N',
sbmDt: new Date().toISOString().replace(/-|:|T/g, '').slice(0, 14),
apInfId: 'test-ap-inf-id-' + Date.now(),
- aplns: approvalLines
+ aplns: approvalLines,
+ attachments: attachmentsArray
}
: await createSubmitApprovalRequest(
data.contents,
@@ -158,14 +164,19 @@ export default function ApprovalSubmit({
notifyOption: data.notifyOption,
docMngSaveCode: data.docMngSaveCode,
sbmLang: data.sbmLang,
- timeZone: data.timeZone
+ timeZone: data.timeZone,
+ attachments: attachmentsArray
}
);
- // API 호출
+ // API 호출 (보안 등급에 따라 분기)
+ const isSecure = data.docSecuType === 'CONFIDENTIAL' || data.docSecuType === 'CONFIDENTIAL_STRICT';
+
const response = useFakeData
? await mockApprovalAPI.submitApproval(submitRequest)
- : await submitApproval(submitRequest, systemId);
+ : isSecure
+ ? await submitSecurityApproval(submitRequest, systemId)
+ : await submitApproval(submitRequest, systemId);
if (response.result === 'SUCCESS') {
setSubmitResult({ apInfId: response.data.apInfId });
@@ -333,6 +344,27 @@ export default function ApprovalSubmit({
)}
/>
</div>
+
+ {/* 첨부 파일 */}
+ <FormField
+ control={form.control}
+ name="attachments"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>첨부 파일</FormLabel>
+ <FormControl>
+ <Input
+ type="file"
+ multiple
+ onChange={(e) => field.onChange(e.target.files)}
+ />
+ </FormControl>
+ <FormDescription>필요 시 파일을 선택하세요. (다중 선택 가능)</FormDescription>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+
</div>
<Separator />