diff options
| author | joonhoekim <26rote@gmail.com> | 2025-11-20 19:36:01 +0900 |
|---|---|---|
| committer | joonhoekim <26rote@gmail.com> | 2025-11-20 19:36:01 +0900 |
| commit | 935fd22e17afc034a472bc2d159de7b9f5e5dcae (patch) | |
| tree | 6beea33ab38750be17632dffca5e05e644647365 /components/contract/contract-items-card.tsx | |
| parent | b75b1cd920efd61923f7b2dbc4c49987b7b0c4e1 (diff) | |
(김준회) PO, POS, swp
- PO: 발주서출력기능 초안
- 벤더측 POS 다운로드 기능 추가
- Contract 생성시 Status 설정 (mapper)
- swp document registration table 로직 리팩터링
- swp: 입력가능 문서번호 validation 추가 (리스트 메뉴에서 Completed 된 건)
Diffstat (limited to 'components/contract/contract-items-card.tsx')
| -rw-r--r-- | components/contract/contract-items-card.tsx | 134 |
1 files changed, 133 insertions, 1 deletions
diff --git a/components/contract/contract-items-card.tsx b/components/contract/contract-items-card.tsx index 82d273d4..d3be33c9 100644 --- a/components/contract/contract-items-card.tsx +++ b/components/contract/contract-items-card.tsx @@ -1,5 +1,13 @@ +"use client" + +import * as React from "react" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" +import { Button } from "@/components/ui/button" +import { Download, FileText } from "lucide-react" +import { toast } from "sonner" import { formatCurrency, formatNumber } from "@/lib/utils" +import { getDownloadUrlByMaterialCode, checkPosFileExists } from "@/lib/pos" +import { PosFileSelectionDialog } from "@/lib/pos/components/pos-file-selection-dialog" interface ContractItem { materialNo?: string @@ -25,9 +33,104 @@ interface ContractItem { interface ContractItemsCardProps { items: ContractItem[] currency?: string | null + /** + * 뷰어 타입 + * - 'evcp': EVCP 사용자 (암호화된 파일 직접 다운로드) + * - 'partners': 협력사 사용자 (복호화된 파일 다운로드) + */ + viewerType?: 'evcp' | 'partners' } -export function ContractItemsCard({ items, currency }: ContractItemsCardProps) { +export function ContractItemsCard({ items, currency, viewerType = 'partners' }: ContractItemsCardProps) { + // POS 파일 선택 다이얼로그 상태 + const [posDialogOpen, setPosDialogOpen] = React.useState(false) + const [selectedMaterialCode, setSelectedMaterialCode] = React.useState<string>("") + const [posFiles, setPosFiles] = React.useState<Array<{ + fileName: string + dcmtmId: string + projNo: string + posNo: string + posRevNo: string + fileSer: string + }>>([]) + const [loadingPosFiles, setLoadingPosFiles] = React.useState(false) + const [downloadingFileIndex, setDownloadingFileIndex] = React.useState<number | null>(null) + + // POS 파일 목록 조회 및 다이얼로그 열기 + const handleOpenPosDialog = async (materialCode: string) => { + if (!materialCode) { + toast.error("자재코드가 없습니다") + return + } + + setLoadingPosFiles(true) + setSelectedMaterialCode(materialCode) + + try { + toast.loading(`POS 파일 목록 조회 중... (${materialCode})`, { id: `pos-check-${materialCode}` }) + + const result = await checkPosFileExists(materialCode) + + if (result.exists && result.files && result.files.length > 0) { + // 파일 정보를 상세하게 가져오기 위해 getDownloadUrlByMaterialCode 사용 + const detailResult = await getDownloadUrlByMaterialCode(materialCode) + + if (detailResult.success && detailResult.availableFiles) { + setPosFiles(detailResult.availableFiles) + setPosDialogOpen(true) + toast.success(`${result.fileCount}개의 POS 파일을 찾았습니다`, { id: `pos-check-${materialCode}` }) + } else { + toast.error('POS 파일 정보를 가져올 수 없습니다', { id: `pos-check-${materialCode}` }) + } + } else { + toast.error(result.error || 'POS 파일을 찾을 수 없습니다', { id: `pos-check-${materialCode}` }) + } + } catch (error) { + console.error("POS 파일 조회 오류:", error) + toast.error("POS 파일 조회에 실패했습니다", { id: `pos-check-${materialCode}` }) + } finally { + setLoadingPosFiles(false) + } + } + + // POS 파일 다운로드 실행 + const handleDownloadPosFile = async (fileIndex: number, fileName: string) => { + if (!selectedMaterialCode) return + + setDownloadingFileIndex(fileIndex) + + try { + toast.loading(`POS 파일 다운로드 준비 중...`, { id: `download-${fileIndex}` }) + + // viewerType에 따라 다른 엔드포인트 사용 + const endpoint = viewerType === 'partners' + ? `/api/pos/download-on-demand-partners` // 복호화 포함 + : `/api/pos/download-on-demand` // 암호화 파일 그대로 + + const downloadUrl = `${endpoint}?materialCode=${encodeURIComponent(selectedMaterialCode)}&fileIndex=${fileIndex}` + + toast.success(`POS 파일 다운로드 시작: ${fileName}`, { id: `download-${fileIndex}` }) + window.open(downloadUrl, '_blank', 'noopener,noreferrer') + + // 다운로드 시작 후 잠시 대기 후 상태 초기화 + setTimeout(() => { + setDownloadingFileIndex(null) + }, 1000) + } catch (error) { + console.error("POS 파일 다운로드 오류:", error) + toast.error("POS 파일 다운로드에 실패했습니다", { id: `download-${fileIndex}` }) + setDownloadingFileIndex(null) + } + } + + // POS 다이얼로그 닫기 + const handleClosePosDialog = () => { + setPosDialogOpen(false) + setSelectedMaterialCode("") + setPosFiles([]) + setDownloadingFileIndex(null) + } + if (!items || items.length === 0) { return null } @@ -51,6 +154,7 @@ export function ContractItemsCard({ items, currency }: ContractItemsCardProps) { <th className="px-4 py-3 text-right font-medium">조정금액(ZPDT)</th> <th className="px-4 py-3 text-right font-medium">최종정가(NETWR)</th> <th className="px-4 py-3 text-center font-medium">납기일자</th> + <th className="px-4 py-3 text-center font-medium">POS</th> </tr> </thead> <tbody> @@ -96,12 +200,40 @@ export function ContractItemsCard({ items, currency }: ContractItemsCardProps) { <td className="px-4 py-3 text-center"> {item.ZPO_DLV_DT || "-"} </td> + <td className="px-4 py-3 text-center"> + {item.materialNo ? ( + <Button + variant="ghost" + size="sm" + className="h-8 px-2 text-xs text-green-600 hover:text-green-800" + onClick={() => handleOpenPosDialog(item.materialNo!)} + disabled={loadingPosFiles && selectedMaterialCode === item.materialNo} + title={`POS 파일 다운로드 (자재코드: ${item.materialNo})`} + > + <FileText className="h-3 w-3 mr-1" /> + <Download className="h-3 w-3 mr-1" /> + {loadingPosFiles && selectedMaterialCode === item.materialNo ? '조회중...' : 'POS'} + </Button> + ) : ( + <span className="text-muted-foreground">-</span> + )} + </td> </tr> ))} </tbody> </table> </div> </CardContent> + + {/* POS 파일 선택 다이얼로그 */} + <PosFileSelectionDialog + isOpen={posDialogOpen} + onClose={handleClosePosDialog} + materialCode={selectedMaterialCode} + files={posFiles} + onDownload={handleDownloadPosFile} + downloadingIndex={downloadingFileIndex} + /> </Card> ) } |
