summaryrefslogtreecommitdiff
path: root/components/contract/contract-items-card.tsx
diff options
context:
space:
mode:
authorjoonhoekim <26rote@gmail.com>2025-11-20 19:36:01 +0900
committerjoonhoekim <26rote@gmail.com>2025-11-20 19:36:01 +0900
commit935fd22e17afc034a472bc2d159de7b9f5e5dcae (patch)
tree6beea33ab38750be17632dffca5e05e644647365 /components/contract/contract-items-card.tsx
parentb75b1cd920efd61923f7b2dbc4c49987b7b0c4e1 (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.tsx134
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>
)
}