summaryrefslogtreecommitdiff
path: root/lib/vendor-document-list/ship/swp-workflow-panel.tsx
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-06-13 07:11:18 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-06-13 07:11:18 +0000
commit0fddf148402fd6b99a1b3800d73679899bcb2ed3 (patch)
treeeb51c02e6fa6037ddcc38a3b57d10d8c739125cf /lib/vendor-document-list/ship/swp-workflow-panel.tsx
parentc72d0897f7b37843109c86f61d97eba05ba3ca0d (diff)
(대표님) 20250613 16시 10분 global css, b-rfq, document 등
Diffstat (limited to 'lib/vendor-document-list/ship/swp-workflow-panel.tsx')
-rw-r--r--lib/vendor-document-list/ship/swp-workflow-panel.tsx370
1 files changed, 0 insertions, 370 deletions
diff --git a/lib/vendor-document-list/ship/swp-workflow-panel.tsx b/lib/vendor-document-list/ship/swp-workflow-panel.tsx
deleted file mode 100644
index ded306e7..00000000
--- a/lib/vendor-document-list/ship/swp-workflow-panel.tsx
+++ /dev/null
@@ -1,370 +0,0 @@
-"use client"
-
-import * as React from "react"
-import { Send, Eye, CheckCircle, Clock, RefreshCw, AlertTriangle, Loader2 } from "lucide-react"
-import { toast } from "sonner"
-
-import { Button } from "@/components/ui/button"
-import {
- Popover,
- PopoverContent,
- PopoverTrigger,
-} from "@/components/ui/popover"
-import { Badge } from "@/components/ui/badge"
-import { Separator } from "@/components/ui/separator"
-import { Progress } from "@/components/ui/progress"
-import type { EnhancedDocument } from "@/types/enhanced-documents"
-
-interface SWPWorkflowPanelProps {
- contractId: number
- documents: EnhancedDocument[]
- onWorkflowUpdate?: () => void
-}
-
-type WorkflowStatus =
- | 'IDLE' // 대기 상태
- | 'SUBMITTED' // 목록 전송됨
- | 'UNDER_REVIEW' // 검토 중
- | 'CONFIRMED' // 컨펌됨
- | 'REVISION_REQUIRED' // 수정 요청됨
- | 'RESUBMITTED' // 재전송됨
- | 'APPROVED' // 최종 승인됨
-
-interface WorkflowState {
- status: WorkflowStatus
- lastUpdatedAt?: string
- pendingActions: string[]
- confirmationData?: any
- revisionComments?: string[]
- approvalData?: any
-}
-
-export function SWPWorkflowPanel({
- contractId,
- documents,
- onWorkflowUpdate
-}: SWPWorkflowPanelProps) {
- const [workflowState, setWorkflowState] = React.useState<WorkflowState | null>(null)
- const [isLoading, setIsLoading] = React.useState(false)
- const [actionProgress, setActionProgress] = React.useState(0)
-
- // 워크플로우 상태 조회
- const fetchWorkflowStatus = async () => {
- setIsLoading(true)
- try {
- const response = await fetch(`/api/sync/workflow/status?contractId=${contractId}&targetSystem=SWP`)
- if (!response.ok) throw new Error('Failed to fetch workflow status')
-
- const status = await response.json()
- setWorkflowState(status)
- } catch (error) {
- console.error('Failed to fetch workflow status:', error)
- toast.error('워크플로우 상태를 확인할 수 없습니다')
- } finally {
- setIsLoading(false)
- }
- }
-
- // 컴포넌트 마운트 시 상태 조회
- React.useEffect(() => {
- fetchWorkflowStatus()
- }, [contractId])
-
- // 워크플로우 액션 실행
- const executeWorkflowAction = async (action: string) => {
- setActionProgress(0)
- setIsLoading(true)
-
- try {
- // 진행률 시뮬레이션
- const progressInterval = setInterval(() => {
- setActionProgress(prev => Math.min(prev + 20, 90))
- }, 200)
-
- const response = await fetch('/api/sync/workflow/action', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({
- contractId,
- targetSystem: 'SWP',
- action,
- documents: documents.map(doc => ({ id: doc.id, documentNo: doc.documentNo }))
- })
- })
-
- if (!response.ok) {
- const errorData = await response.json()
- throw new Error(errorData.message || 'Workflow action failed')
- }
-
- const result = await response.json()
-
- clearInterval(progressInterval)
- setActionProgress(100)
-
- setTimeout(() => {
- setActionProgress(0)
-
- if (result?.success) {
- toast.success(
- `${getActionLabel(action)} 완료`,
- { description: result?.message || '워크플로우가 성공적으로 진행되었습니다.' }
- )
- } else {
- toast.error(
- `${getActionLabel(action)} 실패`,
- { description: result?.message || '워크플로우 실행에 실패했습니다.' }
- )
- }
-
- fetchWorkflowStatus() // 상태 갱신
- onWorkflowUpdate?.()
- }, 500)
-
- } catch (error) {
- setActionProgress(0)
-
- toast.error(`${getActionLabel(action)} 실패`, {
- description: error instanceof Error ? error.message : '알 수 없는 오류가 발생했습니다.'
- })
- } finally {
- setIsLoading(false)
- }
- }
-
- const getActionLabel = (action: string): string => {
- switch (action) {
- case 'SUBMIT_LIST': return '목록 전송'
- case 'CHECK_CONFIRMATION': return '컨펌 확인'
- case 'RESUBMIT_REVISED': return '수정본 재전송'
- case 'CHECK_APPROVAL': return '승인 확인'
- default: return action
- }
- }
-
- const getStatusBadge = () => {
- if (isLoading) {
- return <Badge variant="secondary">확인 중...</Badge>
- }
-
- if (!workflowState) {
- return <Badge variant="destructive">오류</Badge>
- }
-
- switch (workflowState.status) {
- case 'IDLE':
- return <Badge variant="secondary">대기</Badge>
- case 'SUBMITTED':
- return (
- <Badge variant="default" className="gap-1 bg-blue-500">
- <Clock className="w-3 h-3" />
- 전송됨
- </Badge>
- )
- case 'UNDER_REVIEW':
- return (
- <Badge variant="default" className="gap-1 bg-yellow-500">
- <Eye className="w-3 h-3" />
- 검토 중
- </Badge>
- )
- case 'CONFIRMED':
- return (
- <Badge variant="default" className="gap-1 bg-green-500">
- <CheckCircle className="w-3 h-3" />
- 컨펌됨
- </Badge>
- )
- case 'REVISION_REQUIRED':
- return (
- <Badge variant="destructive" className="gap-1">
- <AlertTriangle className="w-3 h-3" />
- 수정 요청
- </Badge>
- )
- case 'RESUBMITTED':
- return (
- <Badge variant="default" className="gap-1 bg-orange-500">
- <RefreshCw className="w-3 h-3" />
- 재전송됨
- </Badge>
- )
- case 'APPROVED':
- return (
- <Badge variant="default" className="gap-1 bg-green-600">
- <CheckCircle className="w-3 h-3" />
- 승인 완료
- </Badge>
- )
- default:
- return <Badge variant="secondary">알 수 없음</Badge>
- }
- }
-
- const getAvailableActions = (): string[] => {
- if (!workflowState) return []
-
- switch (workflowState.status) {
- case 'IDLE':
- return ['SUBMIT_LIST']
- case 'SUBMITTED':
- return ['CHECK_CONFIRMATION']
- case 'UNDER_REVIEW':
- return ['CHECK_CONFIRMATION']
- case 'CONFIRMED':
- return [] // 컨펌되면 자동으로 다음 단계로
- case 'REVISION_REQUIRED':
- return ['RESUBMIT_REVISED']
- case 'RESUBMITTED':
- return ['CHECK_APPROVAL']
- case 'APPROVED':
- return [] // 완료 상태
- default:
- return []
- }
- }
-
- const availableActions = getAvailableActions()
-
- return (
- <Popover>
- <PopoverTrigger asChild>
- <div className="flex items-center gap-3">
- <Button
- variant="outline"
- size="sm"
- className="flex items-center border-orange-200 hover:bg-orange-50"
- disabled={isLoading}
- >
- {isLoading ? (
- <Loader2 className="w-4 h-4 animate-spin" />
- ) : (
- <RefreshCw className="w-4 h-4" />
- )}
- <span className="hidden sm:inline">SWP 워크플로우</span>
- {workflowState?.pendingActions && workflowState.pendingActions.length > 0 && (
- <Badge
- variant="destructive"
- className="h-5 w-5 p-0 text-xs flex items-center justify-center"
- >
- {workflowState.pendingActions.length}
- </Badge>
- )}
- </Button>
- </div>
- </PopoverTrigger>
-
- <PopoverContent className="w-80">
- <div className="space-y-4">
- <div className="space-y-2">
- <h4 className="font-medium">SWP 워크플로우 상태</h4>
- <div className="flex items-center justify-between">
- <span className="text-sm text-muted-foreground">현재 상태</span>
- {getStatusBadge()}
- </div>
- </div>
-
- {workflowState && (
- <div className="space-y-3">
- <Separator />
-
- {/* 대기 중인 액션들 */}
- {workflowState.pendingActions && workflowState.pendingActions.length > 0 && (
- <div className="space-y-2">
- <div className="text-sm font-medium">대기 중인 작업</div>
- {workflowState.pendingActions.map((action, index) => (
- <Badge key={index} variant="outline" className="mr-1">
- {getActionLabel(action)}
- </Badge>
- ))}
- </div>
- )}
-
- {/* 수정 요청 사항 */}
- {workflowState.revisionComments && workflowState.revisionComments.length > 0 && (
- <div className="space-y-2">
- <div className="text-sm font-medium text-red-600">수정 요청 사항</div>
- <div className="text-xs text-muted-foreground space-y-1">
- {workflowState.revisionComments.map((comment, index) => (
- <div key={index} className="p-2 bg-red-50 rounded text-red-700">
- {comment}
- </div>
- ))}
- </div>
- </div>
- )}
-
- {/* 마지막 업데이트 시간 */}
- {workflowState.lastUpdatedAt && (
- <div className="text-sm">
- <div className="text-muted-foreground">마지막 업데이트</div>
- <div className="font-medium">
- {new Date(workflowState.lastUpdatedAt).toLocaleString()}
- </div>
- </div>
- )}
-
- {/* 진행률 표시 */}
- {isLoading && actionProgress > 0 && (
- <div className="space-y-2">
- <div className="flex items-center justify-between text-sm">
- <span>진행률</span>
- <span>{actionProgress}%</span>
- </div>
- <Progress value={actionProgress} className="h-2" />
- </div>
- )}
- </div>
- )}
-
- <Separator />
-
- {/* 액션 버튼들 */}
- <div className="space-y-2">
- {availableActions.length > 0 ? (
- availableActions.map((action) => (
- <Button
- key={action}
- onClick={() => executeWorkflowAction(action)}
- disabled={isLoading}
- className="w-full justify-start"
- size="sm"
- variant={action.includes('SUBMIT') || action.includes('RESUBMIT') ? 'default' : 'outline'}
- >
- {isLoading ? (
- <Loader2 className="w-4 h-4 mr-2 animate-spin" />
- ) : (
- <Send className="w-4 h-4 mr-2" />
- )}
- {getActionLabel(action)}
- </Button>
- ))
- ) : (
- <div className="text-sm text-muted-foreground text-center py-2">
- {workflowState?.status === 'APPROVED'
- ? '워크플로우가 완료되었습니다.'
- : '실행 가능한 작업이 없습니다.'}
- </div>
- )}
-
- {/* 상태 새로고침 버튼 */}
- <Button
- variant="outline"
- size="sm"
- onClick={fetchWorkflowStatus}
- disabled={isLoading}
- className="w-full"
- >
- {isLoading ? (
- <Loader2 className="w-4 h-4 mr-2 animate-spin" />
- ) : (
- <RefreshCw className="w-4 h-4 mr-2" />
- )}
- 상태 새로고침
- </Button>
- </div>
- </div>
- </PopoverContent>
- </Popover>
- )
-} \ No newline at end of file