@opsnow-mcp/opsnow-mcp-common-ui-server 1.0.21 → 1.0.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/components/data/icon-names.js +129 -0
- package/build/components/examples/opsnow-common-calendar-examples-data.js +48 -0
- package/build/components/examples/opsnow-common-chart-examples-data.js +6843 -0
- package/build/components/examples/opsnow-common-data-status-examples-data.js +65 -0
- package/build/components/examples/opsnow-common-file-upload-examples-data.js +95 -0
- package/build/components/examples/opsnow-common-forms-examples-data.js +1715 -0
- package/build/components/examples/opsnow-common-grid-examples-data.js +2389 -0
- package/build/components/examples/opsnow-common-icons-examples-data.js +72 -0
- package/build/components/examples/opsnow-common-layout-examples-data.js +97 -0
- package/build/components/examples/opsnow-common-notification-examples-data.js +78 -0
- package/build/components/examples/opsnow-common-pagination-examples-data.js +82 -0
- package/build/components/examples/opsnow-common-popup-examples-data.js +205 -0
- package/build/components/examples/opsnow-common-progress-examples-data.js +86 -0
- package/build/components/examples/opsnow-common-select-examples-data.js +131 -0
- package/build/components/examples/opsnow-common-stepper-examples-data.js +180 -0
- package/build/components/examples/opsnow-common-storage-examples-data.js +8 -0
- package/build/components/examples/opsnow-common-tab-examples-data.js +87 -0
- package/build/components/examples/opsnow-common-toast-popup-examples-data.js +129 -0
- package/build/components/examples/opsnow-common-tooltip-examples-data.js +80 -0
- package/build/components/examples/opsnow-common-typography-examples-data.js +366 -0
- package/build/components/opsnow-common-calendar.js +228 -0
- package/build/components/opsnow-common-chart.js +1643 -0
- package/build/components/opsnow-common-data-status.js +116 -0
- package/build/components/opsnow-common-examples.js +109 -0
- package/build/components/opsnow-common-file-upload.js +57 -0
- package/build/components/opsnow-common-forms.js +1009 -0
- package/build/components/opsnow-common-grid.js +352 -0
- package/build/components/opsnow-common-icons.js +139 -0
- package/build/components/opsnow-common-layout.js +141 -0
- package/build/components/opsnow-common-notification.js +110 -0
- package/build/components/opsnow-common-pagination.js +164 -0
- package/build/components/opsnow-common-popup.js +71 -0
- package/build/components/opsnow-common-progress.js +177 -0
- package/build/components/opsnow-common-select.js +135 -0
- package/build/components/opsnow-common-stepper.js +72 -0
- package/build/components/opsnow-common-tab.js +111 -0
- package/build/components/opsnow-common-toast-popup.js +135 -0
- package/build/components/opsnow-common-tooltip.js +204 -0
- package/build/components/opsnow-common-typography.js +93 -0
- package/build/index.js +135 -0
- package/package.json +1 -1
|
@@ -0,0 +1,2389 @@
|
|
|
1
|
+
// DataGrid 컴포넌트 예제 데이터
|
|
2
|
+
export const DataGridExamples = [
|
|
3
|
+
{
|
|
4
|
+
title: '기본 데이터 그리드',
|
|
5
|
+
description: '기본 데이터 그리드 예제입니다.',
|
|
6
|
+
code_props_usage: `
|
|
7
|
+
import { useCommonComponents, useGlobalContext } from '@opsnow-common/opsnow-finops-common-ui-loader'
|
|
8
|
+
import i18n from '@opsnow-common/opsnow-finops-common-i18n'
|
|
9
|
+
|
|
10
|
+
// 컬럼 정의
|
|
11
|
+
const columnDefs = [
|
|
12
|
+
{ headerName: 'Make', field: 'make' },
|
|
13
|
+
{ headerName: 'Model', field: 'model' },
|
|
14
|
+
{ headerName: 'Price', field: 'price' },
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
// 데이터 정의
|
|
18
|
+
const initRowData = [
|
|
19
|
+
{ make: 'Toyota', model: 'Celica', price: 35000 },
|
|
20
|
+
{ make: 'Ford', model: 'Mondeo', price: 32000 },
|
|
21
|
+
{ make: 'Porsche', model: 'Boxster', price: 72000 }
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
const gridOptions = {
|
|
25
|
+
animateRows: true,
|
|
26
|
+
// 체크박스 사용하고 싶지 않으면 checkboxes 를 false로 해야됨.
|
|
27
|
+
// enableClickSelection: 행 선택은 활성화 시키고 싶으면 true로 설정
|
|
28
|
+
rowSelection: { mode: 'singleRow', checkboxes: false, enableClickSelection: true },
|
|
29
|
+
tooltipShowDelay: 100,
|
|
30
|
+
domLayout: 'autoHeight',
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const { OpsnowCommonDataGrid } = useCommonComponents();
|
|
34
|
+
const { CommonConst } = useGlobalContext()
|
|
35
|
+
// rowData와 status 상태를 관리
|
|
36
|
+
const [rowData, setRowData] = useState([]) // 초기에는 빈 배열로 설정
|
|
37
|
+
const [status, setStatus] = useState(CommonConst.GRID_STATUS.LOADING) // 초기에는 LOADING 상태
|
|
38
|
+
const gridRef = useRef(null)
|
|
39
|
+
|
|
40
|
+
// 데이터 지연 설정을 위한 useEffect
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
// 1초 후에 데이터를 설정
|
|
43
|
+
const timeoutId = setTimeout(() => {
|
|
44
|
+
setRowData(initRowData)
|
|
45
|
+
setStatus(CommonConst.GRID_STATUS.DATA_EXISTS)
|
|
46
|
+
}, 1000)
|
|
47
|
+
|
|
48
|
+
// 컴포넌트 언마운트 시 타임아웃 클리어
|
|
49
|
+
return () => {
|
|
50
|
+
clearTimeout(timeoutId)
|
|
51
|
+
}
|
|
52
|
+
}, [])
|
|
53
|
+
`,
|
|
54
|
+
code: `<OpsnowCommonDataGrid
|
|
55
|
+
key={status}
|
|
56
|
+
columnDefs={columnDefs}
|
|
57
|
+
rowData={rowData}
|
|
58
|
+
status={status} // 상태를 전달
|
|
59
|
+
gridOptions={gridOptions}
|
|
60
|
+
langCd={i18n.getLocale()}
|
|
61
|
+
/>`
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
title: 'Search + Pagination 추가',
|
|
65
|
+
description: 'Search + Pagination 추가 예제입니다.',
|
|
66
|
+
code_props_usage: `
|
|
67
|
+
import { useCommonComponents, useGlobalContext } from '@opsnow-common/opsnow-finops-common-ui-loader'
|
|
68
|
+
import i18n from '@opsnow-common/opsnow-finops-common-i18n'
|
|
69
|
+
|
|
70
|
+
// 컬럼 정의
|
|
71
|
+
const columnDefs = [
|
|
72
|
+
{ headerName: 'Make', field: 'make' },
|
|
73
|
+
{ headerName: 'Model', field: 'model' },
|
|
74
|
+
{ headerName: 'Price', field: 'price' },
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
// 데이터 정의
|
|
78
|
+
const initRowData = [
|
|
79
|
+
{ make: 'Toyota', model: 'Celica', price: 35000 },
|
|
80
|
+
{ make: 'Ford', model: 'Mondeo', price: 32000 },
|
|
81
|
+
{ make: 'Porsche', model: 'Boxster', price: 72000 }
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
const gridOptions = {
|
|
85
|
+
animateRows: true,
|
|
86
|
+
// 체크박스 사용하고 싶지 않으면 checkboxes 를 false로 해야됨.
|
|
87
|
+
// enableClickSelection: 행 선택은 활성화 시키고 싶으면 true로 설정
|
|
88
|
+
rowSelection: { mode: 'singleRow', checkboxes: false, enableClickSelection: true },
|
|
89
|
+
tooltipShowDelay: 100,
|
|
90
|
+
domLayout: 'autoHeight',
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const { OpsnowCommonDataGrid } = useCommonComponents();
|
|
94
|
+
const { CommonConst } = useGlobalContext()
|
|
95
|
+
// rowData와 status 상태를 관리
|
|
96
|
+
const [rowData, setRowData] = useState([]) // 초기에는 빈 배열로 설정
|
|
97
|
+
const [status, setStatus] = useState(CommonConst.GRID_STATUS.LOADING) // 초기에는 LOADING 상태
|
|
98
|
+
const gridRef = useRef(null)
|
|
99
|
+
|
|
100
|
+
// 데이터 지연 설정을 위한 useEffect
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
// 1초 후에 데이터를 설정
|
|
103
|
+
const timeoutId = setTimeout(() => {
|
|
104
|
+
setRowData(initRowData)
|
|
105
|
+
setStatus(CommonConst.GRID_STATUS.DATA_EXISTS)
|
|
106
|
+
}, 1000)
|
|
107
|
+
|
|
108
|
+
// 컴포넌트 언마운트 시 타임아웃 클리어
|
|
109
|
+
return () => {
|
|
110
|
+
clearTimeout(timeoutId)
|
|
111
|
+
}
|
|
112
|
+
}, [])
|
|
113
|
+
`,
|
|
114
|
+
code: `<OpsnowCommonDataGrid
|
|
115
|
+
columnDefs={columnDefs}
|
|
116
|
+
rowData={rowData}
|
|
117
|
+
status={status} // 상태를 전달
|
|
118
|
+
gridOptions={gridOptions}
|
|
119
|
+
usedSearch={true}
|
|
120
|
+
pagination={true}
|
|
121
|
+
langCd={i18n.getLocale()}
|
|
122
|
+
/>`
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
title: 'Insert Button Inside the Common Grid Search Section',
|
|
126
|
+
description: 'Insert Button Inside the Common Grid Search Section 예제입니다.',
|
|
127
|
+
code_props_usage: `
|
|
128
|
+
import { useCommonComponents, useGlobalContext } from '@opsnow-common/opsnow-finops-common-ui-loader'
|
|
129
|
+
import i18n from '@opsnow-common/opsnow-finops-common-i18n'
|
|
130
|
+
|
|
131
|
+
// 컬럼 정의
|
|
132
|
+
const columnDefs = [
|
|
133
|
+
{ headerName: 'Make', field: 'make' },
|
|
134
|
+
{ headerName: 'Model', field: 'model' },
|
|
135
|
+
{ headerName: 'Price', field: 'price' },
|
|
136
|
+
]
|
|
137
|
+
|
|
138
|
+
// 데이터 정의
|
|
139
|
+
const initRowData = [
|
|
140
|
+
{ make: 'Toyota', model: 'Celica', price: 35000 },
|
|
141
|
+
{ make: 'Ford', model: 'Mondeo', price: 32000 },
|
|
142
|
+
{ make: 'Porsche', model: 'Boxster', price: 72000 }
|
|
143
|
+
]
|
|
144
|
+
|
|
145
|
+
const gridOptions = {
|
|
146
|
+
animateRows: true,
|
|
147
|
+
// 체크박스 사용하고 싶지 않으면 checkboxes 를 false로 해야됨.
|
|
148
|
+
// enableClickSelection: 행 선택은 활성화 시키고 싶으면 true로 설정
|
|
149
|
+
rowSelection: { mode: 'singleRow', checkboxes: false, enableClickSelection: true },
|
|
150
|
+
tooltipShowDelay: 100,
|
|
151
|
+
domLayout: 'autoHeight',
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const { OpsnowCommonDataGrid, OpsnowCommonButton } = useCommonComponents();
|
|
155
|
+
const { CommonConst } = useGlobalContext()
|
|
156
|
+
// rowData와 status 상태를 관리
|
|
157
|
+
const [rowData, setRowData] = useState([]) // 초기에는 빈 배열로 설정
|
|
158
|
+
const [status, setStatus] = useState(CommonConst.GRID_STATUS.LOADING) // 초기에는 LOADING 상태
|
|
159
|
+
const gridRef = useRef(null)
|
|
160
|
+
|
|
161
|
+
// 데이터 지연 설정을 위한 useEffect
|
|
162
|
+
useEffect(() => {
|
|
163
|
+
// 1초 후에 데이터를 설정
|
|
164
|
+
const timeoutId = setTimeout(() => {
|
|
165
|
+
setRowData(initRowData)
|
|
166
|
+
setStatus(CommonConst.GRID_STATUS.DATA_EXISTS)
|
|
167
|
+
}, 1000)
|
|
168
|
+
|
|
169
|
+
// 컴포넌트 언마운트 시 타임아웃 클리어
|
|
170
|
+
return () => {
|
|
171
|
+
clearTimeout(timeoutId)
|
|
172
|
+
}
|
|
173
|
+
}, [])
|
|
174
|
+
|
|
175
|
+
// 다운로드 핸들러
|
|
176
|
+
const downloadTest = () => {
|
|
177
|
+
if (gridRef.current) {
|
|
178
|
+
gridRef.current.downloadExcel('test.xlsx')
|
|
179
|
+
// 또는 CSV 다운로드
|
|
180
|
+
// gridRef1.current.downloadCsv('test.csv')
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
`,
|
|
184
|
+
code: `<OpsnowCommonDataGrid
|
|
185
|
+
ref={gridRef}
|
|
186
|
+
columnDefs={columnDefs}
|
|
187
|
+
rowData={rowData}
|
|
188
|
+
status={status} // 상태를 전달
|
|
189
|
+
gridOptions={gridOptions}
|
|
190
|
+
usedSearch={true}
|
|
191
|
+
pagination={true}
|
|
192
|
+
langCd={i18n.getLocale()}
|
|
193
|
+
>
|
|
194
|
+
<OpsnowCommonButton
|
|
195
|
+
label={'Download Excel'}
|
|
196
|
+
size="large"
|
|
197
|
+
variant="outlined"
|
|
198
|
+
color="secondary"
|
|
199
|
+
onClick={downloadTest}
|
|
200
|
+
style={{ marginRight: '8px' }}
|
|
201
|
+
/>
|
|
202
|
+
<OpsnowCommonButton
|
|
203
|
+
label={'Download CSV'}
|
|
204
|
+
size="large"
|
|
205
|
+
variant="outlined"
|
|
206
|
+
color="secondary"
|
|
207
|
+
onClick={() => gridRef.current?.downloadCsv('test.csv')}
|
|
208
|
+
/>
|
|
209
|
+
</OpsnowCommonDataGrid>`
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
title: 'Header Filter, Tooltip, Sort',
|
|
213
|
+
description: 'Header Filter, Tooltip, Sort 예제입니다.',
|
|
214
|
+
code_props_usage: `
|
|
215
|
+
import { useCommonComponents, useGlobalContext } from '@opsnow-common/opsnow-finops-common-ui-loader'
|
|
216
|
+
import i18n from '@opsnow-common/opsnow-finops-common-i18n'
|
|
217
|
+
|
|
218
|
+
// 컬럼 정의
|
|
219
|
+
const columnDefs = [
|
|
220
|
+
{
|
|
221
|
+
headerName: 'Make',
|
|
222
|
+
field: 'make',
|
|
223
|
+
colId: 'make',
|
|
224
|
+
headerComponentParams: { tooltipInfoText: 'Percentage Share of Sales' },
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
headerName: 'Model',
|
|
228
|
+
field: 'model',
|
|
229
|
+
colId: 'model',
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
headerName: 'Price',
|
|
233
|
+
field: 'price',
|
|
234
|
+
colId: 'price',
|
|
235
|
+
headerComponentParams: { filter: true },
|
|
236
|
+
// sortable: false,
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
headerName: 'Test1',
|
|
240
|
+
field: 'test1',
|
|
241
|
+
colId: 'test1',
|
|
242
|
+
headerComponentParams: {
|
|
243
|
+
tooltipInfoText: 'test22',
|
|
244
|
+
tooltipField: 'title',
|
|
245
|
+
filter: true,
|
|
246
|
+
filterParams: { showTooltips: true },
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
]
|
|
250
|
+
|
|
251
|
+
// 데이터 정의
|
|
252
|
+
const initRowData = [
|
|
253
|
+
{
|
|
254
|
+
make: 'Toyota',
|
|
255
|
+
model: 'Celica',
|
|
256
|
+
price: 35000,
|
|
257
|
+
test1: 'abc',
|
|
258
|
+
test2: 'def'
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
make: 'Ford',
|
|
262
|
+
model: 'Mondeo',
|
|
263
|
+
price: 32000,
|
|
264
|
+
test1: 'abc',
|
|
265
|
+
test2: 'def'
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
make: 'Porsche',
|
|
269
|
+
model: 'Boxter',
|
|
270
|
+
price: 72000,
|
|
271
|
+
test1: 'abc',
|
|
272
|
+
test2: 'def'
|
|
273
|
+
},
|
|
274
|
+
]
|
|
275
|
+
|
|
276
|
+
const gridOptions = {
|
|
277
|
+
animateRows: true,
|
|
278
|
+
rowSelection: { mode: 'multiRow', enableSelectionWithoutKeys: true, headerCheckbox: true },
|
|
279
|
+
tooltipShowDelay: 100,
|
|
280
|
+
domLayout: 'autoHeight',
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const defaultColDef = {
|
|
284
|
+
wrapText: true,
|
|
285
|
+
autoHeight: true,
|
|
286
|
+
suppressMenu: true,
|
|
287
|
+
unSortIcon: true,
|
|
288
|
+
sortable: true,
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const { OpsnowCommonDataGrid, OpsnowCommonButton } = useCommonComponents();
|
|
292
|
+
const { CommonConst } = useGlobalContext()
|
|
293
|
+
// rowData와 status 상태를 관리
|
|
294
|
+
const [rowData, setRowData] = useState([]) // 초기에는 빈 배열로 설정
|
|
295
|
+
const [status, setStatus] = useState(CommonConst.GRID_STATUS.LOADING) // 초기에는 LOADING 상태
|
|
296
|
+
const gridRef = useRef(null)
|
|
297
|
+
|
|
298
|
+
// 데이터 지연 설정을 위한 useEffect
|
|
299
|
+
useEffect(() => {
|
|
300
|
+
// 1초 후에 데이터를 설정
|
|
301
|
+
const timeoutId = setTimeout(() => {
|
|
302
|
+
setRowData(initRowData)
|
|
303
|
+
setStatus(CommonConst.GRID_STATUS.DATA_EXISTS)
|
|
304
|
+
}, 1000)
|
|
305
|
+
|
|
306
|
+
// 컴포넌트 언마운트 시 타임아웃 클리어
|
|
307
|
+
return () => {
|
|
308
|
+
clearTimeout(timeoutId)
|
|
309
|
+
}
|
|
310
|
+
}, [])
|
|
311
|
+
|
|
312
|
+
const filterClickTest = (columnName, data) => {
|
|
313
|
+
if (gridRef.current) {
|
|
314
|
+
gridRef.current.updateFilter(columnName, data)
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
`,
|
|
318
|
+
code: `<OpsnowCommonDataGrid
|
|
319
|
+
ref={gridRef}
|
|
320
|
+
key={status}
|
|
321
|
+
columnDefs={columnDefs}
|
|
322
|
+
defaultColDef={defaultColDef}
|
|
323
|
+
rowData={rowData}
|
|
324
|
+
status={status} // 상태를 전달
|
|
325
|
+
gridOptions={gridOptions}
|
|
326
|
+
langCd={i18n.getLocale()}
|
|
327
|
+
>
|
|
328
|
+
<OpsnowCommonButton
|
|
329
|
+
label={'Filter click'}
|
|
330
|
+
size="large"
|
|
331
|
+
variant="outlined"
|
|
332
|
+
color="secondary"
|
|
333
|
+
onClick={() => filterClickTest('test2', ['def', 'def1'])}
|
|
334
|
+
style={{ marginRight: '8px' }}
|
|
335
|
+
/>
|
|
336
|
+
</OpsnowCommonDataGrid>`
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
title: 'Custom Header and Number Tooltip',
|
|
340
|
+
description: 'Custom Header and Number Tooltip 예제입니다.',
|
|
341
|
+
code_props_usage: `
|
|
342
|
+
import { useCommonComponents, useGlobalContext } from '@opsnow-common/opsnow-finops-common-ui-loader'
|
|
343
|
+
import i18n from '@opsnow-common/opsnow-finops-common-i18n'
|
|
344
|
+
import UserCustomHeaderCellRenderer from './renderer/UserCustomCellRenderer.jsx'
|
|
345
|
+
|
|
346
|
+
// 날짜 범위 정의
|
|
347
|
+
const dateRange = [
|
|
348
|
+
'2024-11-05',
|
|
349
|
+
'2024-11-06',
|
|
350
|
+
'2024-11-07'
|
|
351
|
+
]
|
|
352
|
+
|
|
353
|
+
// 데이터 정의
|
|
354
|
+
const initRowData = [
|
|
355
|
+
{
|
|
356
|
+
'displayItem': 'OpsNow - NOS (557892227155)',
|
|
357
|
+
'item': '557892227155',
|
|
358
|
+
'total': 31102.50521165911,
|
|
359
|
+
'share': '34.08',
|
|
360
|
+
'2024-11-05': 519.7175092481002,
|
|
361
|
+
'2024-11-06': 609.6942034389001,
|
|
362
|
+
'2024-11-07': 519.8121360272003
|
|
363
|
+
},
|
|
364
|
+
{
|
|
365
|
+
'displayItem': 'OpsNow - PRD (216093335544)',
|
|
366
|
+
'item': '216093335544',
|
|
367
|
+
'total': 30617.525577458808,
|
|
368
|
+
'share': '33.55',
|
|
369
|
+
'2024-11-05': 1211.0008080524017,
|
|
370
|
+
'2024-11-06': 1044.3593467060014,
|
|
371
|
+
'2024-11-07': 1019.0596313015008
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
'displayItem': '766276932394 (766276932394)',
|
|
375
|
+
'item': '766276932394',
|
|
376
|
+
'total': 4723.605532871798,
|
|
377
|
+
'share': '5.18',
|
|
378
|
+
'2024-11-05': 0,
|
|
379
|
+
'2024-11-06': 0,
|
|
380
|
+
'2024-11-07': 0
|
|
381
|
+
},
|
|
382
|
+
]
|
|
383
|
+
|
|
384
|
+
// 컬럼 정의
|
|
385
|
+
const columnDefs = [
|
|
386
|
+
{
|
|
387
|
+
headerName: 'Display Item',
|
|
388
|
+
field: 'displayItem',
|
|
389
|
+
pinned: 'left',
|
|
390
|
+
width: 300,
|
|
391
|
+
minWidth: 300,
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
headerName: 'Total Spend',
|
|
395
|
+
field: 'total',
|
|
396
|
+
pinned: 'left',
|
|
397
|
+
type: ['number', 'mixFormat'],
|
|
398
|
+
cellRendererParams: (params) => {
|
|
399
|
+
// any 대신 ICellRendererParams<RowData, number> 사용
|
|
400
|
+
// params.value가 number인지 확인하는 타입 가드
|
|
401
|
+
if (typeof params.value === 'number' && params.value > 4500) {
|
|
402
|
+
return {
|
|
403
|
+
typeParams: { isPercent: true },
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
return {
|
|
408
|
+
typeParams: { currency: 'USD' },
|
|
409
|
+
}
|
|
410
|
+
},
|
|
411
|
+
},
|
|
412
|
+
...dateRange.map((element) => ({
|
|
413
|
+
headerName: element,
|
|
414
|
+
field: element,
|
|
415
|
+
width: 150,
|
|
416
|
+
minWidth: 150,
|
|
417
|
+
type: ['number', 'cost', 'numberTooltip'],
|
|
418
|
+
cellRendererParams: {
|
|
419
|
+
typeParams: {
|
|
420
|
+
currency: 'USD',
|
|
421
|
+
iconRight: true,
|
|
422
|
+
},
|
|
423
|
+
},
|
|
424
|
+
headerComponent: UserCustomHeaderCellRenderer,
|
|
425
|
+
headerComponentParams: { dropdownList: dateRange, defaultValue: element },
|
|
426
|
+
})),
|
|
427
|
+
]
|
|
428
|
+
|
|
429
|
+
const gridOptions = {
|
|
430
|
+
animateRows: true,
|
|
431
|
+
rowSelection: { mode: 'multiRow', enableSelectionWithoutKeys: true },
|
|
432
|
+
tooltipShowDelay: 100,
|
|
433
|
+
domLayout: 'autoHeight',
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const defaultColDef = {
|
|
437
|
+
wrapText: true,
|
|
438
|
+
autoHeight: true,
|
|
439
|
+
suppressMenu: true,
|
|
440
|
+
unSortIcon: true,
|
|
441
|
+
sortable: true,
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
const { OpsnowCommonDataGrid } = useCommonComponents();
|
|
445
|
+
const { CommonConst } = useGlobalContext()
|
|
446
|
+
// rowData와 status 상태를 관리
|
|
447
|
+
const [rowData, setRowData] = useState([]) // 초기에는 빈 배열로 설정
|
|
448
|
+
const [status, setStatus] = useState(CommonConst.GRID_STATUS.LOADING) // 초기에는 LOADING 상태
|
|
449
|
+
const [pinnedTopRowData, setPinnedTopRowData] = useState(null)
|
|
450
|
+
const gridRef = useRef(null)
|
|
451
|
+
|
|
452
|
+
// 데이터 지연 설정을 위한 useEffect
|
|
453
|
+
useEffect(() => {
|
|
454
|
+
const timeoutId = setTimeout(() => {
|
|
455
|
+
// (1) totalCost를 직접 계산하거나 임의로 지정
|
|
456
|
+
const totalCost = 99999.99
|
|
457
|
+
|
|
458
|
+
// (2) totalItem 생성
|
|
459
|
+
const totalItem = {
|
|
460
|
+
displayItem: 'Total',
|
|
461
|
+
total: totalCost,
|
|
462
|
+
share: totalCost === 0 ? '0' : '100.00',
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// (3) 날짜별 합계 계산 → totalItem에 해당 날짜 키로 저장
|
|
466
|
+
dateRange.forEach((date) => {
|
|
467
|
+
totalItem[date] = initRowData.reduce((acc, obj) => {
|
|
468
|
+
return acc + (obj[date] || 0)
|
|
469
|
+
}, 0)
|
|
470
|
+
})
|
|
471
|
+
|
|
472
|
+
// (4) 기존 rowData 끝에 totalItem 추가
|
|
473
|
+
const newRowData = [...initRowData, totalItem]
|
|
474
|
+
|
|
475
|
+
// (5) pinned top row로 totalItem 설정
|
|
476
|
+
setPinnedTopRowData([totalItem])
|
|
477
|
+
|
|
478
|
+
// (6) setRowData 호출 → 실제 데이터 그리드 표에 표시될 rowData 갱신
|
|
479
|
+
setRowData(newRowData)
|
|
480
|
+
setStatus(CommonConst.GRID_STATUS.DATA_EXISTS)
|
|
481
|
+
}, 1000)
|
|
482
|
+
|
|
483
|
+
return () => {
|
|
484
|
+
clearTimeout(timeoutId)
|
|
485
|
+
}
|
|
486
|
+
}, [])
|
|
487
|
+
`,
|
|
488
|
+
code: `<OpsnowCommonDataGrid
|
|
489
|
+
ref={gridRef}
|
|
490
|
+
key={status}
|
|
491
|
+
columnDefs={columnDefs}
|
|
492
|
+
defaultColDef={defaultColDef}
|
|
493
|
+
rowData={rowData}
|
|
494
|
+
status={status} // 상태를 전달
|
|
495
|
+
gridOptions={gridOptions}
|
|
496
|
+
pagination={true}
|
|
497
|
+
pinnedTopRowData={pinnedTopRowData} // pinned row 전달
|
|
498
|
+
langCd={i18n.getLocale()}
|
|
499
|
+
/>`
|
|
500
|
+
},
|
|
501
|
+
{
|
|
502
|
+
title: 'Using a three-dots header with a custom header (including filter and sort)',
|
|
503
|
+
description: 'Using a three-dots header with a custom header (including filter and sort) 예제입니다.',
|
|
504
|
+
code_props_usage: `
|
|
505
|
+
import { useCommonComponents, useGlobalContext } from '@opsnow-common/opsnow-finops-common-ui-loader'
|
|
506
|
+
import i18n from '@opsnow-common/opsnow-finops-common-i18n'
|
|
507
|
+
import SingleHeaderTwoLineCustomHeader from './renderer/SingleHeaderTwoLineCustomHeader' // 커스텀 헤더 컴포넌트
|
|
508
|
+
|
|
509
|
+
// 기본 컬럼 정의
|
|
510
|
+
const defaultColDef = {
|
|
511
|
+
wrapText: true,
|
|
512
|
+
autoHeight: true,
|
|
513
|
+
suppressMenu: true, // 개별 컬럼에서 메뉴를 노출하려면 재설정할 예정
|
|
514
|
+
resizable: true,
|
|
515
|
+
sortable: false,
|
|
516
|
+
unSortIcon: true,
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// 컬럼 정의 (custom header 사용)
|
|
520
|
+
const columnDefs = [
|
|
521
|
+
{
|
|
522
|
+
headerName: 'Days (Values Not Provided)',
|
|
523
|
+
field: 'days',
|
|
524
|
+
headerComponent: SingleHeaderTwoLineCustomHeader,
|
|
525
|
+
headerComponentParams: {
|
|
526
|
+
displayData1: 'P1 (8/8-15)',
|
|
527
|
+
isUsedMenuFilter: false, // 필터 버튼은 노출하지 않음
|
|
528
|
+
},
|
|
529
|
+
sortable: true,
|
|
530
|
+
},
|
|
531
|
+
{
|
|
532
|
+
headerName: 'Numbers',
|
|
533
|
+
field: 'numbers',
|
|
534
|
+
// 필요한 경우 suppressHeaderMenuButton 등 추가 옵션 적용 가능
|
|
535
|
+
},
|
|
536
|
+
]
|
|
537
|
+
|
|
538
|
+
const gridOptions = {
|
|
539
|
+
animateRows: true,
|
|
540
|
+
suppressCellFocus: true,
|
|
541
|
+
suppressPaginationPanel: true,
|
|
542
|
+
rowSelection: { mode: 'singleRow', checkboxes: false, enableSelectionWithoutKeys: true },
|
|
543
|
+
domLayout: 'autoHeight',
|
|
544
|
+
suppressMenuHide: false, // 마우스 오버 없이 메뉴 버튼 항상 표시 (필요시 조정)
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
const { OpsnowCommonDataGrid } = useCommonComponents();
|
|
548
|
+
const { CommonConst } = useGlobalContext()
|
|
549
|
+
// rowData와 status 상태를 관리
|
|
550
|
+
const [rowData, setRowData] = useState([]) // 초기에는 빈 배열로 설정
|
|
551
|
+
const [status, setStatus] = useState(CommonConst.GRID_STATUS.LOADING) // 초기에는 LOADING 상태
|
|
552
|
+
const gridRef = useRef(null)
|
|
553
|
+
|
|
554
|
+
// 데이터 지연 설정을 위한 useEffect
|
|
555
|
+
useEffect(() => {
|
|
556
|
+
const timeoutId = setTimeout(() => {
|
|
557
|
+
setRowData(getRowData())
|
|
558
|
+
setStatus(CommonConst.GRID_STATUS.DATA_EXISTS)
|
|
559
|
+
}, 1000)
|
|
560
|
+
|
|
561
|
+
return () => {
|
|
562
|
+
clearTimeout(timeoutId)
|
|
563
|
+
}
|
|
564
|
+
}, [])
|
|
565
|
+
|
|
566
|
+
// 행 데이터 생성 함수
|
|
567
|
+
const getRowData = () => {
|
|
568
|
+
const weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday']
|
|
569
|
+
const rows = []
|
|
570
|
+
for (let i = 0; i < 200; i++) {
|
|
571
|
+
const index = Math.floor(Math.random() * weekdays.length)
|
|
572
|
+
rows.push({ days: weekdays[index], numbers: index })
|
|
573
|
+
}
|
|
574
|
+
return rows
|
|
575
|
+
}
|
|
576
|
+
`,
|
|
577
|
+
code: `<OpsnowCommonDataGrid
|
|
578
|
+
ref={gridRef}
|
|
579
|
+
key={status}
|
|
580
|
+
columnDefs={columnDefs}
|
|
581
|
+
defaultColDef={defaultColDef}
|
|
582
|
+
rowData={rowData}
|
|
583
|
+
status={status} // 상태 전달
|
|
584
|
+
gridOptions={gridOptions}
|
|
585
|
+
usedSearch={true}
|
|
586
|
+
usedMenuItem={true}
|
|
587
|
+
langCd={i18n.getLocale()}
|
|
588
|
+
/>`
|
|
589
|
+
},
|
|
590
|
+
{
|
|
591
|
+
title: 'Master / Detail Cell',
|
|
592
|
+
description: 'Master / Detail Cell 예제입니다.',
|
|
593
|
+
code_props_usage: `
|
|
594
|
+
import { useCommonComponents, useGlobalContext } from '@opsnow-common/opsnow-finops-common-ui-loader'
|
|
595
|
+
import i18n from '@opsnow-common/opsnow-finops-common-i18n'
|
|
596
|
+
import DetailCellRenderer from './renderer/DetailCellRenderer' // 커스텀 헤더 컴포넌트
|
|
597
|
+
|
|
598
|
+
// 컬럼 정의 (custom header 사용)
|
|
599
|
+
const columnDefs = [
|
|
600
|
+
{
|
|
601
|
+
field: 'make',
|
|
602
|
+
pinned: 'left',
|
|
603
|
+
lockPinned: true,
|
|
604
|
+
// AG Grid의 그룹 셀 렌더러 (마스터-디테일에서 그룹 펼치기/접기 사용)
|
|
605
|
+
cellRenderer: 'agGroupCellRenderer',
|
|
606
|
+
},
|
|
607
|
+
{ field: 'model' },
|
|
608
|
+
{ field: 'price' },
|
|
609
|
+
{ field: 'test1' },
|
|
610
|
+
{ field: 'test2' },
|
|
611
|
+
]
|
|
612
|
+
|
|
613
|
+
// 데이터 정의
|
|
614
|
+
const initRowData = [
|
|
615
|
+
{ make: 'Total', price: 3948475 },
|
|
616
|
+
{ make: 'Toyota', model: 'Celica', price: 35000, test1: 'test1', test2: 'test2' },
|
|
617
|
+
{ make: 'Ford', model: 'Mondeo', price: 32000, test1: 'test1', test2: 'test2' },
|
|
618
|
+
{ make: 'Porsche', model: 'Boxster', price: 72000, test1: 'test1', test2: 'test2' }
|
|
619
|
+
]
|
|
620
|
+
|
|
621
|
+
const gridOptions = {
|
|
622
|
+
animateRows: true,
|
|
623
|
+
//rowSelection: { mode: 'multiRow', checkboxes: false, headerCheckbox: false },
|
|
624
|
+
masterDetail: true,
|
|
625
|
+
domLayout: 'autoHeight',
|
|
626
|
+
detailCellRenderer: DetailCellRenderer,
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
const { OpsnowCommonDataGrid } = useCommonComponents();
|
|
630
|
+
const { CommonConst } = useGlobalContext()
|
|
631
|
+
// rowData와 status 상태를 관리
|
|
632
|
+
const [rowData, setRowData] = useState([]) // 초기에는 빈 배열로 설정
|
|
633
|
+
const [status, setStatus] = useState(CommonConst.GRID_STATUS.LOADING) // 초기에는 LOADING 상태
|
|
634
|
+
|
|
635
|
+
// 마운트 시점에 데이터 로딩 시뮬레이션
|
|
636
|
+
useEffect(() => {
|
|
637
|
+
const timer = setTimeout(() => {
|
|
638
|
+
setRowData(initRowData)
|
|
639
|
+
setStatus(CommonConst.GRID_STATUS.DATA_EXISTS)
|
|
640
|
+
}, 1000)
|
|
641
|
+
|
|
642
|
+
return () => clearTimeout(timer)
|
|
643
|
+
}, [])
|
|
644
|
+
`,
|
|
645
|
+
code: `<OpsnowCommonDataGrid
|
|
646
|
+
columnDefs={columnDefs}
|
|
647
|
+
rowData={rowData}
|
|
648
|
+
status={status}
|
|
649
|
+
gridOptions={gridOptions}
|
|
650
|
+
langCd={i18n.getLocale()}
|
|
651
|
+
/>`
|
|
652
|
+
},
|
|
653
|
+
{
|
|
654
|
+
title: 'No Data',
|
|
655
|
+
description: 'No Data 예제입니다.',
|
|
656
|
+
code_props_usage: `
|
|
657
|
+
import { useCommonComponents, useGlobalContext } from '@opsnow-common/opsnow-finops-common-ui-loader'
|
|
658
|
+
import i18n from '@opsnow-common/opsnow-finops-common-i18n'
|
|
659
|
+
|
|
660
|
+
// 컬럼 정의 (custom header 사용)
|
|
661
|
+
const columnDefs = [
|
|
662
|
+
{ headerName: 'Make', field: 'make' },
|
|
663
|
+
{ headerName: 'Model', field: 'model' },
|
|
664
|
+
{ headerName: 'Price', field: 'price' },
|
|
665
|
+
]
|
|
666
|
+
|
|
667
|
+
const defaultColDef = {
|
|
668
|
+
wrapText: true,
|
|
669
|
+
autoHeight: true,
|
|
670
|
+
suppressMenu: true,
|
|
671
|
+
unSortIcon: true,
|
|
672
|
+
sortable: false,
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
const gridOptions = {
|
|
676
|
+
animateRows: true,
|
|
677
|
+
domLayout: 'autoHeight',
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
const { OpsnowCommonDataGrid } = useCommonComponents();
|
|
681
|
+
const { CommonConst } = useGlobalContext()
|
|
682
|
+
`,
|
|
683
|
+
code: `<OpsnowCommonDataGrid
|
|
684
|
+
columnDefs={columnDefs}
|
|
685
|
+
rowData={[]}
|
|
686
|
+
gridOptions={gridOptions}
|
|
687
|
+
status={CommonConst.GRID_STATUS.DATA_EXISTS}
|
|
688
|
+
defaultColDef={defaultColDef}
|
|
689
|
+
langCd={i18n.getLocale()}
|
|
690
|
+
/>`
|
|
691
|
+
},
|
|
692
|
+
{
|
|
693
|
+
title: 'Custom No Data',
|
|
694
|
+
description: 'Custom No Data 예제입니다.',
|
|
695
|
+
code_props_usage: `
|
|
696
|
+
import { useCommonComponents, useGlobalContext } from '@opsnow-common/opsnow-finops-common-ui-loader'
|
|
697
|
+
import i18n from '@opsnow-common/opsnow-finops-common-i18n'
|
|
698
|
+
import CustomNoData from './renderer/CustomNoData'
|
|
699
|
+
|
|
700
|
+
// 컬럼 정의 (custom header 사용)
|
|
701
|
+
const columnDefs = [
|
|
702
|
+
{ headerName: 'Make', field: 'make' },
|
|
703
|
+
{ headerName: 'Model', field: 'model' },
|
|
704
|
+
{ headerName: 'Price', field: 'price' },
|
|
705
|
+
]
|
|
706
|
+
|
|
707
|
+
const defaultColDef = {
|
|
708
|
+
wrapText: true,
|
|
709
|
+
autoHeight: true,
|
|
710
|
+
suppressMenu: true,
|
|
711
|
+
unSortIcon: true,
|
|
712
|
+
sortable: false,
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
const gridOptions = {
|
|
716
|
+
animateRows: true,
|
|
717
|
+
domLayout: 'autoHeight',
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
const { OpsnowCommonDataGrid } = useCommonComponents();
|
|
721
|
+
const { CommonConst } = useGlobalContext()
|
|
722
|
+
`,
|
|
723
|
+
code: `<OpsnowCommonDataGrid
|
|
724
|
+
columnDefs={columnDefs}
|
|
725
|
+
rowData={[]}
|
|
726
|
+
gridOptions={gridOptions}
|
|
727
|
+
status={CommonConst.GRID_STATUS.DATA_EXISTS}
|
|
728
|
+
defaultColDef={defaultColDef}
|
|
729
|
+
noDataCustomComponent={CustomNoData}
|
|
730
|
+
langCd={i18n.getLocale()}
|
|
731
|
+
/>`
|
|
732
|
+
},
|
|
733
|
+
{
|
|
734
|
+
title: 'Pivot, Sidebar',
|
|
735
|
+
description: 'Pivot, Sidebar 예제입니다. 피벗이나 사이드바를 사용할 때는 높이가 항상 고정되어야 함(autoHeight는 허용되지 않음).',
|
|
736
|
+
code_props_usage: `
|
|
737
|
+
import { useCommonComponents, useGlobalContext } from '@opsnow-common/opsnow-finops-common-ui-loader'
|
|
738
|
+
import i18n from '@opsnow-common/opsnow-finops-common-i18n'
|
|
739
|
+
|
|
740
|
+
// defaultColDef
|
|
741
|
+
const defaultColDef = {
|
|
742
|
+
suppressMenu: true,
|
|
743
|
+
unSortIcon: false,
|
|
744
|
+
sortable: false,
|
|
745
|
+
flex: 1,
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// 컬럼 정의
|
|
749
|
+
const columnDefs = [
|
|
750
|
+
{
|
|
751
|
+
field: 'company_name',
|
|
752
|
+
rowGroup: true,
|
|
753
|
+
enableRowGroup: true,
|
|
754
|
+
enablePivot: true,
|
|
755
|
+
},
|
|
756
|
+
{
|
|
757
|
+
field: 'linked_account_id',
|
|
758
|
+
rowGroup: true,
|
|
759
|
+
pivot: true,
|
|
760
|
+
enableRowGroup: true,
|
|
761
|
+
enablePivot: true,
|
|
762
|
+
},
|
|
763
|
+
{
|
|
764
|
+
field: 'product_name',
|
|
765
|
+
rowGroup: true,
|
|
766
|
+
enableRowGroup: true,
|
|
767
|
+
enablePivot: true,
|
|
768
|
+
},
|
|
769
|
+
{
|
|
770
|
+
field: 'region_name',
|
|
771
|
+
rowGroup: true,
|
|
772
|
+
enableRowGroup: true,
|
|
773
|
+
enablePivot: true,
|
|
774
|
+
},
|
|
775
|
+
{
|
|
776
|
+
field: 'instance_family',
|
|
777
|
+
rowGroup: true,
|
|
778
|
+
enableRowGroup: true,
|
|
779
|
+
enablePivot: true,
|
|
780
|
+
},
|
|
781
|
+
{
|
|
782
|
+
field: 'on_demand_cost',
|
|
783
|
+
aggFunc: 'sum',
|
|
784
|
+
enableValue: true,
|
|
785
|
+
type: ['number', 'cost', 'numberTooltip'],
|
|
786
|
+
cellRendererParams: {
|
|
787
|
+
typeParams: {
|
|
788
|
+
currency: 'USD',
|
|
789
|
+
},
|
|
790
|
+
},
|
|
791
|
+
},
|
|
792
|
+
{
|
|
793
|
+
field: 'savings_plans_on_demand_cost_equivalent',
|
|
794
|
+
aggFunc: 'sum',
|
|
795
|
+
enableValue: true,
|
|
796
|
+
type: ['number', 'cost', 'numberTooltip'],
|
|
797
|
+
cellRendererParams: {
|
|
798
|
+
typeParams: {
|
|
799
|
+
currency: 'USD',
|
|
800
|
+
},
|
|
801
|
+
},
|
|
802
|
+
},
|
|
803
|
+
{
|
|
804
|
+
field: 'total_on_demand_cost_equivalent',
|
|
805
|
+
aggFunc: 'sum',
|
|
806
|
+
enableValue: true,
|
|
807
|
+
type: ['number', 'cost', 'numberTooltip'],
|
|
808
|
+
cellRendererParams: {
|
|
809
|
+
typeParams: {
|
|
810
|
+
currency: 'USD',
|
|
811
|
+
},
|
|
812
|
+
},
|
|
813
|
+
},
|
|
814
|
+
]
|
|
815
|
+
|
|
816
|
+
// 데이터 정의
|
|
817
|
+
const initRowData = [
|
|
818
|
+
{
|
|
819
|
+
"company_name": "smilegate",
|
|
820
|
+
"linked_account_id": "008969382493",
|
|
821
|
+
"product_name": "EC2",
|
|
822
|
+
"region_name": "Asia Pacific (Tokyo)",
|
|
823
|
+
"instance_family": "t2",
|
|
824
|
+
"on_demand_cost": 0,
|
|
825
|
+
"savings_plans_on_demand_cost_equivalent": 80.79280000000004,
|
|
826
|
+
"total_on_demand_cost_equivalent": 80.79280000000004
|
|
827
|
+
},
|
|
828
|
+
{
|
|
829
|
+
"company_name": "smilegate",
|
|
830
|
+
"linked_account_id": "008969382493",
|
|
831
|
+
"product_name": "EC2",
|
|
832
|
+
"region_name": "Asia Pacific (Tokyo)",
|
|
833
|
+
"instance_family": "t3",
|
|
834
|
+
"on_demand_cost": 0,
|
|
835
|
+
"savings_plans_on_demand_cost_equivalent": 120.71359999999994,
|
|
836
|
+
"total_on_demand_cost_equivalent": 120.71359999999994
|
|
837
|
+
},
|
|
838
|
+
{
|
|
839
|
+
"company_name": "smilegate",
|
|
840
|
+
"linked_account_id": "050451403493",
|
|
841
|
+
"product_name": "EC2",
|
|
842
|
+
"region_name": "Asia Pacific (Seoul)",
|
|
843
|
+
"instance_family": "t2",
|
|
844
|
+
"on_demand_cost": 0,
|
|
845
|
+
"savings_plans_on_demand_cost_equivalent": 10.655999999999997,
|
|
846
|
+
"total_on_demand_cost_equivalent": 10.655999999999997
|
|
847
|
+
}
|
|
848
|
+
]
|
|
849
|
+
|
|
850
|
+
const gridOptions = {
|
|
851
|
+
animateRows: true,
|
|
852
|
+
suppressCellFocus: true,
|
|
853
|
+
suppressPaginationPanel: true,
|
|
854
|
+
rowSelection: { mode: 'singleRow', enableClickSelection: true },
|
|
855
|
+
pivotMode: true, // pivot 기본 활성
|
|
856
|
+
autoGroupColumnDef: { minWidth: 300, sortable: false },
|
|
857
|
+
sideBar: 'columns', // 사이드바 활성
|
|
858
|
+
groupDefaultExpanded: -1,
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
const { OpsnowCommonDataGrid } = useCommonComponents();
|
|
862
|
+
const { CommonConst } = useGlobalContext()
|
|
863
|
+
// rowData와 status 상태를 관리
|
|
864
|
+
const [rowData, setRowData] = useState([]) // 초기에는 빈 배열로 설정
|
|
865
|
+
const [status, setStatus] = useState(CommonConst.GRID_STATUS.LOADING) // 초기에는 LOADING 상태
|
|
866
|
+
|
|
867
|
+
// 데이터 지연 설정을 위한 useEffect
|
|
868
|
+
useEffect(() => {
|
|
869
|
+
// 1초 후에 데이터를 설정
|
|
870
|
+
const timeoutId = setTimeout(() => {
|
|
871
|
+
setRowData(initRowData)
|
|
872
|
+
setStatus(CommonConst.GRID_STATUS.DATA_EXISTS)
|
|
873
|
+
}, 1000)
|
|
874
|
+
|
|
875
|
+
// 컴포넌트 언마운트 시 타임아웃 클리어
|
|
876
|
+
return () => {
|
|
877
|
+
clearTimeout(timeoutId)
|
|
878
|
+
}
|
|
879
|
+
}, [])
|
|
880
|
+
`,
|
|
881
|
+
code: `<OpsnowCommonDataGrid
|
|
882
|
+
rowData={currentDataList}
|
|
883
|
+
columnDefs={columnDefs}
|
|
884
|
+
defaultColDef={defaultColDef}
|
|
885
|
+
status={gridStatus}
|
|
886
|
+
gridOptions={gridOptions}
|
|
887
|
+
useOpsnowUs={true}
|
|
888
|
+
usedSearch={true}
|
|
889
|
+
gridHeight={800} // 높이 고정
|
|
890
|
+
langCd={i18n.getLocale()}
|
|
891
|
+
/>`
|
|
892
|
+
},
|
|
893
|
+
{
|
|
894
|
+
title: 'Ratio Bar',
|
|
895
|
+
description: 'Ratio Bar 예제입니다.',
|
|
896
|
+
code_props_usage: `
|
|
897
|
+
import { useCommonComponents, useGlobalContext } from '@opsnow-common/opsnow-finops-common-ui-loader'
|
|
898
|
+
import i18n from '@opsnow-common/opsnow-finops-common-i18n'
|
|
899
|
+
|
|
900
|
+
// 컬럼 정의
|
|
901
|
+
const columnDefs = [
|
|
902
|
+
{ headerName: 'Make', field: 'make', colId: 'make' },
|
|
903
|
+
{ headerName: 'Model', field: 'model', colId: 'model' },
|
|
904
|
+
{ headerName: 'Price', field: 'price', colId: 'price' },
|
|
905
|
+
{
|
|
906
|
+
headerName: 'Share',
|
|
907
|
+
field: 'shareRatio',
|
|
908
|
+
colId: 'shareRatio', // colId 추가
|
|
909
|
+
resizable: true,
|
|
910
|
+
cellStyle: {
|
|
911
|
+
display: 'flex',
|
|
912
|
+
justifyContent: 'center',
|
|
913
|
+
alignItems: 'center',
|
|
914
|
+
},
|
|
915
|
+
width: 200,
|
|
916
|
+
minWidth: 150,
|
|
917
|
+
maxWidth: 200,
|
|
918
|
+
headerComponentParams: { tooltipInfoText: 'Percentage Share of Sales' }, // tooltipInfoText 수정
|
|
919
|
+
type: ['ratioBar'],
|
|
920
|
+
cellRendererParams: {
|
|
921
|
+
typeParams: {
|
|
922
|
+
value: {
|
|
923
|
+
type: 'ratio',
|
|
924
|
+
},
|
|
925
|
+
label: {
|
|
926
|
+
postfix: '%',
|
|
927
|
+
location: 'right',
|
|
928
|
+
},
|
|
929
|
+
},
|
|
930
|
+
},
|
|
931
|
+
},
|
|
932
|
+
{
|
|
933
|
+
headerName: 'Cost Composition by Type',
|
|
934
|
+
field: 'totalCost',
|
|
935
|
+
colId: 'totalCost', // colId 추가
|
|
936
|
+
resizable: true,
|
|
937
|
+
cellStyle: {
|
|
938
|
+
display: 'flex',
|
|
939
|
+
justifyContent: 'center',
|
|
940
|
+
alignItems: 'center',
|
|
941
|
+
},
|
|
942
|
+
width: 230,
|
|
943
|
+
minWidth: 230,
|
|
944
|
+
maxWidth: 260,
|
|
945
|
+
type: ['ratioBar'],
|
|
946
|
+
cellRendererParams: {
|
|
947
|
+
typeParams: {
|
|
948
|
+
fields: ['riCost', 'spCost'],
|
|
949
|
+
value: {
|
|
950
|
+
type: 'value',
|
|
951
|
+
},
|
|
952
|
+
},
|
|
953
|
+
},
|
|
954
|
+
},
|
|
955
|
+
]
|
|
956
|
+
|
|
957
|
+
// 데이터 정의
|
|
958
|
+
const initRowData = [
|
|
959
|
+
{
|
|
960
|
+
make: 'Toyota',
|
|
961
|
+
model: 'Celica',
|
|
962
|
+
price: 35000,
|
|
963
|
+
spCost: '2.5',
|
|
964
|
+
riCost: 20.5,
|
|
965
|
+
totalCost: 50.0,
|
|
966
|
+
shareRatio: 40,
|
|
967
|
+
},
|
|
968
|
+
{
|
|
969
|
+
make: 'Ford',
|
|
970
|
+
model: 'Mondeo',
|
|
971
|
+
price: 32000,
|
|
972
|
+
spCost: '3.0',
|
|
973
|
+
riCost: 15.0,
|
|
974
|
+
totalCost: 45.0,
|
|
975
|
+
shareRatio: 35,
|
|
976
|
+
},
|
|
977
|
+
{
|
|
978
|
+
make: 'Porsche',
|
|
979
|
+
model: 'Boxter',
|
|
980
|
+
price: 72000,
|
|
981
|
+
spCost: '4.0',
|
|
982
|
+
riCost: 25.0,
|
|
983
|
+
totalCost: 70.0,
|
|
984
|
+
shareRatio: 50,
|
|
985
|
+
}
|
|
986
|
+
]
|
|
987
|
+
|
|
988
|
+
const gridOptions = {
|
|
989
|
+
animateRows: true,
|
|
990
|
+
rowSelection: { mode: 'multiRow' },
|
|
991
|
+
tooltipShowDelay: 100,
|
|
992
|
+
domLayout: 'autoHeight',
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
const defaultColDef = {
|
|
996
|
+
wrapText: true,
|
|
997
|
+
autoHeight: true,
|
|
998
|
+
suppressMenu: true,
|
|
999
|
+
unSortIcon: true,
|
|
1000
|
+
sortable: true,
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
const { OpsnowCommonDataGrid } = useCommonComponents();
|
|
1004
|
+
const { CommonConst } = useGlobalContext()
|
|
1005
|
+
// rowData와 status 상태를 관리
|
|
1006
|
+
const [rowData, setRowData] = useState([]) // 초기에는 빈 배열로 설정
|
|
1007
|
+
const [status, setStatus] = useState(CommonConst.GRID_STATUS.LOADING) // 초기에는 LOADING 상태
|
|
1008
|
+
const gridRef = useRef(null)
|
|
1009
|
+
|
|
1010
|
+
// 데이터 지연 설정을 위한 useEffect
|
|
1011
|
+
useEffect(() => {
|
|
1012
|
+
// 1초 후에 데이터를 설정
|
|
1013
|
+
const timeoutId = setTimeout(() => {
|
|
1014
|
+
setRowData(initRowData)
|
|
1015
|
+
setStatus(CommonConst.GRID_STATUS.DATA_EXISTS)
|
|
1016
|
+
}, 1000)
|
|
1017
|
+
|
|
1018
|
+
// 컴포넌트 언마운트 시 타임아웃 클리어
|
|
1019
|
+
return () => {
|
|
1020
|
+
clearTimeout(timeoutId)
|
|
1021
|
+
}
|
|
1022
|
+
}, [])
|
|
1023
|
+
`,
|
|
1024
|
+
code: `<OpsnowCommonDataGrid
|
|
1025
|
+
ref={gridRef}
|
|
1026
|
+
key={status}
|
|
1027
|
+
columnDefs={columnDefs}
|
|
1028
|
+
defaultColDef={defaultColDef}
|
|
1029
|
+
rowData={rowData}
|
|
1030
|
+
status={status} // 상태를 전달
|
|
1031
|
+
gridOptions={gridOptions}
|
|
1032
|
+
langCd={i18n.getLocale()}
|
|
1033
|
+
/>`
|
|
1034
|
+
},
|
|
1035
|
+
{
|
|
1036
|
+
title: 'Data Bar',
|
|
1037
|
+
description: 'Excel 스타일의 Data Bar 예제입니다. 셀 배경에 값에 비례하는 막대를 표시합니다. 월별 비용 데이터를 시각화하는 예제입니다.',
|
|
1038
|
+
code_props_usage: `
|
|
1039
|
+
import { useCommonComponents, useGlobalContext } from '@opsnow-common/opsnow-finops-common-ui-loader'
|
|
1040
|
+
import i18n from '@opsnow-common/opsnow-finops-common-i18n'
|
|
1041
|
+
|
|
1042
|
+
// 월별 비용 데이터
|
|
1043
|
+
const initRowData = [
|
|
1044
|
+
{ product: 'Amazon Elastic Compute Cloud', cost_202410: 11255, cost_202411: 18464, cost_202412: 17335, cost_202501: 18731, cost_202502: 20358, cost_202503: 27291 },
|
|
1045
|
+
{ product: 'Amazon CloudFront', cost_202410: 10055, cost_202411: 13741, cost_202412: 12527, cost_202501: 14702, cost_202502: 17044, cost_202503: 23615 },
|
|
1046
|
+
{ product: 'AWS Data Transfer', cost_202410: 13308, cost_202411: 18896, cost_202412: 16358, cost_202501: 16044, cost_202502: 15475, cost_202503: 21206 },
|
|
1047
|
+
{ product: 'Amazon Relational Database Service', cost_202410: 12057, cost_202411: 13592, cost_202412: 12883, cost_202501: 11609, cost_202502: 10681, cost_202503: 13413 },
|
|
1048
|
+
{ product: 'AmazonCloudWatch', cost_202410: 11107, cost_202411: 6883, cost_202412: 6623, cost_202501: 6391, cost_202502: 6295, cost_202503: 8128 },
|
|
1049
|
+
{ product: 'Amazon Managed Streaming for Apache Kafka', cost_202410: 5555, cost_202411: 5423, cost_202412: 5555, cost_202501: 6131, cost_202502: 6193, cost_202503: 7631 },
|
|
1050
|
+
{ product: 'Elastic Load Balancing', cost_202410: 2482, cost_202411: 4760, cost_202412: 3651, cost_202501: 4464, cost_202502: 4219, cost_202503: 6394 },
|
|
1051
|
+
{ product: 'AWS WAF', cost_202410: 3714, cost_202411: 4362, cost_202412: 3768, cost_202501: 3490, cost_202502: 3776, cost_202503: 4905 },
|
|
1052
|
+
]
|
|
1053
|
+
|
|
1054
|
+
// 월 필드 목록
|
|
1055
|
+
const monthFields = [
|
|
1056
|
+
'cost_202410', 'cost_202411', 'cost_202412',
|
|
1057
|
+
'cost_202501', 'cost_202502', 'cost_202503'
|
|
1058
|
+
]
|
|
1059
|
+
|
|
1060
|
+
// 최대값 계산 (바 스케일링용)
|
|
1061
|
+
const maxCost = Math.max(...initRowData.flatMap(row =>
|
|
1062
|
+
monthFields.map(field => row[field])
|
|
1063
|
+
))
|
|
1064
|
+
|
|
1065
|
+
// 합계 행 계산
|
|
1066
|
+
const totals = monthFields.reduce((acc, field) => {
|
|
1067
|
+
acc[field] = initRowData.reduce((sum, r) => sum + r[field], 0)
|
|
1068
|
+
return acc
|
|
1069
|
+
}, { product: 'Total' })
|
|
1070
|
+
|
|
1071
|
+
// 컬럼 정의
|
|
1072
|
+
const columnDefs = [
|
|
1073
|
+
{ headerName: 'Product', field: 'product', colId: 'product', width: 280, pinned: 'left' },
|
|
1074
|
+
...monthFields.map(field => {
|
|
1075
|
+
const year = field.slice(5, 9)
|
|
1076
|
+
const month = field.slice(9, 11)
|
|
1077
|
+
return {
|
|
1078
|
+
headerName: \`\${year}-\${month}\`,
|
|
1079
|
+
field: field,
|
|
1080
|
+
width: 120,
|
|
1081
|
+
type: ['dataBar'],
|
|
1082
|
+
cellRendererParams: {
|
|
1083
|
+
typeParams: {
|
|
1084
|
+
maxValue: maxCost, // 모든 컬럼에서 동일한 스케일 적용
|
|
1085
|
+
barColor: '#E91E8C', // 막대 색상
|
|
1086
|
+
barOpacity: 0.5, // 막대 투명도
|
|
1087
|
+
valuePrefix: '$', // 값 앞에 $ 표시
|
|
1088
|
+
},
|
|
1089
|
+
},
|
|
1090
|
+
}
|
|
1091
|
+
})
|
|
1092
|
+
]
|
|
1093
|
+
|
|
1094
|
+
const gridOptions = {
|
|
1095
|
+
animateRows: true,
|
|
1096
|
+
tooltipShowDelay: 100,
|
|
1097
|
+
domLayout: 'autoHeight',
|
|
1098
|
+
pinnedBottomRowData: [totals], // 합계 행을 하단에 고정
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
const defaultColDef = {
|
|
1102
|
+
suppressMenu: true,
|
|
1103
|
+
sortable: true,
|
|
1104
|
+
resizable: true,
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
const { OpsnowCommonDataGrid } = useCommonComponents();
|
|
1108
|
+
const { CommonConst } = useGlobalContext()
|
|
1109
|
+
// rowData와 status 상태를 관리
|
|
1110
|
+
const [rowData, setRowData] = useState([]) // 초기에는 빈 배열로 설정
|
|
1111
|
+
const [status, setStatus] = useState(CommonConst.GRID_STATUS.LOADING) // 초기에는 LOADING 상태
|
|
1112
|
+
const gridRef = useRef(null)
|
|
1113
|
+
|
|
1114
|
+
// 데이터 지연 설정을 위한 useEffect
|
|
1115
|
+
useEffect(() => {
|
|
1116
|
+
// 1초 후에 데이터를 설정
|
|
1117
|
+
const timeoutId = setTimeout(() => {
|
|
1118
|
+
setRowData(initRowData)
|
|
1119
|
+
setStatus(CommonConst.GRID_STATUS.DATA_EXISTS)
|
|
1120
|
+
}, 1000)
|
|
1121
|
+
|
|
1122
|
+
// 컴포넌트 언마운트 시 타임아웃 클리어
|
|
1123
|
+
return () => {
|
|
1124
|
+
clearTimeout(timeoutId)
|
|
1125
|
+
}
|
|
1126
|
+
}, [])
|
|
1127
|
+
`,
|
|
1128
|
+
code: `<OpsnowCommonDataGrid
|
|
1129
|
+
ref={gridRef}
|
|
1130
|
+
key={status}
|
|
1131
|
+
columnDefs={columnDefs}
|
|
1132
|
+
defaultColDef={defaultColDef}
|
|
1133
|
+
rowData={rowData}
|
|
1134
|
+
status={status} // 상태를 전달
|
|
1135
|
+
gridOptions={gridOptions}
|
|
1136
|
+
langCd={i18n.getLocale()}
|
|
1137
|
+
/>`
|
|
1138
|
+
},
|
|
1139
|
+
{
|
|
1140
|
+
title: 'How to insert into the slot to the left of the search',
|
|
1141
|
+
description: 'Search 왼쪽에 컴포넌트를 추가하는 방법입니다.',
|
|
1142
|
+
code_props_usage: `
|
|
1143
|
+
import { useCommonComponents, useGlobalContext } from '@opsnow-common/opsnow-finops-common-ui-loader'
|
|
1144
|
+
import i18n from '@opsnow-common/opsnow-finops-common-i18n'
|
|
1145
|
+
|
|
1146
|
+
const defaultColDef = {
|
|
1147
|
+
wrapText: true,
|
|
1148
|
+
autoHeight: true,
|
|
1149
|
+
suppressMenu: true,
|
|
1150
|
+
unSortIcon: true,
|
|
1151
|
+
resizable: true,
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
// 컬럼 정의
|
|
1155
|
+
const getColumnDefs = () => [
|
|
1156
|
+
{
|
|
1157
|
+
headerName: 'Period',
|
|
1158
|
+
field: 'period',
|
|
1159
|
+
},
|
|
1160
|
+
{
|
|
1161
|
+
headerName: 'Company',
|
|
1162
|
+
field: 'company',
|
|
1163
|
+
width: 150,
|
|
1164
|
+
minWidth: 150,
|
|
1165
|
+
},
|
|
1166
|
+
{
|
|
1167
|
+
headerName: 'Commit/h',
|
|
1168
|
+
field: 'commit',
|
|
1169
|
+
type: ['number', 'cost'],
|
|
1170
|
+
cellRendererParams: {
|
|
1171
|
+
typeParams: {
|
|
1172
|
+
currency: 'USD',
|
|
1173
|
+
digits: 0,
|
|
1174
|
+
},
|
|
1175
|
+
},
|
|
1176
|
+
},
|
|
1177
|
+
{
|
|
1178
|
+
headerName: 'Transfer Amount',
|
|
1179
|
+
field: 'amount',
|
|
1180
|
+
type: ['number', 'cost', 'numberTooltip'],
|
|
1181
|
+
cellRendererParams: {
|
|
1182
|
+
typeParams: {
|
|
1183
|
+
currency: 'USD',
|
|
1184
|
+
iconRight: true,
|
|
1185
|
+
},
|
|
1186
|
+
},
|
|
1187
|
+
},
|
|
1188
|
+
{
|
|
1189
|
+
headerName: 'P1->Est. Util',
|
|
1190
|
+
field: 'util',
|
|
1191
|
+
type: ['number', 'ratio'],
|
|
1192
|
+
},
|
|
1193
|
+
]
|
|
1194
|
+
|
|
1195
|
+
const gridOptions = {
|
|
1196
|
+
animateRows: true,
|
|
1197
|
+
suppressCellFocus: true,
|
|
1198
|
+
suppressPaginationPanel: true,
|
|
1199
|
+
rowSelection: { mode: 'multiRow' },
|
|
1200
|
+
tooltipShowDelay: 100,
|
|
1201
|
+
domLayout: 'autoHeight',
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
const { OpsnowCommonDataGrid } = useCommonComponents();
|
|
1205
|
+
const { CommonConst } = useGlobalContext()
|
|
1206
|
+
// rowData와 status 상태를 관리
|
|
1207
|
+
const [rowData, setRowData] = useState([]) // 초기에는 빈 배열로 설정
|
|
1208
|
+
const [status, setStatus] = useState(CommonConst.GRID_STATUS.LOADING) // 초기에는 LOADING 상태
|
|
1209
|
+
const [colDefs, setColDefs] = useState([])
|
|
1210
|
+
const [time, setTime] = useState(null) // 달력에서 선택된 날짜 (Dayjs 객체)
|
|
1211
|
+
const gridRef = useRef(null)
|
|
1212
|
+
|
|
1213
|
+
// 행 데이터 설정
|
|
1214
|
+
const getRowData = () => {
|
|
1215
|
+
setStatus(CommonConst.GRID_STATUS.LOADING)
|
|
1216
|
+
setTimeout(() => {
|
|
1217
|
+
const data = [
|
|
1218
|
+
{ period: '2024-08', company: '111111', commit: '5', amount: 34.048487477575, util: 93 },
|
|
1219
|
+
{ period: '2024-08', company: '222222', commit: '5', amount: 56.048487477575, util: 100 },
|
|
1220
|
+
{ period: '2024-08', company: '333333', commit: '5', amount: 234.048487477575, util: 57 },
|
|
1221
|
+
{ period: '2024-07', company: '444444', commit: '5', amount: 77.048487477575, util: 99 },
|
|
1222
|
+
{ period: '2024-07', company: '555555', commit: '5', amount: 88.048487477575, util: 89 },
|
|
1223
|
+
{ period: '2024-06', company: '666666', commit: '5', amount: 12.048487477575, util: 100 },
|
|
1224
|
+
]
|
|
1225
|
+
setRowData(data)
|
|
1226
|
+
setStatus(CommonConst.GRID_STATUS.DATA_EXISTS)
|
|
1227
|
+
}, 1000)
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
// 컴포넌트 마운트 시 초기화
|
|
1231
|
+
useEffect(() => {
|
|
1232
|
+
setColDefs(getColumnDefs())
|
|
1233
|
+
getRowData()
|
|
1234
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1235
|
+
}, [])
|
|
1236
|
+
|
|
1237
|
+
// gridStatus가 DATA_EXISTS일 때만 rowData1을 전달
|
|
1238
|
+
const emptyComputedGridData =
|
|
1239
|
+
status === CommonConst.GRID_STATUS.DATA_EXISTS && rowData ? rowData : []
|
|
1240
|
+
|
|
1241
|
+
// 달력에서 날짜 변경 시
|
|
1242
|
+
const onChangeMonth = (newValue) => {
|
|
1243
|
+
const selectedMonth = newValue ? newValue.format('YYYY-MM') : ''
|
|
1244
|
+
let filteredData = rowData
|
|
1245
|
+
|
|
1246
|
+
if (selectedMonth) {
|
|
1247
|
+
filteredData = rowData.filter(row => row.period === selectedMonth)
|
|
1248
|
+
}
|
|
1249
|
+
|
|
1250
|
+
if (gridRef.current) {
|
|
1251
|
+
// 필터링된 데이터 설정
|
|
1252
|
+
gridRef.current.setRowData(filteredData)
|
|
1253
|
+
// 약간의 지연 후에 컬럼 사이즈 맞추기
|
|
1254
|
+
setTimeout(() => {
|
|
1255
|
+
gridRef.current.sizeColumnsToFit()
|
|
1256
|
+
}, 0)
|
|
1257
|
+
}
|
|
1258
|
+
setTime(newValue)
|
|
1259
|
+
}
|
|
1260
|
+
`,
|
|
1261
|
+
code: `<OpsnowCommonDataGrid
|
|
1262
|
+
ref={gridRef}
|
|
1263
|
+
rowData={emptyComputedGridData}
|
|
1264
|
+
columnDefs={colDefs}
|
|
1265
|
+
defaultColDef={defaultColDef}
|
|
1266
|
+
status={status}
|
|
1267
|
+
gridOptions={gridOptions}
|
|
1268
|
+
useOpsnowUs={true}
|
|
1269
|
+
usedSearch={true}
|
|
1270
|
+
langCd={i18n.getLocale()}
|
|
1271
|
+
// 달력 컴포넌트를 searchAddon에 전달
|
|
1272
|
+
searchAddon={
|
|
1273
|
+
<OpsnowCommonDatePicker
|
|
1274
|
+
clearable={true}
|
|
1275
|
+
placeholderText="날짜를 선택해주세요"
|
|
1276
|
+
type="date"
|
|
1277
|
+
value={time}
|
|
1278
|
+
onChange={onChangeMonth}
|
|
1279
|
+
/>
|
|
1280
|
+
}
|
|
1281
|
+
/>`
|
|
1282
|
+
},
|
|
1283
|
+
{
|
|
1284
|
+
title: 'Tree data drag',
|
|
1285
|
+
description: 'Tree data drag 예제입니다.',
|
|
1286
|
+
code_props_usage: `
|
|
1287
|
+
import { useCommonComponents, useGlobalContext } from '@opsnow-common/opsnow-finops-common-ui-loader'
|
|
1288
|
+
import i18n from '@opsnow-common/opsnow-finops-common-i18n'
|
|
1289
|
+
import AddButtonRenderer from './renderer/AddButtonRenderer.jsx'
|
|
1290
|
+
|
|
1291
|
+
const { OpsnowCommonDataGrid } = useCommonComponents();
|
|
1292
|
+
const { CommonConst } = useGlobalContext()
|
|
1293
|
+
const [treeRowData, setTreeRowData] = useState([])
|
|
1294
|
+
const [treeColumnDefs, setTreeColumnDefs] = useState([])
|
|
1295
|
+
const [gridStatus, setGridStatus] = useState(CommonConst.GRID_STATUS.LOADING)
|
|
1296
|
+
const [draggedNode, setDraggedNode] = useState(null) // onRowDragEnter에서 저장할 노드
|
|
1297
|
+
|
|
1298
|
+
// ▶ 변경: tempGroupCount를 useState 대신 useRef로 관리
|
|
1299
|
+
// useState: 상태가 변하면 UI를 다시 그려야 하는 데이터를 관리 (→ 변경 시 컴포넌트 재렌더링).
|
|
1300
|
+
// useRef: 렌더링과 직접 관련 없는 값을 관리하거나, DOM 요소를 직접 참조할 때 사용 (→ 값 변경해도 재렌더링 없음).
|
|
1301
|
+
const tempGroupCountRef = useRef(1)
|
|
1302
|
+
|
|
1303
|
+
// OpsnowCommonDataGrid ref
|
|
1304
|
+
const treeDataGridRef = useRef(null)
|
|
1305
|
+
|
|
1306
|
+
// 기본 컬럼 설정
|
|
1307
|
+
const defaultColDef = {
|
|
1308
|
+
wrapText: true,
|
|
1309
|
+
autoHeight: true,
|
|
1310
|
+
suppressMenu: true,
|
|
1311
|
+
unSortIcon: true,
|
|
1312
|
+
sortable: false,
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
// onAddRowClick: pinned bottom row에 있는 Add Group 버튼 클릭 시
|
|
1316
|
+
const onAddRowClick = useCallback(() => {
|
|
1317
|
+
// 현재 count를 ref에서 읽어옴
|
|
1318
|
+
const currentCount = tempGroupCountRef.current
|
|
1319
|
+
|
|
1320
|
+
const newRow = [
|
|
1321
|
+
{
|
|
1322
|
+
id: Date.now(),
|
|
1323
|
+
filePath: ['FinOps Practitioner', \`New Group\${currentCount}\`],
|
|
1324
|
+
type: 'group',
|
|
1325
|
+
},
|
|
1326
|
+
]
|
|
1327
|
+
if (treeDataGridRef.current) {
|
|
1328
|
+
// OpsnowCommonDataGrid의 applyTransaction API
|
|
1329
|
+
treeDataGridRef.current.applyTransaction({ add: newRow })
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
// useRef로 관리하므로, React 재렌더링 없이 값만 업데이트
|
|
1333
|
+
tempGroupCountRef.current = currentCount + 1
|
|
1334
|
+
}, [])
|
|
1335
|
+
|
|
1336
|
+
// sizeFormatter: 'MB' 접미사 추가
|
|
1337
|
+
const sizeFormatter = (params) => {
|
|
1338
|
+
return params.value ? \`\${params.value} MB\` : ''
|
|
1339
|
+
}
|
|
1340
|
+
|
|
1341
|
+
// Vue의 onRowDragEnter => React로 변환
|
|
1342
|
+
const onRowDragEnter = useCallback((event) => {
|
|
1343
|
+
// event.node에 드래그한 노드 정보가 담겨 있음
|
|
1344
|
+
setDraggedNode(event.node)
|
|
1345
|
+
}, [])
|
|
1346
|
+
|
|
1347
|
+
// myCustomDropTargetFunc: 특정 조건(type='group')만 drop 가능
|
|
1348
|
+
const myCustomDropTargetFunc = useCallback(
|
|
1349
|
+
(overNode) => {
|
|
1350
|
+
if (!overNode) return null
|
|
1351
|
+
|
|
1352
|
+
let newPotentialParent =
|
|
1353
|
+
overNode.data && overNode.data.type === 'group'
|
|
1354
|
+
? overNode
|
|
1355
|
+
: overNode.parent
|
|
1356
|
+
|
|
1357
|
+
// draggedNode가 group이면서 자식이 있으면, 이동 불가 → 부모로 되돌림
|
|
1358
|
+
if (
|
|
1359
|
+
draggedNode &&
|
|
1360
|
+
draggedNode.data &&
|
|
1361
|
+
draggedNode.data.type === 'group' &&
|
|
1362
|
+
draggedNode.childrenAfterGroup &&
|
|
1363
|
+
draggedNode.childrenAfterGroup.length > 0
|
|
1364
|
+
) {
|
|
1365
|
+
newPotentialParent = draggedNode.parent || null
|
|
1366
|
+
}
|
|
1367
|
+
return newPotentialParent
|
|
1368
|
+
},
|
|
1369
|
+
[draggedNode],
|
|
1370
|
+
)
|
|
1371
|
+
|
|
1372
|
+
// gridOptions
|
|
1373
|
+
const treeGridOptions = {
|
|
1374
|
+
animateRows: true,
|
|
1375
|
+
treeData: true,
|
|
1376
|
+
suppressCellFocus: true,
|
|
1377
|
+
autoGroupColumnDef: {
|
|
1378
|
+
// 이거 설정 안해줘도 :useTreeDataDragDrop="true" 넘기면 자동 세팅
|
|
1379
|
+
//rowDrag: true,
|
|
1380
|
+
headerName: 'Files',
|
|
1381
|
+
minWidth: 300,
|
|
1382
|
+
unSortIcon: false,
|
|
1383
|
+
sortable: false,
|
|
1384
|
+
cellRendererParams: {
|
|
1385
|
+
suppressCount: true,
|
|
1386
|
+
},
|
|
1387
|
+
},
|
|
1388
|
+
groupDefaultExpanded: -1,
|
|
1389
|
+
// row data에서 array로 들어오는 데이터를 path로 설정
|
|
1390
|
+
getDataPath: (data) => data.filePath,
|
|
1391
|
+
// dataPath가 data 에서도 키가 dataPath가 아닌이상
|
|
1392
|
+
// dataPath 를 어디로 지정했는지 common-grid에 알려주어야함. (common-grid에서는 알수가없음)
|
|
1393
|
+
// 즉, 키가 dataPath가 아닌 이상 필수
|
|
1394
|
+
// ex) dataPath: ['FinOps Practitioner', 'Group 01', 'Sub Group 01-01'], -> setDataPath 필요없음
|
|
1395
|
+
// filePath: ['FinOps Practitioner', 'Group 01', 'Sub Group 01-01'], -> setDataPath 해줘야함
|
|
1396
|
+
setDataPath: (data, newPath) => {
|
|
1397
|
+
data.filePath = newPath
|
|
1398
|
+
},
|
|
1399
|
+
pinnedBottomRowData: [{ isAddButtonRow: true }],
|
|
1400
|
+
// onRowDragEnter 이벤트 등록
|
|
1401
|
+
onRowDragEnter: onRowDragEnter,
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
// 컬럼 정의
|
|
1405
|
+
const getTreeColumnDefs = useCallback(() => {
|
|
1406
|
+
return [
|
|
1407
|
+
{
|
|
1408
|
+
field: 'dateModified',
|
|
1409
|
+
suppressHeaderMenuButton: true,
|
|
1410
|
+
// pinned row인 경우 전체 컬럼 수만큼 병합
|
|
1411
|
+
colSpan: (params) => {
|
|
1412
|
+
if (params.node.rowPinned) {
|
|
1413
|
+
// API가 없거나 이미 파괴된 경우엔 안전하게 1 반환
|
|
1414
|
+
if (!params.api || (params.api.isDestroyed && params.api.isDestroyed())) {
|
|
1415
|
+
return 1
|
|
1416
|
+
}
|
|
1417
|
+
const allDisplayedColumns = params.api.getAllDisplayedColumns()
|
|
1418
|
+
return allDisplayedColumns && allDisplayedColumns.length
|
|
1419
|
+
? allDisplayedColumns.length
|
|
1420
|
+
: 1
|
|
1421
|
+
}
|
|
1422
|
+
return 1
|
|
1423
|
+
},
|
|
1424
|
+
// pinned row인 경우 AddButtonRenderer 사용
|
|
1425
|
+
cellRendererSelector: (params) => {
|
|
1426
|
+
if (params.node.rowPinned) {
|
|
1427
|
+
return {
|
|
1428
|
+
component: AddButtonRenderer,
|
|
1429
|
+
params: {
|
|
1430
|
+
onClickAddGroup: () => {
|
|
1431
|
+
onAddRowClick()
|
|
1432
|
+
},
|
|
1433
|
+
},
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
return undefined
|
|
1437
|
+
},
|
|
1438
|
+
},
|
|
1439
|
+
{
|
|
1440
|
+
field: 'size',
|
|
1441
|
+
valueFormatter: sizeFormatter,
|
|
1442
|
+
suppressHeaderMenuButton: true,
|
|
1443
|
+
},
|
|
1444
|
+
]
|
|
1445
|
+
}, [onAddRowClick])
|
|
1446
|
+
|
|
1447
|
+
// 행 데이터 로딩
|
|
1448
|
+
const getTreeRowData = useCallback(() => {
|
|
1449
|
+
setGridStatus(CommonConst.GRID_STATUS.LOADING)
|
|
1450
|
+
setTimeout(() => {
|
|
1451
|
+
// ** 주의 **
|
|
1452
|
+
// path 구성시 상위 그룹을 반드시 생성후 그 아래 자식 그룹 생성해주세요
|
|
1453
|
+
// ex) 1단계 : filePath: ['FinOps Practitioner'] 생성
|
|
1454
|
+
// 2단계 : filePath: ['FinOps Practitioner', 'Group 01'] 생성
|
|
1455
|
+
// 1단계 없이 바로 2단계로 할 경우 이동이 올바르게 실행되지 않음.
|
|
1456
|
+
const rowData = [
|
|
1457
|
+
{ id: 1, filePath: ['FinOps Practitioner'], type: 'parent' },
|
|
1458
|
+
{ id: 2, filePath: ['FinOps Practitioner', 'Group 01'], type: 'group' },
|
|
1459
|
+
{
|
|
1460
|
+
id: 3,
|
|
1461
|
+
filePath: ['FinOps Practitioner', 'Group 01', 'Sub Group 01-01'],
|
|
1462
|
+
type: 'sub-group',
|
|
1463
|
+
dateModified: 'May 21 2017 01:50:00 PM',
|
|
1464
|
+
size: 14.7,
|
|
1465
|
+
},
|
|
1466
|
+
]
|
|
1467
|
+
setTreeRowData(rowData)
|
|
1468
|
+
setGridStatus(CommonConst.GRID_STATUS.DATA_EXISTS)
|
|
1469
|
+
}, 1000)
|
|
1470
|
+
}, [])
|
|
1471
|
+
|
|
1472
|
+
// 컴포넌트 마운트 시 컬럼/데이터 초기화
|
|
1473
|
+
useEffect(() => {
|
|
1474
|
+
setTreeColumnDefs(getTreeColumnDefs())
|
|
1475
|
+
getTreeRowData()
|
|
1476
|
+
}, [getTreeColumnDefs, getTreeRowData])
|
|
1477
|
+
`,
|
|
1478
|
+
code: `<OpsnowCommonDataGrid
|
|
1479
|
+
ref={treeDataGridRef}
|
|
1480
|
+
rowData={treeRowData}
|
|
1481
|
+
columnDefs={treeColumnDefs}
|
|
1482
|
+
defaultColDef={defaultColDef}
|
|
1483
|
+
status={gridStatus}
|
|
1484
|
+
gridOptions={treeGridOptions}
|
|
1485
|
+
pagination={true}
|
|
1486
|
+
useOpsnowUs={true}
|
|
1487
|
+
usedSearch={true}
|
|
1488
|
+
usedMenuItem={true}
|
|
1489
|
+
// 트리 데이터 드래그 기능
|
|
1490
|
+
useTreeDataDragDrop={true}
|
|
1491
|
+
// 특정 drop target 로직
|
|
1492
|
+
getDropTargetNode={myCustomDropTargetFunc}
|
|
1493
|
+
langCd={i18n.getLocale()}
|
|
1494
|
+
/>`
|
|
1495
|
+
},
|
|
1496
|
+
{
|
|
1497
|
+
title: 'Grid row drag (transferring rows between two grids)',
|
|
1498
|
+
description: 'Grid row drag 예제입니다.',
|
|
1499
|
+
code_props_usage: `
|
|
1500
|
+
import { useCommonComponents, useGlobalContext } from '@opsnow-common/opsnow-finops-common-ui-loader'
|
|
1501
|
+
import i18n from '@opsnow-common/opsnow-finops-common-i18n'
|
|
1502
|
+
|
|
1503
|
+
const { OpsnowCommonDataGrid } = useCommonComponents()
|
|
1504
|
+
const { CommonConst } = useGlobalContext()
|
|
1505
|
+
// 상태 정의
|
|
1506
|
+
const [rowData1, setRowData1] = useState([])
|
|
1507
|
+
const [rowData2, setRowData2] = useState([])
|
|
1508
|
+
const [colDefs1, setColDefs1] = useState([])
|
|
1509
|
+
const [colDefs2, setColDefs2] = useState([])
|
|
1510
|
+
const defaultColDef = {
|
|
1511
|
+
wrapText: true,
|
|
1512
|
+
autoHeight: true,
|
|
1513
|
+
suppressMenu: true,
|
|
1514
|
+
unSortIcon: true,
|
|
1515
|
+
resizable: true,
|
|
1516
|
+
}
|
|
1517
|
+
const gridOptions1 = {
|
|
1518
|
+
animateRows: true,
|
|
1519
|
+
suppressCellFocus: true,
|
|
1520
|
+
suppressPaginationPanel: true,
|
|
1521
|
+
rowSelection: { mode: 'multiRow', checkboxes: true, enableClickSelection: true, headerCheckbox: true },
|
|
1522
|
+
tooltipShowDelay: 100,
|
|
1523
|
+
domLayout: 'autoHeight',
|
|
1524
|
+
rowDragMultiRow: true,
|
|
1525
|
+
rowDragManaged: true,
|
|
1526
|
+
suppressMoveWhenRowDragging: true,
|
|
1527
|
+
}
|
|
1528
|
+
const gridOptions2 = {
|
|
1529
|
+
animateRows: true,
|
|
1530
|
+
suppressCellFocus: true,
|
|
1531
|
+
suppressPaginationPanel: true,
|
|
1532
|
+
rowSelection: { mode: 'multiRow', checkboxes: true, enableClickSelection: true, headerCheckbox: true },
|
|
1533
|
+
tooltipShowDelay: 100,
|
|
1534
|
+
domLayout: 'autoHeight',
|
|
1535
|
+
rowDragMultiRow: true,
|
|
1536
|
+
rowDragManaged: true,
|
|
1537
|
+
suppressMoveWhenRowDragging: true,
|
|
1538
|
+
}
|
|
1539
|
+
const [gridStatus1, setGridStatus1] = useState(CommonConst.GRID_STATUS.LOADING)
|
|
1540
|
+
const [gridStatus2, setGridStatus2] = useState(CommonConst.GRID_STATUS.LOADING)
|
|
1541
|
+
const [leftGridReady, setLeftGridReady] = useState(false)
|
|
1542
|
+
const [rightGridReady, setRightGridReady] = useState(false)
|
|
1543
|
+
|
|
1544
|
+
// OpsnowCommonDataGrid ref
|
|
1545
|
+
const leftGridRef = useRef(null)
|
|
1546
|
+
const rightGridRef = useRef(null)
|
|
1547
|
+
|
|
1548
|
+
|
|
1549
|
+
// 드래그 드랍 관련 함수
|
|
1550
|
+
const addGridDropZone = useCallback(() => {
|
|
1551
|
+
if (rightGridRef.current && leftGridRef.current) {
|
|
1552
|
+
createDropZone(rightGridRef.current, leftGridRef.current)
|
|
1553
|
+
createDropZone(leftGridRef.current, rightGridRef.current)
|
|
1554
|
+
}
|
|
1555
|
+
}, [])
|
|
1556
|
+
|
|
1557
|
+
const createDropZone = (sourceGrid, targetGrid) => {
|
|
1558
|
+
const dropZoneParams = sourceGrid.getRowDropZoneParams({
|
|
1559
|
+
onDragStop: (params) => {
|
|
1560
|
+
const nodes = params.nodes
|
|
1561
|
+
targetGrid.applyTransaction({
|
|
1562
|
+
remove: nodes.map(node => node.data),
|
|
1563
|
+
})
|
|
1564
|
+
},
|
|
1565
|
+
})
|
|
1566
|
+
targetGrid.addRowDropZone(dropZoneParams)
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
// 데이터 그리드 준비 콜백
|
|
1570
|
+
useEffect(() => {
|
|
1571
|
+
if (leftGridReady && rightGridReady) {
|
|
1572
|
+
addGridDropZone()
|
|
1573
|
+
}
|
|
1574
|
+
}, [leftGridReady, rightGridReady, addGridDropZone])
|
|
1575
|
+
|
|
1576
|
+
const onGridReady = useCallback((gridPosition) => {
|
|
1577
|
+
if (gridPosition === 'left') {
|
|
1578
|
+
setLeftGridReady(true)
|
|
1579
|
+
} else if (gridPosition === 'right') {
|
|
1580
|
+
setRightGridReady(true)
|
|
1581
|
+
}
|
|
1582
|
+
}, [])
|
|
1583
|
+
|
|
1584
|
+
// 컬럼 정의 함수
|
|
1585
|
+
const getColumnDefs1 = () => {
|
|
1586
|
+
return [
|
|
1587
|
+
{
|
|
1588
|
+
rowDrag: true,
|
|
1589
|
+
maxWidth: 50,
|
|
1590
|
+
minWidth: 50,
|
|
1591
|
+
pinned: 'left',
|
|
1592
|
+
sortable: false,
|
|
1593
|
+
suppressHeaderMenuButton: true,
|
|
1594
|
+
suppressHeaderFilterButton: true,
|
|
1595
|
+
rowDragText: (params, dragItemCount) => {
|
|
1596
|
+
if (dragItemCount > 1) {
|
|
1597
|
+
return dragItemCount + ' Company'
|
|
1598
|
+
}
|
|
1599
|
+
return params.rowNode.data.company
|
|
1600
|
+
},
|
|
1601
|
+
},
|
|
1602
|
+
{
|
|
1603
|
+
headerName: 'Company',
|
|
1604
|
+
field: 'company',
|
|
1605
|
+
pinned: 'left',
|
|
1606
|
+
width: 150,
|
|
1607
|
+
minWidth: 150,
|
|
1608
|
+
suppressHeaderMenuButton: true,
|
|
1609
|
+
suppressHeaderFilterButton: true,
|
|
1610
|
+
},
|
|
1611
|
+
{
|
|
1612
|
+
headerName: 'Commit/h',
|
|
1613
|
+
field: 'commit',
|
|
1614
|
+
type: ['number', 'cost'],
|
|
1615
|
+
cellRendererParams: {
|
|
1616
|
+
typeParams: {
|
|
1617
|
+
currency: 'USD',
|
|
1618
|
+
digits: 0,
|
|
1619
|
+
},
|
|
1620
|
+
},
|
|
1621
|
+
},
|
|
1622
|
+
{
|
|
1623
|
+
headerName: 'Transfer Amount',
|
|
1624
|
+
field: 'amount',
|
|
1625
|
+
type: ['number', 'cost', 'numberTooltip'],
|
|
1626
|
+
cellRendererParams: {
|
|
1627
|
+
typeParams: {
|
|
1628
|
+
currency: 'USD',
|
|
1629
|
+
iconRight: true,
|
|
1630
|
+
},
|
|
1631
|
+
},
|
|
1632
|
+
},
|
|
1633
|
+
{
|
|
1634
|
+
headerName: 'P1->Est. Util',
|
|
1635
|
+
field: 'util',
|
|
1636
|
+
type: ['number', 'ratio'],
|
|
1637
|
+
},
|
|
1638
|
+
]
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
const getColumnDefs2 = () => {
|
|
1642
|
+
return [
|
|
1643
|
+
{
|
|
1644
|
+
rowDrag: true,
|
|
1645
|
+
maxWidth: 50,
|
|
1646
|
+
minWidth: 50,
|
|
1647
|
+
pinned: 'left',
|
|
1648
|
+
sortable: false,
|
|
1649
|
+
suppressHeaderMenuButton: true,
|
|
1650
|
+
suppressHeaderFilterButton: true,
|
|
1651
|
+
rowDragText: (params, dragItemCount) => {
|
|
1652
|
+
if (dragItemCount > 1) {
|
|
1653
|
+
return dragItemCount + ' Company'
|
|
1654
|
+
}
|
|
1655
|
+
return params.rowNode.data.company
|
|
1656
|
+
},
|
|
1657
|
+
},
|
|
1658
|
+
{
|
|
1659
|
+
headerName: 'Company',
|
|
1660
|
+
field: 'company',
|
|
1661
|
+
pinned: 'left',
|
|
1662
|
+
width: 150,
|
|
1663
|
+
minWidth: 150,
|
|
1664
|
+
suppressHeaderMenuButton: true,
|
|
1665
|
+
suppressHeaderFilterButton: true,
|
|
1666
|
+
},
|
|
1667
|
+
{
|
|
1668
|
+
headerName: 'Commit/h',
|
|
1669
|
+
field: 'commit',
|
|
1670
|
+
type: ['number', 'cost'],
|
|
1671
|
+
cellRendererParams: {
|
|
1672
|
+
typeParams: {
|
|
1673
|
+
currency: 'USD',
|
|
1674
|
+
digits: 0,
|
|
1675
|
+
},
|
|
1676
|
+
},
|
|
1677
|
+
},
|
|
1678
|
+
{
|
|
1679
|
+
headerName: 'Transfer Amount',
|
|
1680
|
+
field: 'amount',
|
|
1681
|
+
type: ['number', 'cost', 'numberTooltip'],
|
|
1682
|
+
cellRendererParams: {
|
|
1683
|
+
typeParams: {
|
|
1684
|
+
currency: 'USD',
|
|
1685
|
+
iconRight: true,
|
|
1686
|
+
},
|
|
1687
|
+
},
|
|
1688
|
+
},
|
|
1689
|
+
{
|
|
1690
|
+
headerName: 'P1->Est. Cov',
|
|
1691
|
+
field: 'util',
|
|
1692
|
+
type: ['number', 'ratio'],
|
|
1693
|
+
},
|
|
1694
|
+
]
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1697
|
+
// 행 데이터 함수 (1, 2번 데이터 그리드 각각)
|
|
1698
|
+
const getRowData1 = () => {
|
|
1699
|
+
setGridStatus1(CommonConst.GRID_STATUS.LOADING)
|
|
1700
|
+
setTimeout(() => {
|
|
1701
|
+
setRowData1([
|
|
1702
|
+
{ company: '111111', commit: '5', amount: 34.048487477575, util: 93 },
|
|
1703
|
+
{ company: '222222', commit: '5', amount: 56.048487477575, util: 100 }
|
|
1704
|
+
])
|
|
1705
|
+
setGridStatus1(CommonConst.GRID_STATUS.DATA_EXISTS)
|
|
1706
|
+
}, 1000)
|
|
1707
|
+
}
|
|
1708
|
+
|
|
1709
|
+
const getRowData2 = () => {
|
|
1710
|
+
setGridStatus2(CommonConst.GRID_STATUS.LOADING)
|
|
1711
|
+
setTimeout(() => {
|
|
1712
|
+
setRowData2([
|
|
1713
|
+
{ company: '123123', commit: '5', amount: 34.048487477575, util: 93 },
|
|
1714
|
+
{ company: '234234', commit: '5', amount: 56.048487477575, util: 100 }
|
|
1715
|
+
])
|
|
1716
|
+
setGridStatus2(CommonConst.GRID_STATUS.DATA_EXISTS)
|
|
1717
|
+
}, 1000)
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
// 컴포넌트 마운트 시 초기화
|
|
1721
|
+
useEffect(() => {
|
|
1722
|
+
setColDefs1(getColumnDefs1())
|
|
1723
|
+
setColDefs2(getColumnDefs2())
|
|
1724
|
+
getRowData1()
|
|
1725
|
+
getRowData2()
|
|
1726
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1727
|
+
}, [])
|
|
1728
|
+
`,
|
|
1729
|
+
code: `<Box style={{ flex: 1 }}>
|
|
1730
|
+
<h3>left grid</h3>
|
|
1731
|
+
<OpsnowCommonDataGrid
|
|
1732
|
+
ref={leftGridRef}
|
|
1733
|
+
rowData={rowData1}
|
|
1734
|
+
columnDefs={colDefs1}
|
|
1735
|
+
defaultColDef={defaultColDef}
|
|
1736
|
+
status={gridStatus1}
|
|
1737
|
+
gridOptions={gridOptions1}
|
|
1738
|
+
usedSearch={true}
|
|
1739
|
+
showSelectedRowCount={true}
|
|
1740
|
+
langCd={i18n.getLocale()}
|
|
1741
|
+
onGridReady={() => onGridReady('left')}
|
|
1742
|
+
/>
|
|
1743
|
+
</Box>
|
|
1744
|
+
<Box style={{ flex: 1 }}>
|
|
1745
|
+
<h3>right grid</h3>
|
|
1746
|
+
<OpsnowCommonDataGrid
|
|
1747
|
+
ref={rightGridRef}
|
|
1748
|
+
rowData={rowData2}
|
|
1749
|
+
columnDefs={colDefs2}
|
|
1750
|
+
defaultColDef={defaultColDef}
|
|
1751
|
+
status={gridStatus2}
|
|
1752
|
+
gridOptions={gridOptions2}
|
|
1753
|
+
usedSearch={true}
|
|
1754
|
+
showSelectedRowCount={true}
|
|
1755
|
+
langCd={i18n.getLocale()}
|
|
1756
|
+
onGridReady={() => onGridReady('right')}
|
|
1757
|
+
/>
|
|
1758
|
+
</Box>
|
|
1759
|
+
</Box>`
|
|
1760
|
+
},
|
|
1761
|
+
{
|
|
1762
|
+
title: 'Server side row model',
|
|
1763
|
+
description: 'Server side row model 예제입니다.',
|
|
1764
|
+
code_props_usage: `
|
|
1765
|
+
import { useCommonComponents, useGlobalContext } from '@opsnow-common/opsnow-finops-common-ui-loader'
|
|
1766
|
+
import i18n from '@opsnow-common/opsnow-finops-common-i18n'
|
|
1767
|
+
import { assetAlarmServiceGetServerSideAlarmHistory } from './data/serverSideAlarmService'
|
|
1768
|
+
import CloudServiceCellRenderer from './renderer/CloudServiceCellRenderer'
|
|
1769
|
+
import DetailCellRenderer from './renderer/DetailCellRenderer'
|
|
1770
|
+
|
|
1771
|
+
const { OpsnowCommonDataGrid } = useCommonComponents()
|
|
1772
|
+
const { CommonConst } = useGlobalContext()
|
|
1773
|
+
const [gridStatus, setGridStatus] = useState(CommonConst.GRID_STATUS.LOADING)
|
|
1774
|
+
const [serverSideDatasource, setServerSideDatasource] = useState(null)
|
|
1775
|
+
|
|
1776
|
+
// 서버 전체 데이터(필터 생성용) - 필요시 로컬 state로 저장
|
|
1777
|
+
const [filterOptions, setFilterOptions] = useState({})
|
|
1778
|
+
// 페이지 사이즈
|
|
1779
|
+
const [paginationPageSize] = useState(10)
|
|
1780
|
+
const [totalRowCount, setTotalRowCount] = useState(0)
|
|
1781
|
+
|
|
1782
|
+
const severSideCheckBoxGridRef = useRef(null)
|
|
1783
|
+
|
|
1784
|
+
// 검색어 변경 확인을 위한 ref
|
|
1785
|
+
const lastSearchQueryRef = useRef('')
|
|
1786
|
+
|
|
1787
|
+
// 공통 컬럼 정의
|
|
1788
|
+
const defaultColDef = {
|
|
1789
|
+
wrapText: true,
|
|
1790
|
+
autoHeight: true,
|
|
1791
|
+
suppressMenu: true,
|
|
1792
|
+
unSortIcon: true,
|
|
1793
|
+
sortable: false,
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1796
|
+
// (1) columnDefs를 useMemo로 감싸기
|
|
1797
|
+
const checkBoxColumnDefs = useMemo(() => {
|
|
1798
|
+
return [
|
|
1799
|
+
{
|
|
1800
|
+
field: 'no',
|
|
1801
|
+
headerName: 'No.',
|
|
1802
|
+
cellStyle: { justifyContent: 'center', alignItems: 'center' },
|
|
1803
|
+
width: 110,
|
|
1804
|
+
maxWidth: 110,
|
|
1805
|
+
sortable: true,
|
|
1806
|
+
},
|
|
1807
|
+
{
|
|
1808
|
+
field: 'cloudService',
|
|
1809
|
+
headerName: '클라우드 서비스',
|
|
1810
|
+
cellStyle: { justifyContent: 'left', alignItems: 'center' },
|
|
1811
|
+
width: 200,
|
|
1812
|
+
maxWidth: 200,
|
|
1813
|
+
sortable: true,
|
|
1814
|
+
cellRenderer: CloudServiceCellRenderer,
|
|
1815
|
+
headerComponentParams: {
|
|
1816
|
+
filter: true,
|
|
1817
|
+
filterParams: {
|
|
1818
|
+
values: (params) => {
|
|
1819
|
+
if (params && typeof params.success === 'function') {
|
|
1820
|
+
params.success(filterOptions.cloudService || [])
|
|
1821
|
+
}
|
|
1822
|
+
},
|
|
1823
|
+
},
|
|
1824
|
+
},
|
|
1825
|
+
},
|
|
1826
|
+
{
|
|
1827
|
+
field: 'account',
|
|
1828
|
+
headerName: '계정',
|
|
1829
|
+
resizable: true,
|
|
1830
|
+
cellStyle: { justifyContent: 'left', alignItems: 'center' },
|
|
1831
|
+
width: 230,
|
|
1832
|
+
minWidth: 230,
|
|
1833
|
+
maxWidth: 260,
|
|
1834
|
+
hide: true,
|
|
1835
|
+
},
|
|
1836
|
+
{
|
|
1837
|
+
field: 'title',
|
|
1838
|
+
headerName: '알람 제목',
|
|
1839
|
+
resizable: true,
|
|
1840
|
+
cellStyle: { justifyContent: 'left', alignItems: 'center' },
|
|
1841
|
+
hide: true,
|
|
1842
|
+
maxWidth: 300,
|
|
1843
|
+
},
|
|
1844
|
+
{
|
|
1845
|
+
field: 'message',
|
|
1846
|
+
headerName: '알람 메시지',
|
|
1847
|
+
sortable: true,
|
|
1848
|
+
resizable: true,
|
|
1849
|
+
cellStyle: { justifyContent: 'left', alignItems: 'center' },
|
|
1850
|
+
},
|
|
1851
|
+
{
|
|
1852
|
+
field: 'date',
|
|
1853
|
+
headerName: '알람 일시',
|
|
1854
|
+
sortable: true,
|
|
1855
|
+
cellStyle: { justifyContent: 'left', alignItems: 'center' },
|
|
1856
|
+
},
|
|
1857
|
+
{
|
|
1858
|
+
field: 'detail',
|
|
1859
|
+
headerName: '상세',
|
|
1860
|
+
width: 110,
|
|
1861
|
+
maxWidth: 110,
|
|
1862
|
+
cellRenderer: DetailCellRenderer,
|
|
1863
|
+
},
|
|
1864
|
+
]
|
|
1865
|
+
}, [filterOptions])
|
|
1866
|
+
|
|
1867
|
+
const gridOptions = useMemo(() => ({
|
|
1868
|
+
animateRows: true,
|
|
1869
|
+
rowSelection: { mode: 'multiRow', enableSelectionWithoutKeys: true, headerCheckbox: true },
|
|
1870
|
+
tooltipShowDelay: 100,
|
|
1871
|
+
domLayout: 'autoHeight',
|
|
1872
|
+
alwaysMultiSort: true,
|
|
1873
|
+
serverSideSortAllLevels: true,
|
|
1874
|
+
getRowId: (params) => params.data.no,
|
|
1875
|
+
}), [])
|
|
1876
|
+
|
|
1877
|
+
// (2) serverSideDatasource 생성 함수
|
|
1878
|
+
const setupServerSideDatasource = useCallback((searchQuery = '') => {
|
|
1879
|
+
setGridStatus(CommonConst.GRID_STATUS.LOADING)
|
|
1880
|
+
return {
|
|
1881
|
+
getRows: (params) => {
|
|
1882
|
+
console.log('getRows 호출', params, searchQuery)
|
|
1883
|
+
|
|
1884
|
+
assetAlarmServiceGetServerSideAlarmHistory(params, searchQuery)
|
|
1885
|
+
.then((data) => {
|
|
1886
|
+
// data: { success, allData, rows, lastRow }
|
|
1887
|
+
|
|
1888
|
+
// OpsnowCommonDataGrid의 setServerSideFilterList 사용
|
|
1889
|
+
if (severSideGridRef.current) {
|
|
1890
|
+
const newFilterOpts = severSideGridRef.current.setServerSideFilterList(data.allData)
|
|
1891
|
+
|
|
1892
|
+
// (3) 이전 filterOptions와 달라야만 업데이트
|
|
1893
|
+
setFilterOptions(prev => {
|
|
1894
|
+
const prevStr = JSON.stringify(prev)
|
|
1895
|
+
const newStr = JSON.stringify(newFilterOpts)
|
|
1896
|
+
if (prevStr === newStr) {
|
|
1897
|
+
return prev
|
|
1898
|
+
}
|
|
1899
|
+
return newFilterOpts
|
|
1900
|
+
})
|
|
1901
|
+
}
|
|
1902
|
+
|
|
1903
|
+
setTotalRowCount(data.lastRow)
|
|
1904
|
+
// 데이터 그리드에 데이터 전달
|
|
1905
|
+
params.success({
|
|
1906
|
+
rowData: data.rows,
|
|
1907
|
+
rowCount: data.lastRow,
|
|
1908
|
+
})
|
|
1909
|
+
setGridStatus(CommonConst.GRID_STATUS.DATA_EXISTS)
|
|
1910
|
+
})
|
|
1911
|
+
.catch((error) => {
|
|
1912
|
+
console.error('Error while loading data:', error)
|
|
1913
|
+
setGridStatus(CommonConst.GRID_STATUS.ERROR)
|
|
1914
|
+
params.fail()
|
|
1915
|
+
})
|
|
1916
|
+
},
|
|
1917
|
+
}
|
|
1918
|
+
}, [])
|
|
1919
|
+
|
|
1920
|
+
// (4) 컴포넌트 마운트 시 1회
|
|
1921
|
+
useEffect(() => {
|
|
1922
|
+
const ds = setupServerSideDatasource('')
|
|
1923
|
+
setServerSideDatasource(ds)
|
|
1924
|
+
}, [setupServerSideDatasource])
|
|
1925
|
+
|
|
1926
|
+
const handleSearchQueryChange = (searchQuery) => {
|
|
1927
|
+
if (lastSearchQueryRef.current === searchQuery) return
|
|
1928
|
+
lastSearchQueryRef.current = searchQuery
|
|
1929
|
+
const newDs = setupServerSideDatasource(searchQuery)
|
|
1930
|
+
severSideGridRef.current?.setServerSideDatasource(newDs)
|
|
1931
|
+
severSideCheckBoxGridRef.current?.setServerSideDatasource(newDs)
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
// 페이지 사이즈 변경 (Vue 예제: @changePageSize="getChangePageSize")
|
|
1935
|
+
const getChangePageSize = (num, size) => {
|
|
1936
|
+
console.log('Page changed:', num, size)
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1939
|
+
// selectionChanged
|
|
1940
|
+
const selectionChanged = (selectedNodes) => {
|
|
1941
|
+
console.log('Selected nodes:', selectedNodes)
|
|
1942
|
+
}
|
|
1943
|
+
|
|
1944
|
+
// resetCheckBox
|
|
1945
|
+
const resetCheckBox = () => {
|
|
1946
|
+
severSideCheckBoxGridRef.current?.clearSelectionState()
|
|
1947
|
+
}
|
|
1948
|
+
// loadCheckBox
|
|
1949
|
+
const loadCheckBox = () => {
|
|
1950
|
+
severSideCheckBoxGridRef.current?.loadSelectionState()
|
|
1951
|
+
}
|
|
1952
|
+
// setCheckBox
|
|
1953
|
+
const setCheckBox = () => {
|
|
1954
|
+
severSideCheckBoxGridRef.current?.setSelectionState({
|
|
1955
|
+
selectAll: false,
|
|
1956
|
+
toggledNodes: ['50', '48', '35'],
|
|
1957
|
+
})
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1960
|
+
// 아직 datasource가 준비 안 되었으면 로딩 표시
|
|
1961
|
+
if (!serverSideDatasource) {
|
|
1962
|
+
return <div>Loading datasource...</div>
|
|
1963
|
+
}
|
|
1964
|
+
`,
|
|
1965
|
+
code: `<OpsnowCommonDataGrid
|
|
1966
|
+
ref={severSideCheckBoxGridRef}
|
|
1967
|
+
usedSearch={true}
|
|
1968
|
+
columnDefs={checkBoxColumnDefs}
|
|
1969
|
+
defaultColDef={defaultColDef}
|
|
1970
|
+
status={gridStatus}
|
|
1971
|
+
gridOptions={gridOptions}
|
|
1972
|
+
useOpsnowUs={true}
|
|
1973
|
+
rowModelType="serverSide"
|
|
1974
|
+
serverSideDatasource={serverSideDatasource}
|
|
1975
|
+
pagination={true}
|
|
1976
|
+
cacheBlockSize={10}
|
|
1977
|
+
paginationPageSize={paginationPageSize}
|
|
1978
|
+
serverSideTotalPageSize={totalRowCount}
|
|
1979
|
+
langCd={i18n.getLocale()}
|
|
1980
|
+
onSearchQueryChanged={handleSearchQueryChange}
|
|
1981
|
+
onSelectionChanged={selectionChanged}
|
|
1982
|
+
>
|
|
1983
|
+
<button onClick={resetCheckBox} style={{ marginRight: '1rem' }}>
|
|
1984
|
+
reset check box
|
|
1985
|
+
</button>
|
|
1986
|
+
<button onClick={loadCheckBox} style={{ marginRight: '1rem' }}>
|
|
1987
|
+
load check box
|
|
1988
|
+
</button>
|
|
1989
|
+
<button onClick={setCheckBox}>set check box</button>
|
|
1990
|
+
</OpsnowCommonDataGrid>`
|
|
1991
|
+
},
|
|
1992
|
+
{
|
|
1993
|
+
title: 'Column Group - Basic',
|
|
1994
|
+
description: 'children 속성을 사용한 기본 컬럼 그룹 예제입니다.',
|
|
1995
|
+
code_props_usage: `
|
|
1996
|
+
import { useCommonComponents, useGlobalContext } from '@opsnow-common/opsnow-finops-common-ui-loader'
|
|
1997
|
+
import i18n from '@opsnow-common/opsnow-finops-common-i18n'
|
|
1998
|
+
|
|
1999
|
+
// 컬럼 그룹 정의 - children을 사용한 예제
|
|
2000
|
+
const columnDefs = [
|
|
2001
|
+
{
|
|
2002
|
+
groupId: 'vehicleGroup',
|
|
2003
|
+
headerName: 'Vehicle Info',
|
|
2004
|
+
children: [
|
|
2005
|
+
{ headerName: 'Make', field: 'make' },
|
|
2006
|
+
{ headerName: 'Model', field: 'model' },
|
|
2007
|
+
],
|
|
2008
|
+
},
|
|
2009
|
+
{
|
|
2010
|
+
groupId: 'financialGroup',
|
|
2011
|
+
headerName: 'Financial Info',
|
|
2012
|
+
children: [
|
|
2013
|
+
{
|
|
2014
|
+
headerName: 'Price',
|
|
2015
|
+
field: 'price',
|
|
2016
|
+
valueFormatter: params => params.value ? \`$\${params.value.toLocaleString()}\` : ''
|
|
2017
|
+
},
|
|
2018
|
+
{
|
|
2019
|
+
headerName: 'Tax',
|
|
2020
|
+
field: 'tax',
|
|
2021
|
+
valueFormatter: params => params.value ? \`$\${params.value.toLocaleString()}\` : ''
|
|
2022
|
+
},
|
|
2023
|
+
],
|
|
2024
|
+
},
|
|
2025
|
+
{
|
|
2026
|
+
groupId: 'salesGroup',
|
|
2027
|
+
headerName: 'Sales Info',
|
|
2028
|
+
children: [
|
|
2029
|
+
{ headerName: 'Year', field: 'year' },
|
|
2030
|
+
{ headerName: 'Country', field: 'country' },
|
|
2031
|
+
],
|
|
2032
|
+
},
|
|
2033
|
+
]
|
|
2034
|
+
|
|
2035
|
+
// 초기 행 데이터
|
|
2036
|
+
const initialRowData = [
|
|
2037
|
+
{ make: 'Toyota', model: 'Celica', price: 35000, tax: 3500, year: 2020, country: 'Japan' },
|
|
2038
|
+
{ make: 'Ford', model: 'Mondeo', price: 32000, tax: 3200, year: 2019, country: 'USA' },
|
|
2039
|
+
{ make: 'Porsche', model: 'Boxter', price: 72000, tax: 7200, year: 2021, country: 'Germany' },
|
|
2040
|
+
{ make: 'BMW', model: 'M3', price: 65000, tax: 6500, year: 2020, country: 'Germany' },
|
|
2041
|
+
{ make: 'Honda', model: 'Civic', price: 28000, tax: 2800, year: 2019, country: 'Japan' },
|
|
2042
|
+
{ make: 'Tesla', model: 'Model S', price: 95000, tax: 9500, year: 2022, country: 'USA' },
|
|
2043
|
+
{ make: 'Audi', model: 'A4', price: 45000, tax: 4500, year: 2020, country: 'Germany' },
|
|
2044
|
+
{ make: 'Mercedes', model: 'C-Class', price: 55000, tax: 5500, year: 2021, country: 'Germany' },
|
|
2045
|
+
{ make: 'Nissan', model: 'Altima', price: 30000, tax: 3000, year: 2019, country: 'Japan' },
|
|
2046
|
+
{ make: 'Chevrolet', model: 'Malibu', price: 33000, tax: 3300, year: 2020, country: 'USA' },
|
|
2047
|
+
]
|
|
2048
|
+
|
|
2049
|
+
const gridOptions = {
|
|
2050
|
+
animateRows: true,
|
|
2051
|
+
rowSelection: { mode: 'singleRow', checkboxes: false, enableClickSelection: true },
|
|
2052
|
+
tooltipShowDelay: 100,
|
|
2053
|
+
domLayout: 'autoHeight',
|
|
2054
|
+
suppressCellFocus: true,
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
const { OpsnowCommonDataGrid } = useCommonComponents();
|
|
2058
|
+
const { CommonConst } = useGlobalContext()
|
|
2059
|
+
// rowData와 status 상태를 관리
|
|
2060
|
+
const [rowData, setRowData] = useState([]) // 초기에는 빈 배열로 설정
|
|
2061
|
+
const [status, setStatus] = useState(CommonConst.GRID_STATUS.LOADING) // 초기에는 LOADING 상태
|
|
2062
|
+
|
|
2063
|
+
// 데이터 지연 설정을 위한 useEffect
|
|
2064
|
+
useEffect(() => {
|
|
2065
|
+
// 1초 후에 데이터를 설정
|
|
2066
|
+
const timeoutId = setTimeout(() => {
|
|
2067
|
+
setRowData(initialRowData)
|
|
2068
|
+
setStatus(CommonConst.GRID_STATUS.DATA_EXISTS)
|
|
2069
|
+
}, 1000)
|
|
2070
|
+
|
|
2071
|
+
// 컴포넌트 언마운트 시 타임아웃 클리어
|
|
2072
|
+
return () => {
|
|
2073
|
+
clearTimeout(timeoutId)
|
|
2074
|
+
}
|
|
2075
|
+
}, [])
|
|
2076
|
+
`,
|
|
2077
|
+
code: `<OpsnowCommonDataGrid
|
|
2078
|
+
columnDefs={columnDefs}
|
|
2079
|
+
rowData={rowData}
|
|
2080
|
+
status={status}
|
|
2081
|
+
gridOptions={gridOptions}
|
|
2082
|
+
usedSearch={true}
|
|
2083
|
+
pagination={true}
|
|
2084
|
+
paginationPageSize={5}
|
|
2085
|
+
pageSizeList={[
|
|
2086
|
+
{ label: '5', value: 5 },
|
|
2087
|
+
{ label: '10', value: 10 },
|
|
2088
|
+
{ label: '20', value: 20 },
|
|
2089
|
+
]}
|
|
2090
|
+
langCd={i18n.getLocale()}
|
|
2091
|
+
/>`
|
|
2092
|
+
},
|
|
2093
|
+
{
|
|
2094
|
+
title: 'Column Group - Nested',
|
|
2095
|
+
description: '다층 구조의 중첩된 컬럼 그룹 예제입니다.',
|
|
2096
|
+
code_props_usage: `
|
|
2097
|
+
import { useCommonComponents, useGlobalContext } from '@opsnow-common/opsnow-finops-common-ui-loader'
|
|
2098
|
+
import i18n from '@opsnow-common/opsnow-finops-common-i18n'
|
|
2099
|
+
|
|
2100
|
+
// 중첩된 컬럼 그룹 정의
|
|
2101
|
+
const nestedColumnDefs = [
|
|
2102
|
+
{
|
|
2103
|
+
groupId: 'vehicleMainGroup',
|
|
2104
|
+
headerName: 'Vehicle',
|
|
2105
|
+
children: [
|
|
2106
|
+
{
|
|
2107
|
+
groupId: 'vehicleBasicGroup',
|
|
2108
|
+
headerName: 'Basic Info',
|
|
2109
|
+
children: [
|
|
2110
|
+
{ headerName: 'Make', field: 'make' },
|
|
2111
|
+
{ headerName: 'Model', field: 'model' },
|
|
2112
|
+
],
|
|
2113
|
+
},
|
|
2114
|
+
{
|
|
2115
|
+
groupId: 'vehicleDetailsGroup',
|
|
2116
|
+
headerName: 'Details',
|
|
2117
|
+
children: [
|
|
2118
|
+
{ headerName: 'Year', field: 'year' },
|
|
2119
|
+
{ headerName: 'Country', field: 'country' },
|
|
2120
|
+
],
|
|
2121
|
+
},
|
|
2122
|
+
],
|
|
2123
|
+
},
|
|
2124
|
+
{
|
|
2125
|
+
groupId: 'financialMainGroup',
|
|
2126
|
+
headerName: 'Financial',
|
|
2127
|
+
children: [
|
|
2128
|
+
{
|
|
2129
|
+
headerName: 'Price',
|
|
2130
|
+
field: 'price',
|
|
2131
|
+
valueFormatter: params => params.value ? \`$\${params.value.toLocaleString()}\` : ''
|
|
2132
|
+
},
|
|
2133
|
+
{
|
|
2134
|
+
headerName: 'Tax',
|
|
2135
|
+
field: 'tax',
|
|
2136
|
+
valueFormatter: params => params.value ? \`$\${params.value.toLocaleString()}\` : ''
|
|
2137
|
+
},
|
|
2138
|
+
],
|
|
2139
|
+
},
|
|
2140
|
+
]
|
|
2141
|
+
|
|
2142
|
+
// 초기 행 데이터
|
|
2143
|
+
const initialRowData = [
|
|
2144
|
+
{ make: 'Toyota', model: 'Celica', price: 35000, tax: 3500, year: 2020, country: 'Japan' },
|
|
2145
|
+
{ make: 'Ford', model: 'Mondeo', price: 32000, tax: 3200, year: 2019, country: 'USA' },
|
|
2146
|
+
{ make: 'Porsche', model: 'Boxter', price: 72000, tax: 7200, year: 2021, country: 'Germany' },
|
|
2147
|
+
{ make: 'BMW', model: 'M3', price: 65000, tax: 6500, year: 2020, country: 'Germany' },
|
|
2148
|
+
{ make: 'Honda', model: 'Civic', price: 28000, tax: 2800, year: 2019, country: 'Japan' },
|
|
2149
|
+
{ make: 'Tesla', model: 'Model S', price: 95000, tax: 9500, year: 2022, country: 'USA' },
|
|
2150
|
+
{ make: 'Audi', model: 'A4', price: 45000, tax: 4500, year: 2020, country: 'Germany' },
|
|
2151
|
+
{ make: 'Mercedes', model: 'C-Class', price: 55000, tax: 5500, year: 2021, country: 'Germany' },
|
|
2152
|
+
{ make: 'Nissan', model: 'Altima', price: 30000, tax: 3000, year: 2019, country: 'Japan' },
|
|
2153
|
+
{ make: 'Chevrolet', model: 'Malibu', price: 33000, tax: 3300, year: 2020, country: 'USA' },
|
|
2154
|
+
]
|
|
2155
|
+
|
|
2156
|
+
const gridOptions = {
|
|
2157
|
+
animateRows: true,
|
|
2158
|
+
rowSelection: { mode: 'singleRow', checkboxes: false, enableClickSelection: true },
|
|
2159
|
+
tooltipShowDelay: 100,
|
|
2160
|
+
domLayout: 'autoHeight',
|
|
2161
|
+
suppressCellFocus: true,
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2164
|
+
const { OpsnowCommonDataGrid } = useCommonComponents();
|
|
2165
|
+
const { CommonConst } = useGlobalContext()
|
|
2166
|
+
// rowData와 status 상태를 관리
|
|
2167
|
+
const [rowData, setRowData] = useState([]) // 초기에는 빈 배열로 설정
|
|
2168
|
+
const [status, setStatus] = useState(CommonConst.GRID_STATUS.LOADING) // 초기에는 LOADING 상태
|
|
2169
|
+
|
|
2170
|
+
// 데이터 지연 설정을 위한 useEffect
|
|
2171
|
+
useEffect(() => {
|
|
2172
|
+
// 1초 후에 데이터를 설정
|
|
2173
|
+
const timeoutId = setTimeout(() => {
|
|
2174
|
+
setRowData(initialRowData)
|
|
2175
|
+
setStatus(CommonConst.GRID_STATUS.DATA_EXISTS)
|
|
2176
|
+
}, 1000)
|
|
2177
|
+
|
|
2178
|
+
// 컴포넌트 언마운트 시 타임아웃 클리어
|
|
2179
|
+
return () => {
|
|
2180
|
+
clearTimeout(timeoutId)
|
|
2181
|
+
}
|
|
2182
|
+
}, [])
|
|
2183
|
+
`,
|
|
2184
|
+
code: `<OpsnowCommonDataGrid
|
|
2185
|
+
key={status}
|
|
2186
|
+
columnDefs={nestedColumnDefs}
|
|
2187
|
+
rowData={rowData}
|
|
2188
|
+
status={status}
|
|
2189
|
+
gridOptions={gridOptions}
|
|
2190
|
+
usedSearch={true}
|
|
2191
|
+
langCd={i18n.getLocale()}
|
|
2192
|
+
/>`
|
|
2193
|
+
},
|
|
2194
|
+
{
|
|
2195
|
+
title: 'Error Status',
|
|
2196
|
+
description: 'Error Status 예제입니다.',
|
|
2197
|
+
code_props_usage: `
|
|
2198
|
+
import { useCommonComponents, useGlobalContext } from '@opsnow-common/opsnow-finops-common-ui-loader'
|
|
2199
|
+
import i18n from '@opsnow-common/opsnow-finops-common-i18n'
|
|
2200
|
+
|
|
2201
|
+
// 컬럼 정의
|
|
2202
|
+
const columnDefs = [
|
|
2203
|
+
{ headerName: 'Make', field: 'make' },
|
|
2204
|
+
{ headerName: 'Model', field: 'model' },
|
|
2205
|
+
{ headerName: 'Price', field: 'price' },
|
|
2206
|
+
]
|
|
2207
|
+
|
|
2208
|
+
const gridOptions = {
|
|
2209
|
+
animateRows: true,
|
|
2210
|
+
rowSelection: { mode: 'singleRow', checkboxes: false, enableClickSelection: true },
|
|
2211
|
+
tooltipShowDelay: 100,
|
|
2212
|
+
domLayout: 'autoHeight',
|
|
2213
|
+
suppressCellFocus: true,
|
|
2214
|
+
}
|
|
2215
|
+
|
|
2216
|
+
const { OpsnowCommonDataGrid } = useCommonComponents();
|
|
2217
|
+
const { CommonConst } = useGlobalContext()
|
|
2218
|
+
// errorStatus 상태를 관리
|
|
2219
|
+
const [errorStatus, setErrorStatus] = useState(CommonConst.GRID_STATUS.LOADING)
|
|
2220
|
+
|
|
2221
|
+
// 에러 상태 시뮬레이션을 위한 useEffect
|
|
2222
|
+
useEffect(() => {
|
|
2223
|
+
// 1초 후에 에러 상태로 설정
|
|
2224
|
+
const timeoutId = setTimeout(() => {
|
|
2225
|
+
setErrorStatus(CommonConst.GRID_STATUS.ERROR)
|
|
2226
|
+
}, 1000)
|
|
2227
|
+
|
|
2228
|
+
// 컴포넌트 언마운트 시 타임아웃 클리어
|
|
2229
|
+
return () => {
|
|
2230
|
+
clearTimeout(timeoutId)
|
|
2231
|
+
}
|
|
2232
|
+
}, [])
|
|
2233
|
+
`,
|
|
2234
|
+
code: `<OpsnowCommonDataGrid
|
|
2235
|
+
columnDefs={columnDefs}
|
|
2236
|
+
rowData={[]}
|
|
2237
|
+
status={errorStatus}
|
|
2238
|
+
gridOptions={gridOptions}
|
|
2239
|
+
textErrorTitle="데이터 로드 실패"
|
|
2240
|
+
textErrorDescription="데이터를 불러오는 중 오류가 발생했습니다."
|
|
2241
|
+
langCd={i18n.getLocale()}
|
|
2242
|
+
/>`
|
|
2243
|
+
},
|
|
2244
|
+
{
|
|
2245
|
+
title: 'Auto Row Height',
|
|
2246
|
+
description: '행 높이 자동 조절 예제입니다. 다음 경우 반드시 적용: 1) 긴 텍스트가 들어올 수 있는 컬럼이 있는 경우 (설명, 메시지, 항목, 이름, 제목, 등) 2) 컬럼이 resizable인 경우 - 폭을 줄여도 텍스트가 잘리지 않고 여러 줄로 정상 표시되어야 함 (ellipsis로 자르면 안됨)',
|
|
2247
|
+
code_props_usage: `
|
|
2248
|
+
import { useCommonComponents, useGlobalContext } from '@opsnow-common/opsnow-finops-common-ui-loader'
|
|
2249
|
+
import i18n from '@opsnow-common/opsnow-finops-common-i18n'
|
|
2250
|
+
|
|
2251
|
+
// Long Text용 컬럼 정의
|
|
2252
|
+
const columnDefs = [
|
|
2253
|
+
{
|
|
2254
|
+
headerName: 'ID',
|
|
2255
|
+
field: 'id',
|
|
2256
|
+
flex: 1,
|
|
2257
|
+
minWidth: 80
|
|
2258
|
+
},
|
|
2259
|
+
{
|
|
2260
|
+
headerName: 'Description',
|
|
2261
|
+
field: 'description',
|
|
2262
|
+
flex: 3, // 가장 넓은 비율
|
|
2263
|
+
minWidth: 300,
|
|
2264
|
+
wrapText: true, // 텍스트 줄바꿈 - 필수
|
|
2265
|
+
autoHeight: true, // 행 높이 자동 조절 - 필수
|
|
2266
|
+
suppressSizeToFit: true, // 자동 리사이즈 방지
|
|
2267
|
+
cellStyle: {
|
|
2268
|
+
alignItems: 'center' // autoHeight 작동을 위해 필수
|
|
2269
|
+
}
|
|
2270
|
+
},
|
|
2271
|
+
{
|
|
2272
|
+
headerName: 'Status',
|
|
2273
|
+
field: 'status',
|
|
2274
|
+
flex: 1,
|
|
2275
|
+
minWidth: 120
|
|
2276
|
+
},
|
|
2277
|
+
]
|
|
2278
|
+
|
|
2279
|
+
// Long Text용 행 데이터
|
|
2280
|
+
const rowData = [
|
|
2281
|
+
{
|
|
2282
|
+
id: 1,
|
|
2283
|
+
description: 'This is a very long description text that will wrap to multiple lines when the column width is not sufficient to display all content in a single line. This demonstrates the autoHeight feature.',
|
|
2284
|
+
status: 'Active'
|
|
2285
|
+
},
|
|
2286
|
+
{
|
|
2287
|
+
id: 2,
|
|
2288
|
+
description: 'Short text',
|
|
2289
|
+
status: 'Inactive'
|
|
2290
|
+
},
|
|
2291
|
+
{
|
|
2292
|
+
id: 3,
|
|
2293
|
+
description: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris.',
|
|
2294
|
+
status: 'Pending'
|
|
2295
|
+
},
|
|
2296
|
+
{
|
|
2297
|
+
id: 4,
|
|
2298
|
+
description: 'Another example of long text content that needs multiple lines',
|
|
2299
|
+
status: 'Active'
|
|
2300
|
+
},
|
|
2301
|
+
]
|
|
2302
|
+
|
|
2303
|
+
const defaultColDef = {
|
|
2304
|
+
wrapText: true, // 텍스트 줄바꿈 활성화
|
|
2305
|
+
autoHeight: true, // 셀 높이 자동 조절
|
|
2306
|
+
}
|
|
2307
|
+
|
|
2308
|
+
const gridOptions = {
|
|
2309
|
+
animateRows: true,
|
|
2310
|
+
rowSelection: { mode: 'singleRow', checkboxes: false, enableClickSelection: true },
|
|
2311
|
+
tooltipShowDelay: 100,
|
|
2312
|
+
domLayout: 'autoHeight', // 그리드 전체 레이아웃 자동 높이
|
|
2313
|
+
suppressCellFocus: true,
|
|
2314
|
+
}
|
|
2315
|
+
|
|
2316
|
+
const { OpsnowCommonDataGrid } = useCommonComponents();
|
|
2317
|
+
const { CommonConst } = useGlobalContext()
|
|
2318
|
+
`,
|
|
2319
|
+
code: `<OpsnowCommonDataGrid
|
|
2320
|
+
columnDefs={columnDefs}
|
|
2321
|
+
defaultColDef={defaultColDef}
|
|
2322
|
+
rowData={rowData}
|
|
2323
|
+
status={CommonConst.GRID_STATUS.DATA_EXISTS}
|
|
2324
|
+
gridOptions={gridOptions}
|
|
2325
|
+
langCd={i18n.getLocale()}
|
|
2326
|
+
/>`
|
|
2327
|
+
},
|
|
2328
|
+
{
|
|
2329
|
+
title: 'API 데이터 로딩 (getAxios 사용)',
|
|
2330
|
+
description: 'API에서 데이터를 가져와 그리드에 표시하는 예제입니다. axios 직접 사용 대신 반드시 getAxios를 사용해야 합니다.',
|
|
2331
|
+
code_props_usage: `
|
|
2332
|
+
import { useCommonComponents, useGlobalContext } from '@opsnow-common/opsnow-finops-common-ui-loader'
|
|
2333
|
+
import { getAxios } from '@opsnow-common/opsnow-finops-common-axios'
|
|
2334
|
+
import i18n from '@opsnow-common/opsnow-finops-common-i18n'
|
|
2335
|
+
|
|
2336
|
+
// ⚠️ 주의: axios 직접 import 금지!
|
|
2337
|
+
// import axios from 'axios' // ❌ 금지
|
|
2338
|
+
// import { getAxios } from '@opsnow-common/opsnow-finops-common-axios' // ✅ 필수
|
|
2339
|
+
|
|
2340
|
+
const columnDefs = [
|
|
2341
|
+
{ headerName: 'ID', field: 'id' },
|
|
2342
|
+
{ headerName: 'Name', field: 'name' },
|
|
2343
|
+
{ headerName: 'Value', field: 'value' },
|
|
2344
|
+
]
|
|
2345
|
+
|
|
2346
|
+
const gridOptions = {
|
|
2347
|
+
animateRows: true,
|
|
2348
|
+
rowSelection: { mode: 'singleRow', checkboxes: false, enableClickSelection: true },
|
|
2349
|
+
domLayout: 'autoHeight',
|
|
2350
|
+
}
|
|
2351
|
+
|
|
2352
|
+
const { OpsnowCommonDataGrid } = useCommonComponents()
|
|
2353
|
+
const { CommonConst } = useGlobalContext()
|
|
2354
|
+
|
|
2355
|
+
const [rowData, setRowData] = useState([])
|
|
2356
|
+
const [status, setStatus] = useState(CommonConst.GRID_STATUS.LOADING)
|
|
2357
|
+
|
|
2358
|
+
// API 데이터 로딩
|
|
2359
|
+
const fetchData = async () => {
|
|
2360
|
+
try {
|
|
2361
|
+
setStatus(CommonConst.GRID_STATUS.LOADING)
|
|
2362
|
+
const response = await getAxios().get('/api/your-endpoint')
|
|
2363
|
+
|
|
2364
|
+
if (response.data && response.data.length > 0) {
|
|
2365
|
+
setRowData(response.data)
|
|
2366
|
+
setStatus(CommonConst.GRID_STATUS.DATA_EXISTS)
|
|
2367
|
+
} else {
|
|
2368
|
+
setRowData([])
|
|
2369
|
+
setStatus(CommonConst.GRID_STATUS.NO_DATA)
|
|
2370
|
+
}
|
|
2371
|
+
} catch (error) {
|
|
2372
|
+
console.error('API 호출 에러:', error)
|
|
2373
|
+
setStatus(CommonConst.GRID_STATUS.ERROR)
|
|
2374
|
+
}
|
|
2375
|
+
}
|
|
2376
|
+
|
|
2377
|
+
useEffect(() => {
|
|
2378
|
+
fetchData()
|
|
2379
|
+
}, [])
|
|
2380
|
+
`,
|
|
2381
|
+
code: `<OpsnowCommonDataGrid
|
|
2382
|
+
columnDefs={columnDefs}
|
|
2383
|
+
rowData={rowData}
|
|
2384
|
+
status={status}
|
|
2385
|
+
gridOptions={gridOptions}
|
|
2386
|
+
langCd={i18n.getLocale()}
|
|
2387
|
+
/>`
|
|
2388
|
+
},
|
|
2389
|
+
];
|