summaryrefslogtreecommitdiff
path: root/lib/bidding/receive
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-11-28 03:12:57 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-11-28 03:12:57 +0000
commit9cda8482660a87fd98c9ee43f507d75ff75b4e23 (patch)
tree67eb1fc24eec7c4e61d3154f7b09fc5349454672 /lib/bidding/receive
parentf57898bd240d068301ce3ef477f52cff1234e4ee (diff)
(최겸) 구매 입찰 피드백 반영(90%)
Diffstat (limited to 'lib/bidding/receive')
-rw-r--r--lib/bidding/receive/biddings-receive-columns.tsx76
-rw-r--r--lib/bidding/receive/biddings-receive-table.tsx53
2 files changed, 99 insertions, 30 deletions
diff --git a/lib/bidding/receive/biddings-receive-columns.tsx b/lib/bidding/receive/biddings-receive-columns.tsx
index 4bde849c..9650574a 100644
--- a/lib/bidding/receive/biddings-receive-columns.tsx
+++ b/lib/bidding/receive/biddings-receive-columns.tsx
@@ -58,6 +58,7 @@ type BiddingReceiveItem = {
interface GetColumnsProps {
setRowAction: React.Dispatch<React.SetStateAction<DataTableRowAction<BiddingReceiveItem> | null>>
+ onParticipantClick?: (biddingId: number, participantType: 'expected' | 'participated' | 'declined' | 'pending') => void
}
// 상태별 배지 색상
@@ -89,7 +90,7 @@ const formatCurrency = (amount: string | number | null, currency = 'KRW') => {
}).format(numAmount)
}
-export function getBiddingsReceiveColumns({ setRowAction }: GetColumnsProps): ColumnDef<BiddingReceiveItem>[] {
+export function getBiddingsReceiveColumns({ setRowAction, onParticipantClick }: GetColumnsProps): ColumnDef<BiddingReceiveItem>[] {
return [
// ░░░ 선택 ░░░
@@ -195,24 +196,17 @@ export function getBiddingsReceiveColumns({ setRowAction }: GetColumnsProps): Co
if (!startDate || !endDate) return <span className="text-muted-foreground">-</span>
- const now = new Date()
const startObj = new Date(startDate)
const endObj = new Date(endDate)
-
- const isActive = now >= startObj && now <= endObj
- const isPast = now > endObj
// UI 표시용 KST 변환
const formatKst = (d: Date) => new Date(d.getTime() + 9 * 60 * 60 * 1000).toISOString().slice(0, 16).replace('T', ' ')
return (
<div className="text-xs">
- <div className={`${isActive ? 'text-green-600 font-medium' : isPast ? 'text-red-600' : 'text-gray-600'}`}>
+ <div>
{formatKst(startObj)} ~ {formatKst(endObj)}
</div>
- {isActive && (
- <Badge variant="default" className="text-xs mt-1">진행중</Badge>
- )}
</div>
)
},
@@ -251,10 +245,18 @@ export function getBiddingsReceiveColumns({ setRowAction }: GetColumnsProps): Co
id: "participantExpected",
header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="참여예정협력사" />,
cell: ({ row }) => (
- <div className="flex items-center gap-1">
- <Users className="h-4 w-4 text-blue-500" />
- <span className="text-sm font-medium">{row.original.participantExpected}</span>
- </div>
+ <Button
+ variant="ghost"
+ size="sm"
+ className="h-auto p-1 hover:bg-blue-50"
+ onClick={() => onParticipantClick?.(row.original.id, 'expected')}
+ disabled={row.original.participantExpected === 0}
+ >
+ <div className="flex items-center gap-1">
+ <Users className="h-4 w-4 text-blue-500" />
+ <span className="text-sm font-medium">{row.original.participantExpected}</span>
+ </div>
+ </Button>
),
size: 100,
meta: { excelHeader: "참여예정협력사" },
@@ -265,10 +267,18 @@ export function getBiddingsReceiveColumns({ setRowAction }: GetColumnsProps): Co
id: "participantParticipated",
header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="참여협력사" />,
cell: ({ row }) => (
- <div className="flex items-center gap-1">
- <CheckCircle className="h-4 w-4 text-green-500" />
- <span className="text-sm font-medium">{row.original.participantParticipated}</span>
- </div>
+ <Button
+ variant="ghost"
+ size="sm"
+ className="h-auto p-1 hover:bg-green-50"
+ onClick={() => onParticipantClick?.(row.original.id, 'participated')}
+ disabled={row.original.participantParticipated === 0}
+ >
+ <div className="flex items-center gap-1">
+ <CheckCircle className="h-4 w-4 text-green-500" />
+ <span className="text-sm font-medium">{row.original.participantParticipated}</span>
+ </div>
+ </Button>
),
size: 100,
meta: { excelHeader: "참여협력사" },
@@ -279,10 +289,18 @@ export function getBiddingsReceiveColumns({ setRowAction }: GetColumnsProps): Co
id: "participantDeclined",
header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="포기협력사" />,
cell: ({ row }) => (
- <div className="flex items-center gap-1">
- <XCircle className="h-4 w-4 text-red-500" />
- <span className="text-sm font-medium">{row.original.participantDeclined}</span>
- </div>
+ <Button
+ variant="ghost"
+ size="sm"
+ className="h-auto p-1 hover:bg-red-50"
+ onClick={() => onParticipantClick?.(row.original.id, 'declined')}
+ disabled={row.original.participantDeclined === 0}
+ >
+ <div className="flex items-center gap-1">
+ <XCircle className="h-4 w-4 text-red-500" />
+ <span className="text-sm font-medium">{row.original.participantDeclined}</span>
+ </div>
+ </Button>
),
size: 100,
meta: { excelHeader: "포기협력사" },
@@ -293,10 +311,18 @@ export function getBiddingsReceiveColumns({ setRowAction }: GetColumnsProps): Co
id: "participantPending",
header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="미제출협력사" />,
cell: ({ row }) => (
- <div className="flex items-center gap-1">
- <Clock className="h-4 w-4 text-yellow-500" />
- <span className="text-sm font-medium">{row.original.participantPending}</span>
- </div>
+ <Button
+ variant="ghost"
+ size="sm"
+ className="h-auto p-1 hover:bg-yellow-50"
+ onClick={() => onParticipantClick?.(row.original.id, 'pending')}
+ disabled={row.original.participantPending === 0}
+ >
+ <div className="flex items-center gap-1">
+ <Clock className="h-4 w-4 text-yellow-500" />
+ <span className="text-sm font-medium">{row.original.participantPending}</span>
+ </div>
+ </Button>
),
size: 100,
meta: { excelHeader: "미제출협력사" },
diff --git a/lib/bidding/receive/biddings-receive-table.tsx b/lib/bidding/receive/biddings-receive-table.tsx
index 5bda921e..2b141d5e 100644
--- a/lib/bidding/receive/biddings-receive-table.tsx
+++ b/lib/bidding/receive/biddings-receive-table.tsx
@@ -22,7 +22,9 @@ import {
contractTypeLabels,
} from "@/db/schema"
// import { SpecificationMeetingDialog, PrDocumentsDialog } from "../list/bidding-detail-dialogs"
-import { openBiddingAction, earlyOpenBiddingAction } from "@/lib/bidding/actions"
+import { openBiddingAction } from "@/lib/bidding/actions"
+import { BiddingParticipantsDialog } from "@/components/bidding/receive/bidding-participants-dialog"
+import { getAllBiddingCompanies } from "@/lib/bidding/detail/service"
type BiddingReceiveItem = {
id: number
@@ -69,17 +71,49 @@ export function BiddingsReceiveTable({ promises }: BiddingsReceiveTableProps) {
const [isCompact, setIsCompact] = React.useState<boolean>(false)
// const [specMeetingDialogOpen, setSpecMeetingDialogOpen] = React.useState(false)
// const [prDocumentsDialogOpen, setPrDocumentsDialogOpen] = React.useState(false)
- // const [selectedBidding, setSelectedBidding] = React.useState<BiddingReceiveItem | null>(null)
+ const [selectedBidding, setSelectedBidding] = React.useState<BiddingReceiveItem | null>(null)
const [rowAction, setRowAction] = React.useState<DataTableRowAction<BiddingReceiveItem> | null>(null)
const [isOpeningBidding, setIsOpeningBidding] = React.useState(false)
+ // 협력사 다이얼로그 관련 상태
+ const [participantsDialogOpen, setParticipantsDialogOpen] = React.useState(false)
+ const [selectedParticipantType, setSelectedParticipantType] = React.useState<'expected' | 'participated' | 'declined' | 'pending' | null>(null)
+ const [selectedBiddingId, setSelectedBiddingId] = React.useState<number | null>(null)
+ const [participantCompanies, setParticipantCompanies] = React.useState<any[]>([])
+ const [isLoadingParticipants, setIsLoadingParticipants] = React.useState(false)
+
const router = useRouter()
const { data: session } = useSession()
+ // 협력사 클릭 핸들러
+ const handleParticipantClick = React.useCallback(async (biddingId: number, participantType: 'expected' | 'participated' | 'declined' | 'pending') => {
+ setSelectedBiddingId(biddingId)
+ setSelectedParticipantType(participantType)
+ setIsLoadingParticipants(true)
+ setParticipantsDialogOpen(true)
+
+ try {
+ // 협력사 데이터 로드 (모든 초대된 협력사)
+ const companies = await getAllBiddingCompanies(biddingId)
+
+ console.log('Loaded companies:', companies)
+
+ // 필터링 없이 모든 데이터 그대로 표시
+ // invitationStatus가 그대로 다이얼로그에 표시됨
+ setParticipantCompanies(companies)
+ } catch (error) {
+ console.error('Failed to load participant companies:', error)
+ toast.error('협력사 목록을 불러오는데 실패했습니다.')
+ setParticipantCompanies([])
+ } finally {
+ setIsLoadingParticipants(false)
+ }
+ }, [])
+
const columns = React.useMemo(
- () => getBiddingsReceiveColumns({ setRowAction }),
- [setRowAction]
+ () => getBiddingsReceiveColumns({ setRowAction, onParticipantClick: handleParticipantClick }),
+ [setRowAction, handleParticipantClick]
)
// rowAction 변경 감지하여 해당 다이얼로그 열기
@@ -96,7 +130,7 @@ export function BiddingsReceiveTable({ promises }: BiddingsReceiveTableProps) {
break
}
}
- }, [rowAction])
+ }, [rowAction, router])
const filterFields: DataTableFilterField<BiddingReceiveItem>[] = [
{
@@ -248,6 +282,15 @@ export function BiddingsReceiveTable({ promises }: BiddingsReceiveTableProps) {
onOpenChange={handlePrDocumentsDialogClose}
bidding={selectedBidding}
/> */}
+
+ {/* 참여 협력사 다이얼로그 */}
+ <BiddingParticipantsDialog
+ open={participantsDialogOpen}
+ onOpenChange={setParticipantsDialogOpen}
+ biddingId={selectedBiddingId}
+ participantType={selectedParticipantType}
+ companies={participantCompanies}
+ />
</>
)
}