summaryrefslogtreecommitdiff
path: root/lib/bidding/selection/bidding-item-table.tsx
blob: aa2b34ec02cd596b97a405c73c3e3a9b07312a92 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
'use client'

import * as React from 'react'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { getBiddingSelectionItemsAndPrices } from '@/lib/bidding/service'
import { formatNumber } from '@/lib/utils'
import { ScrollArea, ScrollBar } from '@/components/ui/scroll-area'

interface BiddingItemTableProps {
  biddingId: number
}

export function BiddingItemTable({ biddingId }: BiddingItemTableProps) {
  const [data, setData] = React.useState<{
    prItems: any[]
    vendorPrices: any[]
  }>({ prItems: [], vendorPrices: [] })
  const [loading, setLoading] = React.useState(true)

  React.useEffect(() => {
    let isMounted = true

    const loadData = async () => {
      try {
        setLoading(true)
        const { prItems, vendorPrices } = await getBiddingSelectionItemsAndPrices(biddingId)
        
        if (isMounted) {
          console.log('prItems', prItems)
          console.log('vendorPrices', vendorPrices)
          setData({ prItems, vendorPrices })
        }
      } catch (error) {
        console.error('Failed to load bidding items:', error)
      } finally {
        if (isMounted) {
          setLoading(false)
        }
      }
    }

    loadData()

    return () => {
      isMounted = false
    }
  }, [biddingId])

  // Memoize calculations
  const totals = React.useMemo(() => {
    const { prItems } = data
    return {
      quantity: prItems.reduce((sum, item) => sum + Number(item.quantity || 0), 0),
      weight: prItems.reduce((sum, item) => sum + Number(item.totalWeight || 0), 0),
      targetAmount: prItems.reduce((sum, item) => sum + Number(item.targetAmount || 0), 0)
    }
  }, [data.prItems])

  const vendorTotals = React.useMemo(() => {
    const { vendorPrices } = data
    return vendorPrices.map(vendor => {
      const total = vendor.itemPrices.reduce((sum: number, item: any) => sum + Number(item.amount || 0), 0)
      return {
        companyId: vendor.companyId,
        totalAmount: total
      }
    })
  }, [data.vendorPrices])

  if (loading) {
    return (
      <Card>
        <CardHeader>
          <CardTitle>응찰품목</CardTitle>
        </CardHeader>
        <CardContent>
          <div className="flex items-center justify-center py-8">
            <div className="text-sm text-muted-foreground">로딩 중...</div>
          </div>
        </CardContent>
      </Card>
    )
  }

  const { prItems, vendorPrices } = data


  return (
    <Card>
      <CardHeader>
        <CardTitle>응찰품목</CardTitle>
      </CardHeader>
      <CardContent>
        <ScrollArea className="w-full whitespace-nowrap rounded-md border">
          <div className="w-max min-w-full">
            <table className="w-full caption-bottom text-sm">
              <thead className="[&_tr]:border-b">
                {/* Header Row 1: Base Info + Vendor Groups */}
                <tr className="border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted">
                  <th className="h-12 px-4 text-center align-middle font-medium text-muted-foreground border-r" rowSpan={2}>자재번호</th>
                  <th className="h-12 px-4 text-center align-middle font-medium text-muted-foreground border-r" rowSpan={2}>자재내역</th>
                  <th className="h-12 px-4 text-center align-middle font-medium text-muted-foreground border-r" rowSpan={2}>자재내역상세</th>
                  <th className="h-12 px-4 text-center align-middle font-medium text-muted-foreground border-r" rowSpan={2}>구매단위</th>
                  <th className="h-12 px-4 text-center align-middle font-medium text-muted-foreground border-r" rowSpan={2}>수량</th>
                  <th className="h-12 px-4 text-center align-middle font-medium text-muted-foreground border-r" rowSpan={2}>단위</th>
                  <th className="h-12 px-4 text-center align-middle font-medium text-muted-foreground border-r" rowSpan={2}>총중량</th>
                  <th className="h-12 px-4 text-center align-middle font-medium text-muted-foreground border-r" rowSpan={2}>중량단위</th>
                  <th className="h-12 px-4 text-center align-middle font-medium text-muted-foreground border-r" rowSpan={2}>내정단가</th>
                  <th className="h-12 px-4 text-center align-middle font-medium text-muted-foreground border-r" rowSpan={2}>내정액</th>
                  <th className="h-12 px-4 text-center align-middle font-medium text-muted-foreground border-r" rowSpan={2}>통화</th>
                  
                  {vendorPrices.map((vendor) => (
                    <th key={vendor.companyId} colSpan={4} className="h-12 px-4 text-center align-middle font-medium text-muted-foreground border-r bg-muted/20">
                      {vendor.companyName}
                    </th>
                  ))}
                </tr>
                {/* Header Row 2: Vendor Sub-columns */}
                <tr className="border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted">
                  {vendorPrices.map((vendor) => (
                    <React.Fragment key={vendor.companyId}>
                      <th className="h-10 px-2 text-center align-middle font-medium text-muted-foreground border-r bg-muted/10">단가</th>
                      <th className="h-10 px-2 text-center align-middle font-medium text-muted-foreground border-r bg-muted/10">총액</th>
                      <th className="h-10 px-2 text-center align-middle font-medium text-muted-foreground border-r bg-muted/10">통화</th>
                      <th className="h-10 px-2 text-center align-middle font-medium text-muted-foreground border-r bg-muted/10">내정액(%)</th>
                    </React.Fragment>
                  ))}
                </tr>
              </thead>
              <tbody className="[&_tr:last-child]:border-0">
                {/* Summary Row */}
                <tr className="border-b transition-colors hover:bg-muted/50 bg-muted/30 font-semibold">
                  <td className="p-4 align-middle text-center border-r" colSpan={4}>합계</td>
                  <td className="p-4 align-middle text-right border-r">{formatNumber(totals.quantity)}</td>
                  <td className="p-4 align-middle text-center border-r">-</td>
                  <td className="p-4 align-middle text-right border-r">{formatNumber(totals.weight)}</td>
                  <td className="p-4 align-middle text-center border-r">-</td>
                  <td className="p-4 align-middle text-center border-r">-</td>
                  <td className="p-4 align-middle text-right border-r">{formatNumber(totals.targetAmount)}</td>
                  <td className="p-4 align-middle text-center border-r">KRW</td>
                  
                  {vendorPrices.map((vendor) => {
                     const vTotal = vendorTotals.find(t => t.companyId === vendor.companyId)?.totalAmount || 0
                     const ratio = totals.targetAmount > 0 ? (vTotal / totals.targetAmount) * 100 : 0
                     return (
                      <React.Fragment key={vendor.companyId}>
                        <td className="p-4 align-middle text-center border-r">-</td>
                        <td className="p-4 align-middle text-right border-r">{formatNumber(vTotal)}</td>
                        <td className="p-4 align-middle text-center border-r">{vendor.currency}</td>
                        <td className="p-4 align-middle text-right border-r">{formatNumber(ratio, 0)}%</td>
                      </React.Fragment>
                    )
                  })}
                </tr>

                {/* Data Rows */}
                {prItems.map((item) => (
                  <tr key={item.id} className="border-b transition-colors hover:bg-muted/50">
                    <td className="p-4 align-middle border-r">{item.materialNumber}</td>
                    <td className="p-4 align-middle border-r min-w-[150px]">{item.materialInfo}</td>
                    <td className="p-4 align-middle border-r min-w-[150px]">{item.specification}</td>
                    <td className="p-4 align-middle text-center border-r">{item.purchaseUnit}</td>
                    <td className="p-4 align-middle text-right border-r">{formatNumber(item.quantity)}</td>
                    <td className="p-4 align-middle text-center border-r">{item.quantityUnit}</td>
                    <td className="p-4 align-middle text-right border-r">{formatNumber(item.totalWeight)}</td>
                    <td className="p-4 align-middle text-center border-r">{item.weightUnit}</td>
                    <td className="p-4 align-middle text-right border-r">{formatNumber(item.targetUnitPrice)}</td>
                    <td className="p-4 align-middle text-right border-r">{formatNumber(item.targetAmount)}</td>
                    <td className="p-4 align-middle text-center border-r">{item.currency}</td>
                    
                    {vendorPrices.map((vendor) => {
                      const bidItem = vendor.itemPrices.find((p: any) => p.prItemId === item.id)
                      const bidAmount = bidItem ? bidItem.amount : 0
                      const targetAmt = Number(item.targetAmount || 0)
                      const ratio = targetAmt > 0 && bidAmount > 0 ? (bidAmount / targetAmt) * 100 : 0
                      
                      return (
                        <React.Fragment key={vendor.companyId}>
                          <td className="p-4 align-middle text-right border-r bg-muted/5">
                            {bidItem ? formatNumber(bidItem.unitPrice) : '-'}
                          </td>
                          <td className="p-4 align-middle text-right border-r bg-muted/5">
                            {bidItem ? formatNumber(bidItem.amount) : '-'}
                          </td>
                          <td className="p-4 align-middle text-center border-r bg-muted/5">
                            {vendor.currency}
                          </td>
                          <td className="p-4 align-middle text-right border-r bg-muted/5">
                            {bidItem && ratio > 0 ? `${formatNumber(ratio, 0)}%` : '-'}
                          </td>
                        </React.Fragment>
                      )
                    })}
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
          <ScrollBar orientation="horizontal" />
        </ScrollArea>
      </CardContent>
    </Card>
  )
}