@opsnow-mcp/opsnow-mcp-common-ui-server 1.0.12 → 1.0.16
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/README.md +105 -0
- package/build/components/examples/opsnow-common-forms-examples-data.js +202 -0
- package/build/components/examples/opsnow-common-layout-examples-data.js +12 -0
- package/build/components/examples/opsnow-common-tab-examples-data.js +5 -0
- package/build/components/examples/opsnow-common-toast-popup-examples-data.js +1 -1
- package/build/components/opsnow-common-examples.js +5 -2
- package/build/components/opsnow-common-forms.js +148 -1
- package/build/components/opsnow-common-layout.js +70 -1
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -209,3 +209,108 @@ MCP 플랫폼의 설정 파일(예: `mcp.config.json` 또는 유사 파일)에
|
|
|
209
209
|
|
|
210
210
|
### 3. MCP 플랫폼에서 서버 인식 확인
|
|
211
211
|
MCP 플랫폼을 재시작하거나 서버 목록을 새로고침하면, `opsnow-mcp-common-ui-server`가 정상적으로 등록되어야 합니다.
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## Claude Code Skills 생성
|
|
216
|
+
|
|
217
|
+
MCP 서버를 Claude Code에서 사용할 수 있는 **정적 Skills**로 변환할 수 있습니다.
|
|
218
|
+
|
|
219
|
+
### MCP vs Skills 비교
|
|
220
|
+
|
|
221
|
+
| 구분 | MCP Server | Claude Code Skills |
|
|
222
|
+
|------|------------|-------------------|
|
|
223
|
+
| 실행 방식 | 런타임 (Node.js 프로세스) | 정적 문서 (마크다운) |
|
|
224
|
+
| 호출 방식 | Tool 호출 | `/skill-name` 슬래시 커맨드 |
|
|
225
|
+
| 배포 | npm 패키지 | Plugin Marketplace 또는 로컬 |
|
|
226
|
+
| 오프라인 | ❌ | ✅ |
|
|
227
|
+
|
|
228
|
+
### Skills 빌드 명령어
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
# Skills 빌드 (기존 파일 유지)
|
|
232
|
+
npm run build:skill
|
|
233
|
+
|
|
234
|
+
# Skills 클린 빌드 (기존 파일 삭제 후 새로 생성)
|
|
235
|
+
npm run build:skill:clean
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### 빌드 출력물
|
|
239
|
+
|
|
240
|
+
```
|
|
241
|
+
skills/plugins/opsnow-common-ui-skill/
|
|
242
|
+
├── .claude-plugin/
|
|
243
|
+
│ └── plugin.json # 플러그인 매니페스트
|
|
244
|
+
└── skills/opsnow-common-ui/
|
|
245
|
+
├── SKILL.md # 메인 스킬 정의 (컴포넌트 카탈로그)
|
|
246
|
+
├── references/ # Props 레퍼런스 (35개)
|
|
247
|
+
│ ├── button.md
|
|
248
|
+
│ ├── datagrid.md
|
|
249
|
+
│ └── ...
|
|
250
|
+
├── examples/ # 상세 예제 (40개 파일, 463개 예제)
|
|
251
|
+
│ ├── button.md
|
|
252
|
+
│ ├── datagrid.md
|
|
253
|
+
│ └── ...
|
|
254
|
+
└── components/ # Zod 스키마 복사본 (18개)
|
|
255
|
+
├── opsnow-common-button.ts
|
|
256
|
+
└── ...
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### 문서 구조 (Progressive Disclosure)
|
|
260
|
+
|
|
261
|
+
| 깊이 | 경로 | 용도 | 언제 참조? |
|
|
262
|
+
|------|------|------|-----------|
|
|
263
|
+
| 1 | `SKILL.md` | 컴포넌트 카탈로그, 개요 | 뭐가 있는지 파악할 때 |
|
|
264
|
+
| 2 | `references/*.md` | Props 테이블, 기본 사용법 | 컴포넌트 처음 사용할 때 |
|
|
265
|
+
| 3 | `examples/*.md` | 케이스별 상세 예제 | 특정 패턴 구현할 때 |
|
|
266
|
+
| 4 | `components/*.ts` | Zod 스키마, 타입 정의 | 타입/옵션 상세 확인할 때 |
|
|
267
|
+
|
|
268
|
+
### 로컬 테스트
|
|
269
|
+
|
|
270
|
+
생성된 Skills를 로컬에서 테스트하려면:
|
|
271
|
+
|
|
272
|
+
```bash
|
|
273
|
+
# 프로젝트 루트에서
|
|
274
|
+
cd skills/plugins/opsnow-common-ui-skill
|
|
275
|
+
|
|
276
|
+
# Claude Code에서 직접 테스트
|
|
277
|
+
claude
|
|
278
|
+
|
|
279
|
+
# 또는 skills 폴더를 Claude Code 설정에 추가
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### 새 컴포넌트 추가 시 수정 필요 사항
|
|
283
|
+
|
|
284
|
+
`scripts/build-skill.ts`에서 다음 5개 매핑을 업데이트해야 합니다:
|
|
285
|
+
|
|
286
|
+
1. **`CATEGORIES[]`** - 카탈로그 분류 (차트, 폼, 레이아웃 등)
|
|
287
|
+
2. **`COMPONENT_NAME_MAP{}`** - React 컴포넌트명 매핑
|
|
288
|
+
3. **`COMPONENT_PURPOSE{}`** - 용도 설명
|
|
289
|
+
4. **`SCHEMA_TO_COMPONENT{}`** - Zod 스키마 → 컴포넌트 매핑
|
|
290
|
+
5. **`INTERNAL_SCHEMAS`** - 제외할 내부 스키마
|
|
291
|
+
|
|
292
|
+
### MCP 소스 작성 규칙
|
|
293
|
+
|
|
294
|
+
Skills가 올바르게 생성되려면 MCP 소스 파일이 다음 규칙을 따라야 합니다:
|
|
295
|
+
|
|
296
|
+
**Zod 스키마** (`src/components/*.ts`):
|
|
297
|
+
```typescript
|
|
298
|
+
// .describe()로 Props 설명 필수
|
|
299
|
+
export const ButtonPropsSchema = z.object({
|
|
300
|
+
label: z.string().describe('버튼 라벨'),
|
|
301
|
+
size: z.enum(['large', 'medium', 'small']).describe('버튼 크기'),
|
|
302
|
+
}).describe('버튼 컴포넌트');
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
**예제 파일** (`src/components/examples/*-examples-data.ts`):
|
|
306
|
+
```typescript
|
|
307
|
+
// 파일명: opsnow-common-{component}-examples-data.ts
|
|
308
|
+
// Export 명명: {PascalCase}Examples
|
|
309
|
+
export const ButtonExamples: Example[] = [
|
|
310
|
+
{
|
|
311
|
+
title: '기본 버튼',
|
|
312
|
+
props: { label: '버튼', size: 'medium' },
|
|
313
|
+
code: `<OpsnowCommonButton label="버튼" size="medium" />`,
|
|
314
|
+
},
|
|
315
|
+
];
|
|
316
|
+
```
|
|
@@ -1346,6 +1346,208 @@ export const LinkExamples = [
|
|
|
1346
1346
|
description: 'Focusable Inherit Link 예제입니다.'
|
|
1347
1347
|
}
|
|
1348
1348
|
];
|
|
1349
|
+
export const TreeViewExamples = [
|
|
1350
|
+
{
|
|
1351
|
+
title: 'basic-treeview-with-mui-icons',
|
|
1352
|
+
code: `<OpsnowCommonTreeView
|
|
1353
|
+
data={[
|
|
1354
|
+
{
|
|
1355
|
+
id: 'c1',
|
|
1356
|
+
label: 'Settings',
|
|
1357
|
+
muiIconName: 'settings',
|
|
1358
|
+
children: [
|
|
1359
|
+
{
|
|
1360
|
+
id: 'c11',
|
|
1361
|
+
label: 'Profile',
|
|
1362
|
+
muiIconName: 'person',
|
|
1363
|
+
},
|
|
1364
|
+
{
|
|
1365
|
+
id: 'c12',
|
|
1366
|
+
label: 'Security',
|
|
1367
|
+
muiIconName: 'lock',
|
|
1368
|
+
},
|
|
1369
|
+
{
|
|
1370
|
+
id: 'c13',
|
|
1371
|
+
label: 'Notifications',
|
|
1372
|
+
muiIconName: 'notifications',
|
|
1373
|
+
},
|
|
1374
|
+
],
|
|
1375
|
+
},
|
|
1376
|
+
{
|
|
1377
|
+
id: 'c2',
|
|
1378
|
+
label: 'Help',
|
|
1379
|
+
muiIconName: 'help',
|
|
1380
|
+
children: [
|
|
1381
|
+
{
|
|
1382
|
+
id: 'c21',
|
|
1383
|
+
label: 'Documentation',
|
|
1384
|
+
muiIconName: 'menu_book',
|
|
1385
|
+
},
|
|
1386
|
+
{
|
|
1387
|
+
id: 'c22',
|
|
1388
|
+
label: 'Support',
|
|
1389
|
+
muiIconName: 'support_agent',
|
|
1390
|
+
},
|
|
1391
|
+
],
|
|
1392
|
+
},
|
|
1393
|
+
]}
|
|
1394
|
+
onNodeClick={handleNodeClick}
|
|
1395
|
+
selectedNodeId={selectedNode?.id}
|
|
1396
|
+
/>`,
|
|
1397
|
+
description: 'Material-UI 아이콘을 사용한 기본 TreeView 예제입니다.'
|
|
1398
|
+
},
|
|
1399
|
+
{
|
|
1400
|
+
title: 'treeview-no-search-no-total',
|
|
1401
|
+
code: `<OpsnowCommonTreeView
|
|
1402
|
+
data={treeData}
|
|
1403
|
+
searchable={false}
|
|
1404
|
+
showTotal={false}
|
|
1405
|
+
defaultExpandAll={true}
|
|
1406
|
+
onNodeClick={handleNodeClick}
|
|
1407
|
+
selectedNodeId={selectedNode?.id}
|
|
1408
|
+
/>`,
|
|
1409
|
+
description: '검색 기능과 Total 라벨을 숨긴 TreeView 예제입니다.'
|
|
1410
|
+
},
|
|
1411
|
+
{
|
|
1412
|
+
title: 'treeview-with-total-label',
|
|
1413
|
+
code: `<OpsnowCommonTreeView
|
|
1414
|
+
data={treeData}
|
|
1415
|
+
totalLabel="All Items"
|
|
1416
|
+
totalIconName="FolderOpen"
|
|
1417
|
+
onTotalClick={handleTotalClick}
|
|
1418
|
+
onNodeClick={handleNodeClick}
|
|
1419
|
+
selectedNodeId={selectedNode?.id}
|
|
1420
|
+
/>`,
|
|
1421
|
+
description: 'Total 라벨과 아이콘이 있는 TreeView 예제입니다.'
|
|
1422
|
+
},
|
|
1423
|
+
{
|
|
1424
|
+
title: 'treeview-with-render-content',
|
|
1425
|
+
code: `<OpsnowCommonTreeView
|
|
1426
|
+
data={treeData}
|
|
1427
|
+
renderRightContent={(node) => (
|
|
1428
|
+
<Chip label={node.count || 0} size="small" />
|
|
1429
|
+
)}
|
|
1430
|
+
onNodeClick={handleNodeClick}
|
|
1431
|
+
selectedNodeId={selectedNode?.id}
|
|
1432
|
+
/>`,
|
|
1433
|
+
description: '각 노드 우측에 커스텀 콘텐츠를 렌더링하는 TreeView 예제입니다.'
|
|
1434
|
+
},
|
|
1435
|
+
{
|
|
1436
|
+
title: 'treeview-expanded-by-default',
|
|
1437
|
+
code: `<OpsnowCommonTreeView
|
|
1438
|
+
data={treeData}
|
|
1439
|
+
defaultExpandAll={true}
|
|
1440
|
+
searchable={true}
|
|
1441
|
+
onNodeClick={handleNodeClick}
|
|
1442
|
+
selectedNodeId={selectedNode?.id}
|
|
1443
|
+
/>`,
|
|
1444
|
+
description: '기본적으로 모든 노드가 확장된 TreeView 예제입니다.'
|
|
1445
|
+
},
|
|
1446
|
+
{
|
|
1447
|
+
title: 'treeview-cloud-providers',
|
|
1448
|
+
code: `<OpsnowCommonTreeView
|
|
1449
|
+
data={[
|
|
1450
|
+
{
|
|
1451
|
+
id: 'aws',
|
|
1452
|
+
label: 'AWS',
|
|
1453
|
+
muiIconName: 'cloud',
|
|
1454
|
+
children: [
|
|
1455
|
+
{ id: 'ec2', label: 'EC2', muiIconName: 'storage' },
|
|
1456
|
+
{ id: 's3', label: 'S3', muiIconName: 'folder' },
|
|
1457
|
+
{ id: 'rds', label: 'RDS', muiIconName: 'database' },
|
|
1458
|
+
],
|
|
1459
|
+
},
|
|
1460
|
+
{
|
|
1461
|
+
id: 'azure',
|
|
1462
|
+
label: 'Azure',
|
|
1463
|
+
muiIconName: 'cloud',
|
|
1464
|
+
children: [
|
|
1465
|
+
{ id: 'vm', label: 'Virtual Machines', muiIconName: 'computer' },
|
|
1466
|
+
{ id: 'blob', label: 'Blob Storage', muiIconName: 'folder' },
|
|
1467
|
+
],
|
|
1468
|
+
},
|
|
1469
|
+
{
|
|
1470
|
+
id: 'gcp',
|
|
1471
|
+
label: 'GCP',
|
|
1472
|
+
muiIconName: 'cloud',
|
|
1473
|
+
children: [
|
|
1474
|
+
{ id: 'compute', label: 'Compute Engine', muiIconName: 'dns' },
|
|
1475
|
+
{ id: 'gcs', label: 'Cloud Storage', muiIconName: 'cloud_upload' },
|
|
1476
|
+
],
|
|
1477
|
+
},
|
|
1478
|
+
]}
|
|
1479
|
+
totalLabel="All Cloud Providers"
|
|
1480
|
+
searchable={true}
|
|
1481
|
+
onNodeClick={handleNodeClick}
|
|
1482
|
+
selectedNodeId={selectedNode?.id}
|
|
1483
|
+
/>`,
|
|
1484
|
+
description: '클라우드 프로바이더 트리 구조 예제입니다.'
|
|
1485
|
+
},
|
|
1486
|
+
{
|
|
1487
|
+
title: 'treeview-with-checkbox-multiselect',
|
|
1488
|
+
code: `<OpsnowCommonTreeView
|
|
1489
|
+
data={sampleTreeData}
|
|
1490
|
+
checkboxSelection={true}
|
|
1491
|
+
multiSelect={true}
|
|
1492
|
+
onCheckedItemsChange={handleMultiCheckedItemsChange}
|
|
1493
|
+
defaultCheckedItems={multiCheckedItems}
|
|
1494
|
+
showTotal={true}
|
|
1495
|
+
totalLabel="Total"
|
|
1496
|
+
totalIconName="Building"
|
|
1497
|
+
searchable={false}
|
|
1498
|
+
defaultExpandAll={true}
|
|
1499
|
+
/>`,
|
|
1500
|
+
description: '체크박스와 다중 선택 기능이 있는 TreeView 예제입니다.'
|
|
1501
|
+
},
|
|
1502
|
+
{
|
|
1503
|
+
title: 'treeview-checkbox-with-custom-content',
|
|
1504
|
+
code: `<OpsnowCommonTreeView
|
|
1505
|
+
data={sampleTreeData}
|
|
1506
|
+
checkboxSelection={true}
|
|
1507
|
+
multiSelect={true}
|
|
1508
|
+
onCheckedItemsChange={handleMultiCheckedItemsChange}
|
|
1509
|
+
defaultCheckedItems={multiCheckedItems}
|
|
1510
|
+
showTotal={true}
|
|
1511
|
+
totalLabel="Total"
|
|
1512
|
+
totalIconName="Building"
|
|
1513
|
+
onTotalClick={handleTotalClick}
|
|
1514
|
+
renderTotalRightContent={() => (
|
|
1515
|
+
<OpsnowCommonChip
|
|
1516
|
+
label={\`\${String(sampleTreeData.length)} BU\`}
|
|
1517
|
+
size="small"
|
|
1518
|
+
sx={{
|
|
1519
|
+
backgroundColor: 'white',
|
|
1520
|
+
border: '1px solid',
|
|
1521
|
+
}}
|
|
1522
|
+
/>
|
|
1523
|
+
)}
|
|
1524
|
+
searchable={false}
|
|
1525
|
+
defaultExpandAll={true}
|
|
1526
|
+
renderRightContent={(node) => (
|
|
1527
|
+
<OpsnowCommonChip
|
|
1528
|
+
label={
|
|
1529
|
+
node.children && node.children.length > 0
|
|
1530
|
+
? \`\${String(node.children.length)} Teams\`
|
|
1531
|
+
: String(node.data?.memberCount ?? '0')
|
|
1532
|
+
}
|
|
1533
|
+
size="small"
|
|
1534
|
+
sx={{
|
|
1535
|
+
backgroundColor: 'white',
|
|
1536
|
+
border: '1px solid',
|
|
1537
|
+
}}
|
|
1538
|
+
/>
|
|
1539
|
+
)}
|
|
1540
|
+
/>`,
|
|
1541
|
+
description: '체크박스, 커스텀 콘텐츠, Chip이 함께 사용된 TreeView 예제입니다.'
|
|
1542
|
+
}
|
|
1543
|
+
];
|
|
1544
|
+
export const InsightCardExamples = [
|
|
1545
|
+
{
|
|
1546
|
+
title: 'basic-insight-card-with-loading',
|
|
1547
|
+
code: `<Box sx={{ mt: 2 }}><OpsnowCommonInsightCard loading={loading} loadingStage={loadingStage} insightText={insightText} title="Opsnow Insight AI" subtitle="AI-powered cost analysis and recommendations" /></Box>`,
|
|
1548
|
+
description: '로딩 상태와 AI insight 텍스트를 표시하는 InsightCard 예제입니다.'
|
|
1549
|
+
}
|
|
1550
|
+
];
|
|
1349
1551
|
export const ToggleButtonExamples = [
|
|
1350
1552
|
{
|
|
1351
1553
|
title: 'large-toggle-group',
|
|
@@ -57,4 +57,16 @@ export const GridLayoutExamples = [
|
|
|
57
57
|
</Grid>
|
|
58
58
|
`,
|
|
59
59
|
},
|
|
60
|
+
{
|
|
61
|
+
title: 'Tile with Checkbox UI',
|
|
62
|
+
description: 'Tile 내부에 체크박스와 텍스트가 포함된 2열 레이아웃 예제입니다.',
|
|
63
|
+
code: `<Grid container spacing={3} sx={{ marginTop: '20px' }}><Grid item xs={12} md={6} sx={{ display: 'flex' }}><OpsnowCommonTile sx={{ width: '100%' }}><OpsnowCommonTile.Content><Stack direction="row" alignItems="flex-start" spacing={2}><OpsnowCommonCheckbox name="organizationScope" checked={checkboxStates.organizationScope} onChange={handleCheckboxChange} color="primary" /><Stack spacing={1}><OpsnowCommonTypography variant="body1" fontWeight={600}>Use Organization Scope(Cost Allocation)</OpsnowCommonTypography><OpsnowCommonTypography variant="body2" color="textSecondary">The same scope defined in Organization Settings is used as the baseline for Budget and KPI tracking.</OpsnowCommonTypography></Stack></Stack></OpsnowCommonTile.Content></OpsnowCommonTile></Grid><Grid item xs={12} md={6} sx={{ display: 'flex' }}><OpsnowCommonTile sx={{ width: '100%' }}><OpsnowCommonTile.Content><Stack direction="row" alignItems="flex-start" spacing={2}><OpsnowCommonCheckbox name="customizeScope" checked={checkboxStates.customizeScope} onChange={handleCheckboxChange} color="primary" /><Stack spacing={1}><OpsnowCommonTypography variant="body1" fontWeight={600}>Customize Scope</OpsnowCommonTypography><OpsnowCommonTypography variant="body2" color="textSecondary">Apply only to selected dimensions, such as specific services (e.g., EC2)</OpsnowCommonTypography></Stack></Stack></OpsnowCommonTile.Content></OpsnowCommonTile></Grid></Grid>`,
|
|
64
|
+
},
|
|
65
|
+
];
|
|
66
|
+
export const MegaFilterExamples = [
|
|
67
|
+
{
|
|
68
|
+
title: 'basic-megafilter-with-vendors-daterange',
|
|
69
|
+
description: '벤더 선택, 날짜 범위, 필터 기능이 포함된 MegaFilter 예제입니다.',
|
|
70
|
+
code: `<OpsnowCommonMegaFilter filters={filters} vendors={vendors} selectedVendor={selectedVendor} onVendorChange={handleVendorChange} onFilterChange={handleFilterChange} selectedDateRange={selectedDateRange} onDateRangeChange={handleDateRangeChange} locale="ko" title="필터" clearAllText="전체 초기화" applyText="적용" cancelText="취소" />`,
|
|
71
|
+
},
|
|
60
72
|
];
|
|
@@ -79,4 +79,9 @@ export const TabExamples = [
|
|
|
79
79
|
code: `<OpsnowCommonTab value = {tabIndex} items={[{ label: '탭1', iconName: 'LogoAws', content: <div>탭1 내용</div> }, { label: '탭2', iconName: 'LogoAzure', content: <div>탭2 내용</div>}]} />`,
|
|
80
80
|
description: "탭 선택이 tabIndex라는 state 변수에 따라 변경되는 상태로 탭이 2개이고 각 탭에 아이콘과 내용이 포함된 예제입니다.",
|
|
81
81
|
},
|
|
82
|
+
{
|
|
83
|
+
title: "2-depth-button-tab-ui",
|
|
84
|
+
code: `<OpsnowCommonTab value={activeTab} onChange={handleTabChange} items={[{ label: 'Overview', content: (<div style={{ marginTop: '20px' }}><p>Overview content</p></div>) }, { label: 'Explorer', content: (<div style={{ marginTop: '20px' }}>{buttons.map((button) => (<OpsnowCommonButton key={button.value} label={button.label} onClick={() => handleButtonClick(button.value)} variant={selectedButton === button.value ? 'contained' : 'outlined'} style={{ marginRight: '10px', marginBottom: '10px' }} />))}{selectedButton && (<div style={{ marginTop: '20px' }}><p>Selected: {selectedButton}</p></div>)}</div>) }]} />`,
|
|
85
|
+
description: "2개의 탭(Overview, Explorer)이 있고, Explorer 탭에 동적 버튼 그룹이 포함된 2-Depth Button Tab UI 예제입니다.",
|
|
86
|
+
},
|
|
82
87
|
];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ButtonExamples, BadgeExamples, TextareaExamples, SwitchExamples, ChipExamples, TextFieldExamples, AvatarExamples, CheckboxExamples, RadioGroupExamples, CollapseExamples, SliderExamples, LinkExamples, ToggleButtonExamples } from "./examples/opsnow-common-forms-examples-data.js";
|
|
1
|
+
import { ButtonExamples, BadgeExamples, TextareaExamples, SwitchExamples, ChipExamples, TextFieldExamples, AvatarExamples, CheckboxExamples, RadioGroupExamples, CollapseExamples, SliderExamples, LinkExamples, TreeViewExamples, InsightCardExamples, ToggleButtonExamples } from "./examples/opsnow-common-forms-examples-data.js";
|
|
2
2
|
import { IconExamples, MuiIconExamples } from "./examples/opsnow-common-icons-examples-data.js";
|
|
3
3
|
import { CalendarExamples } from "./examples/opsnow-common-calendar-examples-data.js";
|
|
4
4
|
import { GaugeChartExamples, PieChartExamples, BarChartExamples, LineChartExamples, StackChartExamples, XyMultiChartExamples } from "./examples/opsnow-common-chart-examples-data.js";
|
|
@@ -14,7 +14,7 @@ import { PaginationExamples } from "./examples/opsnow-common-pagination-examples
|
|
|
14
14
|
import { DataStatusExamples } from "./examples/opsnow-common-data-status-examples-data.js";
|
|
15
15
|
import { ProgressExamples } from "./examples/opsnow-common-progress-examples-data.js";
|
|
16
16
|
import { TabExamples } from "./examples/opsnow-common-tab-examples-data.js";
|
|
17
|
-
import { GridLayoutExamples } from "./examples/opsnow-common-layout-examples-data.js";
|
|
17
|
+
import { GridLayoutExamples, MegaFilterExamples } from "./examples/opsnow-common-layout-examples-data.js";
|
|
18
18
|
import { TypographyExamples } from "./examples/opsnow-common-typography-examples-data.js";
|
|
19
19
|
import { AutocompleteExamples } from "./examples/opsnow-common-select-examples-data.js";
|
|
20
20
|
import { z } from "zod";
|
|
@@ -31,6 +31,8 @@ const EXAMPLES_MAP = {
|
|
|
31
31
|
Collapse: CollapseExamples,
|
|
32
32
|
Slider: SliderExamples,
|
|
33
33
|
Link: LinkExamples,
|
|
34
|
+
TreeView: TreeViewExamples,
|
|
35
|
+
InsightCard: InsightCardExamples,
|
|
34
36
|
ToggleButtonGroup: ToggleButtonExamples,
|
|
35
37
|
Icon: IconExamples,
|
|
36
38
|
MuiIcon: MuiIconExamples,
|
|
@@ -54,6 +56,7 @@ const EXAMPLES_MAP = {
|
|
|
54
56
|
Progress: ProgressExamples,
|
|
55
57
|
Tab: TabExamples,
|
|
56
58
|
GridLayout: GridLayoutExamples,
|
|
59
|
+
MegaFilter: MegaFilterExamples,
|
|
57
60
|
Typography: TypographyExamples,
|
|
58
61
|
Autocomplete: AutocompleteExamples,
|
|
59
62
|
};
|
|
@@ -204,6 +204,55 @@ export const ToggleButtonSchema = z.object({
|
|
|
204
204
|
disabled: z.boolean().optional().describe('비활성화 여부'),
|
|
205
205
|
})).describe('토글 버튼 목록'),
|
|
206
206
|
});
|
|
207
|
+
// TreeView 컴포넌트 관련 스키마 정의
|
|
208
|
+
export const TreeViewSchema = z.object({
|
|
209
|
+
data: z.string().describe(`트리 데이터 배열 변수명 (예: sampleTreeData)
|
|
210
|
+
|
|
211
|
+
트리 데이터 구조 예시:
|
|
212
|
+
- id: 노드 고유 ID
|
|
213
|
+
- label: 노드 라벨 텍스트
|
|
214
|
+
- muiIconName: Material-UI 아이콘 이름 (예: 'settings', 'person', 'lock' 등)
|
|
215
|
+
- children: 자식 노드 배열 (선택사항)
|
|
216
|
+
- data: 노드에 추가 데이터 (선택사항, 예: { memberCount: 5 })
|
|
217
|
+
|
|
218
|
+
예시 데이터:
|
|
219
|
+
[
|
|
220
|
+
{
|
|
221
|
+
id: 'c1',
|
|
222
|
+
label: 'Settings',
|
|
223
|
+
muiIconName: 'settings',
|
|
224
|
+
children: [
|
|
225
|
+
{ id: 'c11', label: 'Profile', muiIconName: 'person' },
|
|
226
|
+
{ id: 'c12', label: 'Security', muiIconName: 'lock' }
|
|
227
|
+
]
|
|
228
|
+
}
|
|
229
|
+
]`),
|
|
230
|
+
totalLabel: z.string().optional().describe("최상단 Total 라벨 텍스트"),
|
|
231
|
+
totalIconName: z.enum(Object.keys(IconNamesMap)).optional().describe(generateIconDescription({ format: 'nested' })),
|
|
232
|
+
showTotal: z.boolean().optional().describe("Total 라벨 표시 여부 (기본값: true)"),
|
|
233
|
+
searchable: z.boolean().optional().describe("검색 기능 활성화 여부 (기본값: true)"),
|
|
234
|
+
defaultExpandAll: z.boolean().optional().describe("기본 전체 확장 여부 (기본값: false)"),
|
|
235
|
+
checkboxSelection: z.boolean().optional().describe("체크박스 선택 모드 활성화"),
|
|
236
|
+
multiSelect: z.boolean().optional().describe("다중 선택 허용 여부"),
|
|
237
|
+
onCheckedItemsChange: z.string().optional().describe("체크된 항목 변경 이벤트 핸들러 (예: handleMultiCheckedItemsChange)"),
|
|
238
|
+
defaultCheckedItems: z.string().optional().describe("기본 체크된 항목 배열 상태 변수명 (예: multiCheckedItems)"),
|
|
239
|
+
onTotalClick: z.string().optional().describe("Total 클릭 이벤트 핸들러 함수명 (예: handleTotalClick)"),
|
|
240
|
+
renderTotalRightContent: z.string().optional().describe("Total 우측에 표시할 JSX 코드 문자열 (함수 형태)"),
|
|
241
|
+
renderRightContent: z.string().optional().describe("각 노드 우측에 표시할 JSX 코드 문자열 (함수 형태, node 파라미터 사용)"),
|
|
242
|
+
onNodeClick: z.string().optional().describe("노드 클릭 이벤트 핸들러 함수명 (예: handleNodeClick)"),
|
|
243
|
+
selectedNodeId: z.string().optional().describe("선택된 노드 ID 상태 변수명 (예: selectedNode?.id)"),
|
|
244
|
+
});
|
|
245
|
+
// InsightCard 컴포넌트 관련 스키마 정의
|
|
246
|
+
export const InsightCardSchema = z.object({
|
|
247
|
+
loading: z.boolean().optional().describe("로딩 상태 여부 (기본값: false)"),
|
|
248
|
+
loadingStage: z.enum(["1", "2", "3"]).optional().describe("현재 로딩 단계 (1, 2, 3 중 하나, 기본값: 1)"),
|
|
249
|
+
loadingStagesMessages: z.string().optional().describe("커스텀 로딩 메시지 배열 변수명 (예: loadingMessages)"),
|
|
250
|
+
insightText: z.string().optional().describe("AI insight 텍스트 내용"),
|
|
251
|
+
title: z.string().optional().describe("타일 제목 (기본값: 'Opsnow Insight AI')"),
|
|
252
|
+
subtitle: z.string().optional().describe("타일 부제목 (기본값: 'AI-powered analysis and recommendations')"),
|
|
253
|
+
onDownload: z.string().optional().describe("커스텀 다운로드 콜백 함수명 (예: handleDownload)"),
|
|
254
|
+
sx: z.string().optional().describe("MUI sx 스타일 객체(JSX/문자열)"),
|
|
255
|
+
});
|
|
207
256
|
// Forms 컴포넌트 함수 - 배열 반환
|
|
208
257
|
export function createFormsComponent() {
|
|
209
258
|
return [
|
|
@@ -749,7 +798,7 @@ export function createFormsComponent() {
|
|
|
749
798
|
{
|
|
750
799
|
name: "createToggleButton",
|
|
751
800
|
description: `ToggleButtonGroup 컴포넌트 - 다양한 스타일, 아이콘, 단일/다중 선택 지원
|
|
752
|
-
|
|
801
|
+
|
|
753
802
|
- 버튼별 아이콘, 라벨, 아이콘 위치, 아이콘만 표시 등 다양한 옵션 지원
|
|
754
803
|
- exclusive: true로 단일 선택, false 또는 미설정 시 다중 선택
|
|
755
804
|
- theme, disabled 등 다양한 스타일 속성 지원
|
|
@@ -807,6 +856,104 @@ export function createFormsComponent() {
|
|
|
807
856
|
]
|
|
808
857
|
};
|
|
809
858
|
}
|
|
859
|
+
},
|
|
860
|
+
{
|
|
861
|
+
name: "createTreeView",
|
|
862
|
+
description: `TreeView 컴포넌트 - 계층 구조 데이터를 트리 형태로 표시
|
|
863
|
+
|
|
864
|
+
- data: 트리 데이터 배열 변수
|
|
865
|
+
- totalLabel, totalIconName: 최상단 Total 표시 옵션
|
|
866
|
+
- renderTotalRightContent, renderRightContent: 각 노드의 우측에 커스텀 콘텐츠 렌더링
|
|
867
|
+
- onTotalClick, onNodeClick: 클릭 이벤트 핸들러
|
|
868
|
+
- selectedNodeId: 선택된 노드 ID
|
|
869
|
+
|
|
870
|
+
**import:**
|
|
871
|
+
\`\`\`javascript
|
|
872
|
+
import { useCommonComponents } from '@opsnow-common/opsnow-finops-common-ui-loader';
|
|
873
|
+
const { OpsnowCommonTreeView } = useCommonComponents();
|
|
874
|
+
\`\`\``,
|
|
875
|
+
parameters: TreeViewSchema,
|
|
876
|
+
handler: async (args) => {
|
|
877
|
+
const props = [];
|
|
878
|
+
if (args.data)
|
|
879
|
+
props.push(`data={${args.data}}`);
|
|
880
|
+
if (args.totalLabel)
|
|
881
|
+
props.push(`totalLabel=\"${args.totalLabel}\"`);
|
|
882
|
+
if (args.totalIconName)
|
|
883
|
+
props.push(`totalIconName=\"${args.totalIconName}\"`);
|
|
884
|
+
if (args.showTotal !== undefined)
|
|
885
|
+
props.push(`showTotal={${args.showTotal}}`);
|
|
886
|
+
if (args.searchable !== undefined)
|
|
887
|
+
props.push(`searchable={${args.searchable}}`);
|
|
888
|
+
if (args.defaultExpandAll !== undefined)
|
|
889
|
+
props.push(`defaultExpandAll={${args.defaultExpandAll}}`);
|
|
890
|
+
if (args.checkboxSelection !== undefined)
|
|
891
|
+
props.push(`checkboxSelection={${args.checkboxSelection}}`);
|
|
892
|
+
if (args.multiSelect !== undefined)
|
|
893
|
+
props.push(`multiSelect={${args.multiSelect}}`);
|
|
894
|
+
if (args.onCheckedItemsChange)
|
|
895
|
+
props.push(`onCheckedItemsChange={${args.onCheckedItemsChange}}`);
|
|
896
|
+
if (args.defaultCheckedItems)
|
|
897
|
+
props.push(`defaultCheckedItems={${args.defaultCheckedItems}}`);
|
|
898
|
+
if (args.onTotalClick)
|
|
899
|
+
props.push(`onTotalClick={${args.onTotalClick}}`);
|
|
900
|
+
if (args.renderTotalRightContent)
|
|
901
|
+
props.push(`renderTotalRightContent={${args.renderTotalRightContent}}`);
|
|
902
|
+
if (args.renderRightContent)
|
|
903
|
+
props.push(`renderRightContent={${args.renderRightContent}}`);
|
|
904
|
+
if (args.onNodeClick)
|
|
905
|
+
props.push(`onNodeClick={${args.onNodeClick}}`);
|
|
906
|
+
if (args.selectedNodeId)
|
|
907
|
+
props.push(`selectedNodeId={${args.selectedNodeId}}`);
|
|
908
|
+
const code = `<OpsnowCommonTreeView\n ${props.join('\n ')}\n/>`;
|
|
909
|
+
return {
|
|
910
|
+
content: [
|
|
911
|
+
{
|
|
912
|
+
type: "text",
|
|
913
|
+
text: `\`\`\`jsx\n${code}\n\`\`\``
|
|
914
|
+
}
|
|
915
|
+
]
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
},
|
|
919
|
+
{
|
|
920
|
+
name: "createInsightCard",
|
|
921
|
+
description: `InsightCard 컴포넌트 - AI 인사이트, 로딩 상태 등 지원
|
|
922
|
+
|
|
923
|
+
**import:**
|
|
924
|
+
\`\`\`javascript
|
|
925
|
+
import { useCommonComponents } from '@opsnow-common/opsnow-finops-common-ui-loader';
|
|
926
|
+
const { OpsnowCommonInsightCard } = useCommonComponents();
|
|
927
|
+
\`\`\``,
|
|
928
|
+
parameters: InsightCardSchema,
|
|
929
|
+
handler: async (args) => {
|
|
930
|
+
const props = [];
|
|
931
|
+
if (args.loading !== undefined)
|
|
932
|
+
props.push(`loading={${args.loading}}`);
|
|
933
|
+
if (args.loadingStage !== undefined)
|
|
934
|
+
props.push(`loadingStage={${args.loadingStage}}`);
|
|
935
|
+
if (args.loadingStagesMessages)
|
|
936
|
+
props.push(`loadingStagesMessages={${args.loadingStagesMessages}}`);
|
|
937
|
+
if (args.insightText)
|
|
938
|
+
props.push(`insightText="${args.insightText}"`);
|
|
939
|
+
if (args.title)
|
|
940
|
+
props.push(`title="${args.title}"`);
|
|
941
|
+
if (args.subtitle)
|
|
942
|
+
props.push(`subtitle="${args.subtitle}"`);
|
|
943
|
+
if (args.onDownload)
|
|
944
|
+
props.push(`onDownload={${args.onDownload}}`);
|
|
945
|
+
if (args.sx)
|
|
946
|
+
props.push(`sx={${args.sx}}`);
|
|
947
|
+
const code = `<OpsnowCommonInsightCard\n ${props.join('\n ')}\n/>`;
|
|
948
|
+
return {
|
|
949
|
+
content: [
|
|
950
|
+
{
|
|
951
|
+
type: "text",
|
|
952
|
+
text: `\`\`\`jsx\n${code}\n\`\`\``
|
|
953
|
+
}
|
|
954
|
+
]
|
|
955
|
+
};
|
|
956
|
+
}
|
|
810
957
|
}
|
|
811
958
|
];
|
|
812
959
|
}
|
|
@@ -18,12 +18,29 @@ const GridLayoutSchema = z.object({
|
|
|
18
18
|
spacing: z.number().optional().describe("Grid 간격 (MUI spacing 단위)"),
|
|
19
19
|
tileProps: z.record(z.any()).optional().describe("OpsnowCommonTile에 전달할 추가 props"),
|
|
20
20
|
});
|
|
21
|
+
const MegaFilterSchema = z.object({
|
|
22
|
+
filters: z.string().describe("필터 배열 변수명"),
|
|
23
|
+
vendors: z.string().optional().describe("벤더 옵션 배열 변수명"),
|
|
24
|
+
selectedVendor: z.string().optional().describe("선택된 벤더 상태 변수명"),
|
|
25
|
+
onVendorChange: z.string().optional().describe("벤더 변경 이벤트 핸들러"),
|
|
26
|
+
onFilterChange: z.string().optional().describe("필터 변경 이벤트 핸들러"),
|
|
27
|
+
selectedDateRange: z.string().optional().describe("선택된 날짜 범위 상태 변수명"),
|
|
28
|
+
onDateRangeChange: z.string().optional().describe("날짜 범위 변경 이벤트 핸들러"),
|
|
29
|
+
locale: z.enum(["en", "ko"]).optional().describe("로케일 설정"),
|
|
30
|
+
title: z.string().optional().describe("필터 제목"),
|
|
31
|
+
clearAllText: z.string().optional().describe("전체 초기화 버튼 텍스트"),
|
|
32
|
+
clearText: z.string().optional().describe("개별 초기화 버튼 텍스트"),
|
|
33
|
+
applyText: z.string().optional().describe("적용 버튼 텍스트"),
|
|
34
|
+
cancelText: z.string().optional().describe("취소 버튼 텍스트"),
|
|
35
|
+
searchPlaceholder: z.string().optional().describe("검색 입력 placeholder"),
|
|
36
|
+
noItemsText: z.string().optional().describe("검색 결과 없음 텍스트"),
|
|
37
|
+
});
|
|
21
38
|
export function createGridLayoutComponent() {
|
|
22
39
|
return [
|
|
23
40
|
{
|
|
24
41
|
name: "createGridLayout",
|
|
25
42
|
description: `Grid 및 Box 기반 레이아웃 생성
|
|
26
|
-
|
|
43
|
+
|
|
27
44
|
**import:**
|
|
28
45
|
\`\`\`jsx
|
|
29
46
|
import { Box, Grid } from '@mui/material';
|
|
@@ -64,6 +81,58 @@ export function createGridLayoutComponent() {
|
|
|
64
81
|
]
|
|
65
82
|
};
|
|
66
83
|
},
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
name: "createMegaFilter",
|
|
87
|
+
description: `MegaFilter 컴포넌트 - 다양한 필터, 벤더 선택, 날짜 범위 등 지원
|
|
88
|
+
|
|
89
|
+
**import:**
|
|
90
|
+
\`\`\`javascript
|
|
91
|
+
import { OpsnowCommonMegaFilter } from '@opsnow-common/opsnow-common-layout';
|
|
92
|
+
\`\`\``,
|
|
93
|
+
parameters: MegaFilterSchema,
|
|
94
|
+
handler: async (args) => {
|
|
95
|
+
const props = [];
|
|
96
|
+
if (args.filters)
|
|
97
|
+
props.push(`filters={${args.filters}}`);
|
|
98
|
+
if (args.vendors)
|
|
99
|
+
props.push(`vendors={${args.vendors}}`);
|
|
100
|
+
if (args.selectedVendor)
|
|
101
|
+
props.push(`selectedVendor={${args.selectedVendor}}`);
|
|
102
|
+
if (args.onVendorChange)
|
|
103
|
+
props.push(`onVendorChange={${args.onVendorChange}}`);
|
|
104
|
+
if (args.onFilterChange)
|
|
105
|
+
props.push(`onFilterChange={${args.onFilterChange}}`);
|
|
106
|
+
if (args.selectedDateRange)
|
|
107
|
+
props.push(`selectedDateRange={${args.selectedDateRange}}`);
|
|
108
|
+
if (args.onDateRangeChange)
|
|
109
|
+
props.push(`onDateRangeChange={${args.onDateRangeChange}}`);
|
|
110
|
+
if (args.locale)
|
|
111
|
+
props.push(`locale="${args.locale}"`);
|
|
112
|
+
if (args.title)
|
|
113
|
+
props.push(`title="${args.title}"`);
|
|
114
|
+
if (args.clearAllText)
|
|
115
|
+
props.push(`clearAllText="${args.clearAllText}"`);
|
|
116
|
+
if (args.clearText)
|
|
117
|
+
props.push(`clearText="${args.clearText}"`);
|
|
118
|
+
if (args.applyText)
|
|
119
|
+
props.push(`applyText="${args.applyText}"`);
|
|
120
|
+
if (args.cancelText)
|
|
121
|
+
props.push(`cancelText="${args.cancelText}"`);
|
|
122
|
+
if (args.searchPlaceholder)
|
|
123
|
+
props.push(`searchPlaceholder="${args.searchPlaceholder}"`);
|
|
124
|
+
if (args.noItemsText)
|
|
125
|
+
props.push(`noItemsText="${args.noItemsText}"`);
|
|
126
|
+
const code = `<OpsnowCommonMegaFilter\n ${props.join('\n ')}\n/>`;
|
|
127
|
+
return {
|
|
128
|
+
content: [
|
|
129
|
+
{
|
|
130
|
+
type: "text",
|
|
131
|
+
text: `\`\`\`jsx\n${code}\n\`\`\``
|
|
132
|
+
}
|
|
133
|
+
]
|
|
134
|
+
};
|
|
135
|
+
},
|
|
67
136
|
}
|
|
68
137
|
];
|
|
69
138
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opsnow-mcp/opsnow-mcp-common-ui-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.16",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -9,6 +9,9 @@
|
|
|
9
9
|
"module": "src/index.ts",
|
|
10
10
|
"scripts": {
|
|
11
11
|
"build": "tsc && node -e \"require('fs').chmodSync('build/index.js', '755')\"",
|
|
12
|
+
"build:skill": "npx tsx scripts/build-skill.ts",
|
|
13
|
+
"build:skill:clean": "rm -rf skills/plugins/opsnow-common-ui-skill/skills && npm run build:skill",
|
|
14
|
+
"build:all": "npm run build && npm run build:skill",
|
|
12
15
|
"start": "node --loader ts-node/esm src/index.ts",
|
|
13
16
|
"dev": "node --loader ts-node/esm src/index.ts",
|
|
14
17
|
"clean": "rm -rf build",
|