@opsnow-mcp/opsnow-mcp-common-ui-server 1.0.8 → 1.0.10
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.
|
@@ -1885,4 +1885,332 @@ export const DataGridExamples = [
|
|
|
1885
1885
|
<button onClick={setCheckBox}>set check box</button>
|
|
1886
1886
|
</OpsnowCommonDataGrid>`
|
|
1887
1887
|
},
|
|
1888
|
+
{
|
|
1889
|
+
title: 'Column Group - Basic',
|
|
1890
|
+
description: 'children 속성을 사용한 기본 컬럼 그룹 예제입니다.',
|
|
1891
|
+
code_props_usage: `
|
|
1892
|
+
import { useCommonComponents, useGlobalContext } from '@opsnow-common/opsnow-finops-common-ui-loader'
|
|
1893
|
+
import i18n from '@opsnow-common/opsnow-finops-common-i18n'
|
|
1894
|
+
|
|
1895
|
+
// 컬럼 그룹 정의 - children을 사용한 예제
|
|
1896
|
+
const columnDefs = [
|
|
1897
|
+
{
|
|
1898
|
+
groupId: 'vehicleGroup',
|
|
1899
|
+
headerName: 'Vehicle Info',
|
|
1900
|
+
children: [
|
|
1901
|
+
{ headerName: 'Make', field: 'make' },
|
|
1902
|
+
{ headerName: 'Model', field: 'model' },
|
|
1903
|
+
],
|
|
1904
|
+
},
|
|
1905
|
+
{
|
|
1906
|
+
groupId: 'financialGroup',
|
|
1907
|
+
headerName: 'Financial Info',
|
|
1908
|
+
children: [
|
|
1909
|
+
{
|
|
1910
|
+
headerName: 'Price',
|
|
1911
|
+
field: 'price',
|
|
1912
|
+
valueFormatter: params => params.value ? \`$\${params.value.toLocaleString()}\` : ''
|
|
1913
|
+
},
|
|
1914
|
+
{
|
|
1915
|
+
headerName: 'Tax',
|
|
1916
|
+
field: 'tax',
|
|
1917
|
+
valueFormatter: params => params.value ? \`$\${params.value.toLocaleString()}\` : ''
|
|
1918
|
+
},
|
|
1919
|
+
],
|
|
1920
|
+
},
|
|
1921
|
+
{
|
|
1922
|
+
groupId: 'salesGroup',
|
|
1923
|
+
headerName: 'Sales Info',
|
|
1924
|
+
children: [
|
|
1925
|
+
{ headerName: 'Year', field: 'year' },
|
|
1926
|
+
{ headerName: 'Country', field: 'country' },
|
|
1927
|
+
],
|
|
1928
|
+
},
|
|
1929
|
+
]
|
|
1930
|
+
|
|
1931
|
+
// 초기 행 데이터
|
|
1932
|
+
const initialRowData = [
|
|
1933
|
+
{ make: 'Toyota', model: 'Celica', price: 35000, tax: 3500, year: 2020, country: 'Japan' },
|
|
1934
|
+
{ make: 'Ford', model: 'Mondeo', price: 32000, tax: 3200, year: 2019, country: 'USA' },
|
|
1935
|
+
{ make: 'Porsche', model: 'Boxter', price: 72000, tax: 7200, year: 2021, country: 'Germany' },
|
|
1936
|
+
{ make: 'BMW', model: 'M3', price: 65000, tax: 6500, year: 2020, country: 'Germany' },
|
|
1937
|
+
{ make: 'Honda', model: 'Civic', price: 28000, tax: 2800, year: 2019, country: 'Japan' },
|
|
1938
|
+
{ make: 'Tesla', model: 'Model S', price: 95000, tax: 9500, year: 2022, country: 'USA' },
|
|
1939
|
+
{ make: 'Audi', model: 'A4', price: 45000, tax: 4500, year: 2020, country: 'Germany' },
|
|
1940
|
+
{ make: 'Mercedes', model: 'C-Class', price: 55000, tax: 5500, year: 2021, country: 'Germany' },
|
|
1941
|
+
{ make: 'Nissan', model: 'Altima', price: 30000, tax: 3000, year: 2019, country: 'Japan' },
|
|
1942
|
+
{ make: 'Chevrolet', model: 'Malibu', price: 33000, tax: 3300, year: 2020, country: 'USA' },
|
|
1943
|
+
]
|
|
1944
|
+
|
|
1945
|
+
const gridOptions = {
|
|
1946
|
+
animateRows: true,
|
|
1947
|
+
rowSelection: { mode: 'singleRow', checkboxes: false, enableClickSelection: true },
|
|
1948
|
+
tooltipShowDelay: 100,
|
|
1949
|
+
domLayout: 'autoHeight',
|
|
1950
|
+
suppressCellFocus: true,
|
|
1951
|
+
}
|
|
1952
|
+
|
|
1953
|
+
const { OpsnowCommonDataGrid } = useCommonComponents();
|
|
1954
|
+
const { CommonConst } = useGlobalContext()
|
|
1955
|
+
// rowData와 status 상태를 관리
|
|
1956
|
+
const [rowData, setRowData] = useState([]) // 초기에는 빈 배열로 설정
|
|
1957
|
+
const [status, setStatus] = useState(CommonConst.GRID_STATUS.LOADING) // 초기에는 LOADING 상태
|
|
1958
|
+
|
|
1959
|
+
// 데이터 지연 설정을 위한 useEffect
|
|
1960
|
+
useEffect(() => {
|
|
1961
|
+
// 1초 후에 데이터를 설정
|
|
1962
|
+
const timeoutId = setTimeout(() => {
|
|
1963
|
+
setRowData(initialRowData)
|
|
1964
|
+
setStatus(CommonConst.GRID_STATUS.DATA_EXISTS)
|
|
1965
|
+
}, 1000)
|
|
1966
|
+
|
|
1967
|
+
// 컴포넌트 언마운트 시 타임아웃 클리어
|
|
1968
|
+
return () => {
|
|
1969
|
+
clearTimeout(timeoutId)
|
|
1970
|
+
}
|
|
1971
|
+
}, [])
|
|
1972
|
+
`,
|
|
1973
|
+
code: `<OpsnowCommonDataGrid
|
|
1974
|
+
columnDefs={columnDefs}
|
|
1975
|
+
rowData={rowData}
|
|
1976
|
+
status={status}
|
|
1977
|
+
gridOptions={gridOptions}
|
|
1978
|
+
usedSearch={true}
|
|
1979
|
+
pagination={true}
|
|
1980
|
+
paginationPageSize={5}
|
|
1981
|
+
pageSizeList={[
|
|
1982
|
+
{ label: '5', value: 5 },
|
|
1983
|
+
{ label: '10', value: 10 },
|
|
1984
|
+
{ label: '20', value: 20 },
|
|
1985
|
+
]}
|
|
1986
|
+
langCd={i18n.getLocale()}
|
|
1987
|
+
/>`
|
|
1988
|
+
},
|
|
1989
|
+
{
|
|
1990
|
+
title: 'Column Group - Nested',
|
|
1991
|
+
description: '다층 구조의 중첩된 컬럼 그룹 예제입니다.',
|
|
1992
|
+
code_props_usage: `
|
|
1993
|
+
import { useCommonComponents, useGlobalContext } from '@opsnow-common/opsnow-finops-common-ui-loader'
|
|
1994
|
+
import i18n from '@opsnow-common/opsnow-finops-common-i18n'
|
|
1995
|
+
|
|
1996
|
+
// 중첩된 컬럼 그룹 정의
|
|
1997
|
+
const nestedColumnDefs = [
|
|
1998
|
+
{
|
|
1999
|
+
groupId: 'vehicleMainGroup',
|
|
2000
|
+
headerName: 'Vehicle',
|
|
2001
|
+
children: [
|
|
2002
|
+
{
|
|
2003
|
+
groupId: 'vehicleBasicGroup',
|
|
2004
|
+
headerName: 'Basic Info',
|
|
2005
|
+
children: [
|
|
2006
|
+
{ headerName: 'Make', field: 'make' },
|
|
2007
|
+
{ headerName: 'Model', field: 'model' },
|
|
2008
|
+
],
|
|
2009
|
+
},
|
|
2010
|
+
{
|
|
2011
|
+
groupId: 'vehicleDetailsGroup',
|
|
2012
|
+
headerName: 'Details',
|
|
2013
|
+
children: [
|
|
2014
|
+
{ headerName: 'Year', field: 'year' },
|
|
2015
|
+
{ headerName: 'Country', field: 'country' },
|
|
2016
|
+
],
|
|
2017
|
+
},
|
|
2018
|
+
],
|
|
2019
|
+
},
|
|
2020
|
+
{
|
|
2021
|
+
groupId: 'financialMainGroup',
|
|
2022
|
+
headerName: 'Financial',
|
|
2023
|
+
children: [
|
|
2024
|
+
{
|
|
2025
|
+
headerName: 'Price',
|
|
2026
|
+
field: 'price',
|
|
2027
|
+
valueFormatter: params => params.value ? \`$\${params.value.toLocaleString()}\` : ''
|
|
2028
|
+
},
|
|
2029
|
+
{
|
|
2030
|
+
headerName: 'Tax',
|
|
2031
|
+
field: 'tax',
|
|
2032
|
+
valueFormatter: params => params.value ? \`$\${params.value.toLocaleString()}\` : ''
|
|
2033
|
+
},
|
|
2034
|
+
],
|
|
2035
|
+
},
|
|
2036
|
+
]
|
|
2037
|
+
|
|
2038
|
+
// 초기 행 데이터
|
|
2039
|
+
const initialRowData = [
|
|
2040
|
+
{ make: 'Toyota', model: 'Celica', price: 35000, tax: 3500, year: 2020, country: 'Japan' },
|
|
2041
|
+
{ make: 'Ford', model: 'Mondeo', price: 32000, tax: 3200, year: 2019, country: 'USA' },
|
|
2042
|
+
{ make: 'Porsche', model: 'Boxter', price: 72000, tax: 7200, year: 2021, country: 'Germany' },
|
|
2043
|
+
{ make: 'BMW', model: 'M3', price: 65000, tax: 6500, year: 2020, country: 'Germany' },
|
|
2044
|
+
{ make: 'Honda', model: 'Civic', price: 28000, tax: 2800, year: 2019, country: 'Japan' },
|
|
2045
|
+
{ make: 'Tesla', model: 'Model S', price: 95000, tax: 9500, year: 2022, country: 'USA' },
|
|
2046
|
+
{ make: 'Audi', model: 'A4', price: 45000, tax: 4500, year: 2020, country: 'Germany' },
|
|
2047
|
+
{ make: 'Mercedes', model: 'C-Class', price: 55000, tax: 5500, year: 2021, country: 'Germany' },
|
|
2048
|
+
{ make: 'Nissan', model: 'Altima', price: 30000, tax: 3000, year: 2019, country: 'Japan' },
|
|
2049
|
+
{ make: 'Chevrolet', model: 'Malibu', price: 33000, tax: 3300, year: 2020, country: 'USA' },
|
|
2050
|
+
]
|
|
2051
|
+
|
|
2052
|
+
const gridOptions = {
|
|
2053
|
+
animateRows: true,
|
|
2054
|
+
rowSelection: { mode: 'singleRow', checkboxes: false, enableClickSelection: true },
|
|
2055
|
+
tooltipShowDelay: 100,
|
|
2056
|
+
domLayout: 'autoHeight',
|
|
2057
|
+
suppressCellFocus: true,
|
|
2058
|
+
}
|
|
2059
|
+
|
|
2060
|
+
const { OpsnowCommonDataGrid } = useCommonComponents();
|
|
2061
|
+
const { CommonConst } = useGlobalContext()
|
|
2062
|
+
// rowData와 status 상태를 관리
|
|
2063
|
+
const [rowData, setRowData] = useState([]) // 초기에는 빈 배열로 설정
|
|
2064
|
+
const [status, setStatus] = useState(CommonConst.GRID_STATUS.LOADING) // 초기에는 LOADING 상태
|
|
2065
|
+
|
|
2066
|
+
// 데이터 지연 설정을 위한 useEffect
|
|
2067
|
+
useEffect(() => {
|
|
2068
|
+
// 1초 후에 데이터를 설정
|
|
2069
|
+
const timeoutId = setTimeout(() => {
|
|
2070
|
+
setRowData(initialRowData)
|
|
2071
|
+
setStatus(CommonConst.GRID_STATUS.DATA_EXISTS)
|
|
2072
|
+
}, 1000)
|
|
2073
|
+
|
|
2074
|
+
// 컴포넌트 언마운트 시 타임아웃 클리어
|
|
2075
|
+
return () => {
|
|
2076
|
+
clearTimeout(timeoutId)
|
|
2077
|
+
}
|
|
2078
|
+
}, [])
|
|
2079
|
+
`,
|
|
2080
|
+
code: `<OpsnowCommonDataGrid
|
|
2081
|
+
key={status}
|
|
2082
|
+
columnDefs={nestedColumnDefs}
|
|
2083
|
+
rowData={rowData}
|
|
2084
|
+
status={status}
|
|
2085
|
+
gridOptions={gridOptions}
|
|
2086
|
+
usedSearch={true}
|
|
2087
|
+
langCd={i18n.getLocale()}
|
|
2088
|
+
/>`
|
|
2089
|
+
},
|
|
2090
|
+
{
|
|
2091
|
+
title: 'Error Status',
|
|
2092
|
+
description: 'Error Status 예제입니다.',
|
|
2093
|
+
code_props_usage: `
|
|
2094
|
+
import { useCommonComponents, useGlobalContext } from '@opsnow-common/opsnow-finops-common-ui-loader'
|
|
2095
|
+
import i18n from '@opsnow-common/opsnow-finops-common-i18n'
|
|
2096
|
+
|
|
2097
|
+
// 컬럼 정의
|
|
2098
|
+
const columnDefs = [
|
|
2099
|
+
{ headerName: 'Make', field: 'make' },
|
|
2100
|
+
{ headerName: 'Model', field: 'model' },
|
|
2101
|
+
{ headerName: 'Price', field: 'price' },
|
|
2102
|
+
]
|
|
2103
|
+
|
|
2104
|
+
const gridOptions = {
|
|
2105
|
+
animateRows: true,
|
|
2106
|
+
rowSelection: { mode: 'singleRow', checkboxes: false, enableClickSelection: true },
|
|
2107
|
+
tooltipShowDelay: 100,
|
|
2108
|
+
domLayout: 'autoHeight',
|
|
2109
|
+
suppressCellFocus: true,
|
|
2110
|
+
}
|
|
2111
|
+
|
|
2112
|
+
const { OpsnowCommonDataGrid } = useCommonComponents();
|
|
2113
|
+
const { CommonConst } = useGlobalContext()
|
|
2114
|
+
// errorStatus 상태를 관리
|
|
2115
|
+
const [errorStatus, setErrorStatus] = useState(CommonConst.GRID_STATUS.LOADING)
|
|
2116
|
+
|
|
2117
|
+
// 에러 상태 시뮬레이션을 위한 useEffect
|
|
2118
|
+
useEffect(() => {
|
|
2119
|
+
// 1초 후에 에러 상태로 설정
|
|
2120
|
+
const timeoutId = setTimeout(() => {
|
|
2121
|
+
setErrorStatus(CommonConst.GRID_STATUS.ERROR)
|
|
2122
|
+
}, 1000)
|
|
2123
|
+
|
|
2124
|
+
// 컴포넌트 언마운트 시 타임아웃 클리어
|
|
2125
|
+
return () => {
|
|
2126
|
+
clearTimeout(timeoutId)
|
|
2127
|
+
}
|
|
2128
|
+
}, [])
|
|
2129
|
+
`,
|
|
2130
|
+
code: `<OpsnowCommonDataGrid
|
|
2131
|
+
columnDefs={columnDefs}
|
|
2132
|
+
rowData={[]}
|
|
2133
|
+
status={errorStatus}
|
|
2134
|
+
gridOptions={gridOptions}
|
|
2135
|
+
textErrorTitle="데이터 로드 실패"
|
|
2136
|
+
textErrorDescription="데이터를 불러오는 중 오류가 발생했습니다."
|
|
2137
|
+
langCd={i18n.getLocale()}
|
|
2138
|
+
/>`
|
|
2139
|
+
},
|
|
2140
|
+
{
|
|
2141
|
+
title: 'Long Text with autoHeight',
|
|
2142
|
+
description: 'Long Text with autoHeight (긴 텍스트 자동 높이 조절) 예제입니다.',
|
|
2143
|
+
code_props_usage: `
|
|
2144
|
+
import { useCommonComponents, useGlobalContext } from '@opsnow-common/opsnow-finops-common-ui-loader'
|
|
2145
|
+
import i18n from '@opsnow-common/opsnow-finops-common-i18n'
|
|
2146
|
+
|
|
2147
|
+
// Long Text용 컬럼 정의
|
|
2148
|
+
const columnDefs = [
|
|
2149
|
+
{
|
|
2150
|
+
headerName: 'ID',
|
|
2151
|
+
field: 'id',
|
|
2152
|
+
flex: 1,
|
|
2153
|
+
minWidth: 80
|
|
2154
|
+
},
|
|
2155
|
+
{
|
|
2156
|
+
headerName: 'Description',
|
|
2157
|
+
field: 'description',
|
|
2158
|
+
flex: 3, // 가장 넓은 비율
|
|
2159
|
+
minWidth: 300,
|
|
2160
|
+
suppressSizeToFit: true, // 자동 리사이즈 방지
|
|
2161
|
+
cellStyle: {
|
|
2162
|
+
'align-items': 'center' // autoHeight 작동을 위해 필수
|
|
2163
|
+
}
|
|
2164
|
+
},
|
|
2165
|
+
{
|
|
2166
|
+
headerName: 'Status',
|
|
2167
|
+
field: 'status',
|
|
2168
|
+
flex: 1,
|
|
2169
|
+
minWidth: 120
|
|
2170
|
+
},
|
|
2171
|
+
]
|
|
2172
|
+
|
|
2173
|
+
// Long Text용 행 데이터
|
|
2174
|
+
const rowData = [
|
|
2175
|
+
{
|
|
2176
|
+
id: 1,
|
|
2177
|
+
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.',
|
|
2178
|
+
status: 'Active'
|
|
2179
|
+
},
|
|
2180
|
+
{
|
|
2181
|
+
id: 2,
|
|
2182
|
+
description: 'Short text',
|
|
2183
|
+
status: 'Inactive'
|
|
2184
|
+
},
|
|
2185
|
+
{
|
|
2186
|
+
id: 3,
|
|
2187
|
+
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.',
|
|
2188
|
+
status: 'Pending'
|
|
2189
|
+
},
|
|
2190
|
+
{
|
|
2191
|
+
id: 4,
|
|
2192
|
+
description: 'Another example of long text content that needs multiple lines',
|
|
2193
|
+
status: 'Active'
|
|
2194
|
+
},
|
|
2195
|
+
]
|
|
2196
|
+
|
|
2197
|
+
const gridOptions = {
|
|
2198
|
+
animateRows: true,
|
|
2199
|
+
rowSelection: { mode: 'singleRow', checkboxes: false, enableClickSelection: true },
|
|
2200
|
+
tooltipShowDelay: 100,
|
|
2201
|
+
domLayout: 'autoHeight',
|
|
2202
|
+
suppressCellFocus: true,
|
|
2203
|
+
}
|
|
2204
|
+
|
|
2205
|
+
const { OpsnowCommonDataGrid } = useCommonComponents();
|
|
2206
|
+
const { CommonConst } = useGlobalContext()
|
|
2207
|
+
`,
|
|
2208
|
+
code: `<OpsnowCommonDataGrid
|
|
2209
|
+
columnDefs={columnDefs}
|
|
2210
|
+
rowData={rowData}
|
|
2211
|
+
status={CommonConst.GRID_STATUS.DATA_EXISTS}
|
|
2212
|
+
gridOptions={gridOptions}
|
|
2213
|
+
langCd={i18n.getLocale()}
|
|
2214
|
+
/>`
|
|
2215
|
+
},
|
|
1888
2216
|
];
|
|
@@ -31,12 +31,13 @@ const gridOptionsSchema = z.object({
|
|
|
31
31
|
getRowId: z.string().optional().describe('행 ID 반환 함수(stringified)'),
|
|
32
32
|
suppressMenuHide: z.boolean().optional().describe('메뉴 숨김 억제'),
|
|
33
33
|
});
|
|
34
|
-
const columnDefsSchema = z.object({
|
|
34
|
+
const columnDefsSchema = z.lazy(() => z.object({
|
|
35
35
|
headerName: z.string().optional().describe('컬럼 헤더명'),
|
|
36
36
|
field: z.string().optional().describe('컬럼 데이터 필드명'),
|
|
37
37
|
colId: z.string().optional().describe('컬럼 고유 ID'),
|
|
38
|
+
groupId: z.string().optional().describe('컬럼 그룹 고유 ID'),
|
|
38
39
|
width: z.number().optional().describe('컬럼 너비'),
|
|
39
|
-
minWidth: z.number().
|
|
40
|
+
minWidth: z.number().describe('컬럼 최소 너비'),
|
|
40
41
|
maxWidth: z.number().optional().describe('컬럼 최대 너비'),
|
|
41
42
|
pinned: z.enum(['left', 'right']).optional().describe('컬럼 고정 위치'),
|
|
42
43
|
type: z.array(z.enum([
|
|
@@ -101,7 +102,8 @@ const columnDefsSchema = z.object({
|
|
|
101
102
|
filterParams: z.any().optional().describe('필터 파라미터'),
|
|
102
103
|
cellRendererSelector: z.string().optional().describe('셀 렌더러 선택 함수(stringified)'),
|
|
103
104
|
colSpan: z.string().optional().describe('컬럼 병합 함수(stringified)'),
|
|
104
|
-
|
|
105
|
+
children: z.array(z.lazy(() => columnDefsSchema)).optional().describe('하위 컬럼 정의 배열 (컬럼 그룹화)'),
|
|
106
|
+
}));
|
|
105
107
|
const defaultColDefSchema = z.object({
|
|
106
108
|
wrapText: z.boolean().optional().describe('텍스트 줄바꿈'),
|
|
107
109
|
autoHeight: z.boolean().optional().describe('자동 높이'),
|
|
@@ -124,7 +126,27 @@ export const DataGridSchema = z.object({
|
|
|
124
126
|
columnDefs: z.array(columnDefsSchema).describe("ag-grid 컬럼 정의").min(1),
|
|
125
127
|
defaultColDef: defaultColDefSchema.optional().describe('ag-grid 기본 컬럼 옵션'),
|
|
126
128
|
rowData: z.array(z.record(z.any())).optional().describe('그리드에 표시할 데이터'),
|
|
127
|
-
status: z.number().describe(
|
|
129
|
+
status: z.number().describe(`그리드 상태값: GRID.STATUS.LOADING(0), GRID.STATUS.DATA_EXISTS(1), GRID.STATUS.ERROR(9) 중 하나를 사용해야 합니다.
|
|
130
|
+
|
|
131
|
+
**필수 구현 패턴:**
|
|
132
|
+
반드시 useState로 상태를 관리하고, useEffect 등을 사용하여 데이터 로딩 로직을 구현해야 합니다.
|
|
133
|
+
사용자는 이 상태 제어 코드 안에 실제 비즈니스 로직(API 호출, 데이터 처리 등)을 추가할 수 있습니다.
|
|
134
|
+
|
|
135
|
+
예제:
|
|
136
|
+
const [status, setStatus] = useState(CommonConst.GRID_STATUS.LOADING)
|
|
137
|
+
const [rowData, setRowData] = useState([])
|
|
138
|
+
|
|
139
|
+
useEffect(() => {
|
|
140
|
+
// 여기에 실제 비즈니스 로직을 추가합니다 (예: API 호출)
|
|
141
|
+
fetchData()
|
|
142
|
+
.then(data => {
|
|
143
|
+
setRowData(data)
|
|
144
|
+
setStatus(CommonConst.GRID_STATUS.DATA_EXISTS)
|
|
145
|
+
})
|
|
146
|
+
.catch(error => {
|
|
147
|
+
setStatus(CommonConst.GRID_STATUS.ERROR)
|
|
148
|
+
})
|
|
149
|
+
}, [])`),
|
|
128
150
|
gridHeight: z.number().optional().describe('그리드 높이'),
|
|
129
151
|
setClass: z.string().optional().describe('그리드 클래스명'),
|
|
130
152
|
localRowClass: z.string().optional().describe('로컬 행 클래스명'),
|
|
@@ -139,7 +161,10 @@ export const DataGridSchema = z.object({
|
|
|
139
161
|
searchTextNoData: z.string().optional().describe('검색 결과 없음 텍스트'),
|
|
140
162
|
searchTextNoDataDescription: z.string().optional().describe('검색 결과 없음 설명'),
|
|
141
163
|
pagingFromServer: z.boolean().optional().describe('서버 페이징 사용 여부'),
|
|
142
|
-
pageSizeList: z.array(z.
|
|
164
|
+
pageSizeList: z.array(z.object({
|
|
165
|
+
label: z.string().describe('페이지 사이즈 라벨'),
|
|
166
|
+
value: z.number().describe('페이지 사이즈 값')
|
|
167
|
+
})).optional().describe('페이지 사이즈 리스트 (예: [{ label: "5", value: 5 }, { label: "10", value: 10 }])'),
|
|
143
168
|
placeholderText: z.string().optional().describe('플레이스홀더 텍스트'),
|
|
144
169
|
usedRowClass: z.boolean().optional().describe('행 클래스 사용 여부 (셀 클릭 이벤트 사용 시 필수: true)'),
|
|
145
170
|
usedSearch: z.boolean().optional().describe('검색창 사용 여부'),
|
|
@@ -179,12 +204,113 @@ export function createDataGridComponent() {
|
|
|
179
204
|
|
|
180
205
|
**이 데이터 그리드 컴포넌트는 ag-grid를 기반으로 구현되었습니다.**
|
|
181
206
|
**데이터 그리드 데이터, 컬럼 정의 등의 데이터가 제공되지 않을 경우 목업 데이터를 사용하세요.**
|
|
182
|
-
|
|
207
|
+
|
|
208
|
+
**중요: 긴 텍스트 컬럼 처리 (필수)**
|
|
209
|
+
컬럼 값이 길어서 줄바꿈이 필요한 경우, 반드시 다음을 따라야 합니다:
|
|
210
|
+
1. 모든 컬럼에 flex 속성 필수 (비율로 너비 분배, 예: flex: 1, flex: 3)
|
|
211
|
+
2. 긴 텍스트 컬럼에 suppressSizeToFit: true 필수
|
|
212
|
+
3. 긴 텍스트 컬럼에 cellStyle: { 'align-items': 'center' } 필수
|
|
213
|
+
4. gridOptions에 domLayout: 'autoHeight' 설정
|
|
214
|
+
|
|
215
|
+
예시:
|
|
216
|
+
const columnDefs = [
|
|
217
|
+
{ headerName: 'ID', field: 'id', flex: 1, minWidth: 80 },
|
|
218
|
+
{
|
|
219
|
+
headerName: 'Description',
|
|
220
|
+
field: 'description',
|
|
221
|
+
flex: 3,
|
|
222
|
+
minWidth: 300,
|
|
223
|
+
suppressSizeToFit: true,
|
|
224
|
+
cellStyle: { 'align-items': 'center' }
|
|
225
|
+
},
|
|
226
|
+
{ headerName: 'Status', field: 'status', flex: 1, minWidth: 120 }
|
|
227
|
+
];
|
|
228
|
+
|
|
183
229
|
**import:**
|
|
184
230
|
\`\`\`javascript
|
|
185
231
|
import { useCommonComponents, useGlobalContext } from '@opsnow-common/opsnow-finops-common-ui-loader';
|
|
186
232
|
const { OpsnowCommonDataGrid } = useCommonComponents();
|
|
187
|
-
const { CommonConst } = useGlobalContext()
|
|
233
|
+
const { CommonConst } = useGlobalContext();
|
|
234
|
+
\`\`\`
|
|
235
|
+
|
|
236
|
+
**중요: 상태 관리 패턴 (필수 구현)**
|
|
237
|
+
데이터 그리드를 구현할 때는 반드시 아래의 세 가지 상태(LOADING, DATA_EXISTS, ERROR)를 모두 처리하는 코드를 포함해야 합니다.
|
|
238
|
+
|
|
239
|
+
**절대 하지 말아야 할 것:**
|
|
240
|
+
- useState에 직접 데이터를 넣지 마세요: const [rowData] = useState([{...}]) ❌
|
|
241
|
+
- status를 바로 DATA_EXISTS로 설정하지 마세요: const [status] = useState(CommonConst.GRID_STATUS.DATA_EXISTS) ❌
|
|
242
|
+
- useEffect 없이 구현하지 마세요 ❌
|
|
243
|
+
|
|
244
|
+
**반드시 따라야 할 구현 패턴:**
|
|
245
|
+
1. status는 반드시 CommonConst.GRID_STATUS.LOADING으로 시작
|
|
246
|
+
2. rowData는 반드시 빈 배열 []로 시작
|
|
247
|
+
3. useEffect를 반드시 사용하여 데이터 로딩 시뮬레이션
|
|
248
|
+
4. .then()으로 성공 처리 → CommonConst.GRID_STATUS.DATA_EXISTS로 변경
|
|
249
|
+
5. .catch()로 에러 처리 → CommonConst.GRID_STATUS.ERROR로 변경
|
|
250
|
+
6. OpsnowCommonDataGrid에 textErrorTitle, textErrorDescription 속성 반드시 포함
|
|
251
|
+
|
|
252
|
+
사용자는 생성된 코드의 useEffect 내부에 실제 비즈니스 로직(API 호출, 데이터 변환 등)을 추가할 수 있습니다.
|
|
253
|
+
|
|
254
|
+
**기본 패턴 예제 (정확히 이 구조를 따라야 함):**
|
|
255
|
+
\`\`\`javascript
|
|
256
|
+
// ✅ 올바른 방법: LOADING으로 시작, 빈 배열로 시작
|
|
257
|
+
const [status, setStatus] = useState(CommonConst.GRID_STATUS.LOADING);
|
|
258
|
+
const [rowData, setRowData] = useState([]);
|
|
259
|
+
|
|
260
|
+
// ✅ 올바른 방법: useEffect에서 데이터 로딩
|
|
261
|
+
useEffect(() => {
|
|
262
|
+
// 사용자가 여기에 실제 API 호출 등 비즈니스 로직을 추가합니다
|
|
263
|
+
const fetchData = async () => {
|
|
264
|
+
try {
|
|
265
|
+
// 실제로는 여기에 API 호출을 넣습니다
|
|
266
|
+
// 예: const response = await fetch('/api/data');
|
|
267
|
+
// const data = await response.json();
|
|
268
|
+
|
|
269
|
+
// 데모를 위한 시뮬레이션
|
|
270
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
271
|
+
|
|
272
|
+
const mockData = [
|
|
273
|
+
{ make: 'Toyota', model: 'Celica', price: 35000 },
|
|
274
|
+
{ make: 'Ford', model: 'Mondeo', price: 32000 }
|
|
275
|
+
];
|
|
276
|
+
|
|
277
|
+
setRowData(mockData);
|
|
278
|
+
setStatus(CommonConst.GRID_STATUS.DATA_EXISTS);
|
|
279
|
+
} catch (error) {
|
|
280
|
+
console.error('Error loading data:', error);
|
|
281
|
+
setStatus(CommonConst.GRID_STATUS.ERROR);
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
fetchData();
|
|
286
|
+
}, []);
|
|
287
|
+
|
|
288
|
+
// gridOptions 정의 (권장)
|
|
289
|
+
const gridOptions = {
|
|
290
|
+
animateRows: true,
|
|
291
|
+
rowSelection: { mode: 'singleRow', checkboxes: false, enableClickSelection: true },
|
|
292
|
+
tooltipShowDelay: 100,
|
|
293
|
+
domLayout: 'autoHeight',
|
|
294
|
+
suppressCellFocus: true,
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
// ✅ 올바른 방법: 에러 관련 속성 포함
|
|
298
|
+
<OpsnowCommonDataGrid
|
|
299
|
+
columnDefs={columnDefs}
|
|
300
|
+
rowData={rowData}
|
|
301
|
+
status={status}
|
|
302
|
+
gridOptions={gridOptions}
|
|
303
|
+
pagination={true}
|
|
304
|
+
paginationPageSize={5}
|
|
305
|
+
pageSizeList={[
|
|
306
|
+
{ label: '5', value: 5 },
|
|
307
|
+
{ label: '10', value: 10 },
|
|
308
|
+
{ label: '20', value: 20 }
|
|
309
|
+
]}
|
|
310
|
+
textErrorTitle="데이터 로드 실패"
|
|
311
|
+
textErrorDescription="데이터를 불러오는 중 오류가 발생했습니다."
|
|
312
|
+
langCd={i18n.getLocale()}
|
|
313
|
+
/>
|
|
188
314
|
\`\`\``,
|
|
189
315
|
parameters: DataGridSchema,
|
|
190
316
|
handler: async (args) => {
|
package/build/index.js
CHANGED
|
@@ -25,8 +25,57 @@ import { createCommonExamplesComponent } from "./components/opsnow-common-exampl
|
|
|
25
25
|
// MCP 서버 생성
|
|
26
26
|
const server = new McpServer({
|
|
27
27
|
name: "opsnow-mcp-common-ui-server",
|
|
28
|
-
version: "1.0.0"
|
|
29
|
-
|
|
28
|
+
version: "1.0.0"
|
|
29
|
+
}, {
|
|
30
|
+
instructions: `OpsNow UI Components Usage Guidelines:
|
|
31
|
+
|
|
32
|
+
IMPORTANT: All components require React 18.x or higher environment.
|
|
33
|
+
Always include required import statements and i18n configuration when needed.
|
|
34
|
+
|
|
35
|
+
**CRITICAL: OpsnowCommonDataGrid Usage Rules**
|
|
36
|
+
|
|
37
|
+
Before implementing OpsnowCommonDataGrid, you MUST use getUIExamples tool with 'DataGrid' parameter first!
|
|
38
|
+
|
|
39
|
+
1. Grid Top Search/Button Area - You MUST use children or searchAddon slots:
|
|
40
|
+
|
|
41
|
+
[CORRECT] Using children slot:
|
|
42
|
+
<OpsnowCommonDataGrid usedSearch={true}>
|
|
43
|
+
<OpsnowCommonButton label="Add" iconName="Plus" />
|
|
44
|
+
</OpsnowCommonDataGrid>
|
|
45
|
+
|
|
46
|
+
[CORRECT] Using searchAddon slot:
|
|
47
|
+
<OpsnowCommonDataGrid
|
|
48
|
+
usedSearch={true}
|
|
49
|
+
searchAddon={<OpsnowCommonDatePicker />}
|
|
50
|
+
/>
|
|
51
|
+
|
|
52
|
+
[FORBIDDEN] DO NOT create separate Stack/Box outside the grid:
|
|
53
|
+
<Stack direction="row"> <- DO NOT DO THIS!
|
|
54
|
+
<OpsnowCommonButton label="Add" />
|
|
55
|
+
</Stack>
|
|
56
|
+
<OpsnowCommonDataGrid usedSearch={true} />
|
|
57
|
+
|
|
58
|
+
Reference example: "Insert Button Inside the Common Grid Search Section"
|
|
59
|
+
|
|
60
|
+
2. Grid Bottom Pagination - You MUST use built-in pagination prop:
|
|
61
|
+
|
|
62
|
+
[CORRECT] Using built-in pagination:
|
|
63
|
+
<OpsnowCommonDataGrid
|
|
64
|
+
pagination={true}
|
|
65
|
+
paginationPageSize={10}
|
|
66
|
+
pageSizeList={[
|
|
67
|
+
{ label: '10', value: 10 },
|
|
68
|
+
{ label: '20', value: 20 }
|
|
69
|
+
]}
|
|
70
|
+
/>
|
|
71
|
+
|
|
72
|
+
[FORBIDDEN] DO NOT use OpsnowCommonPagination component separately:
|
|
73
|
+
<OpsnowCommonDataGrid />
|
|
74
|
+
<OpsnowCommonPagination /> <- DO NOT DO THIS!
|
|
75
|
+
|
|
76
|
+
Reference example: "Search + Pagination"
|
|
77
|
+
|
|
78
|
+
WARNING: Violating these rules will cause the UI to malfunction.`
|
|
30
79
|
});
|
|
31
80
|
// 컴포넌트들 등록
|
|
32
81
|
const componentFactories = [
|