@things-factory/labeling 9.1.19
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/CHANGELOG.md +87 -0
- package/ENTITY_IMPLEMENTATION.md +351 -0
- package/INTEGRATION_COMPLETE.md +531 -0
- package/MIGRATION_GUIDE.md +310 -0
- package/README.md +551 -0
- package/REFACTORING_SUMMARY.md +212 -0
- package/UI_DOCUMENTATION.md +552 -0
- package/dist-client/index.d.ts +3 -0
- package/dist-client/index.js +9 -0
- package/dist-client/index.js.map +1 -0
- package/dist-client/pages/labeling-workflow-builder.d.ts +26 -0
- package/dist-client/pages/labeling-workflow-builder.js +636 -0
- package/dist-client/pages/labeling-workflow-builder.js.map +1 -0
- package/dist-client/pages/labeling-workflow-list.d.ts +24 -0
- package/dist-client/pages/labeling-workflow-list.js +495 -0
- package/dist-client/pages/labeling-workflow-list.js.map +1 -0
- package/dist-client/route.d.ts +1 -0
- package/dist-client/route.js +47 -0
- package/dist-client/route.js.map +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -0
- package/dist-server/entities/index.d.ts +5 -0
- package/dist-server/entities/index.js +11 -0
- package/dist-server/entities/index.js.map +1 -0
- package/dist-server/index.d.ts +3 -0
- package/dist-server/index.js +7 -0
- package/dist-server/index.js.map +1 -0
- package/dist-server/route.d.ts +2 -0
- package/dist-server/route.js +6 -0
- package/dist-server/route.js.map +1 -0
- package/dist-server/service/index.d.ts +8 -0
- package/dist-server/service/index.js +21 -0
- package/dist-server/service/index.js.map +1 -0
- package/dist-server/service/labeling-workflow-service.d.ts +69 -0
- package/dist-server/service/labeling-workflow-service.js +521 -0
- package/dist-server/service/labeling-workflow-service.js.map +1 -0
- package/dist-server/service/labeling-workflow.d.ts +30 -0
- package/dist-server/service/labeling-workflow.js +119 -0
- package/dist-server/service/labeling-workflow.js.map +1 -0
- package/dist-server/service/workflow-execution-step.d.ts +28 -0
- package/dist-server/service/workflow-execution-step.js +115 -0
- package/dist-server/service/workflow-execution-step.js.map +1 -0
- package/dist-server/service/workflow-execution.d.ts +27 -0
- package/dist-server/service/workflow-execution.js +110 -0
- package/dist-server/service/workflow-execution.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -0
- package/dist-server/types/workflow-types.d.ts +141 -0
- package/dist-server/types/workflow-types.js +488 -0
- package/dist-server/types/workflow-types.js.map +1 -0
- package/package.json +51 -0
- package/things-factory.config.js +11 -0
- package/translations/en.json +6 -0
- package/translations/ja.json +6 -0
- package/translations/ko.json +6 -0
- package/translations/ms.json +6 -0
- package/translations/zh.json +6 -0
|
@@ -0,0 +1,552 @@
|
|
|
1
|
+
# Labeling Workflow UI Documentation
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
`@things-factory/labeling` 패키지는 레이블링 워크플로우를 시각적으로 생성, 편집, 관리할 수 있는 범용 UI 컴포넌트를 제공합니다.
|
|
6
|
+
|
|
7
|
+
## Components
|
|
8
|
+
|
|
9
|
+
### 1. Workflow List Page (`workflow-list.ts`)
|
|
10
|
+
|
|
11
|
+
워크플로우 목록을 카드 형태로 표시하고 CRUD 작업을 수행하는 페이지입니다.
|
|
12
|
+
|
|
13
|
+
#### Features
|
|
14
|
+
|
|
15
|
+
- **Grid Layout**: 반응형 그리드로 워크플로우 카드 표시
|
|
16
|
+
- **Status Indicators**: 색상별 상태 표시
|
|
17
|
+
- Green (Active): 활성 워크플로우
|
|
18
|
+
- Gray (Draft): 초안 상태
|
|
19
|
+
- Orange (Paused): 일시정지
|
|
20
|
+
- Blue (Completed): 완료
|
|
21
|
+
- Red (Failed): 실패
|
|
22
|
+
- **Filtering**: 상태 및 프로젝트 ID로 필터링
|
|
23
|
+
- **Actions**:
|
|
24
|
+
- Create: 새 워크플로우 생성
|
|
25
|
+
- Edit: 워크플로우 편집
|
|
26
|
+
- Execute: 워크플로우 실행
|
|
27
|
+
- Pause/Resume: 실행 제어
|
|
28
|
+
- Delete: 워크플로우 삭제
|
|
29
|
+
|
|
30
|
+
#### Usage
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import '@things-factory/labeling/dist-client/pages/workflow-list'
|
|
34
|
+
|
|
35
|
+
// HTML에서 사용
|
|
36
|
+
<workflow-list-page></workflow-list-page>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
#### GraphQL Queries Used
|
|
40
|
+
|
|
41
|
+
```graphql
|
|
42
|
+
# 워크플로우 목록 조회
|
|
43
|
+
query GetWorkflows($projectId: Int) {
|
|
44
|
+
labelingWorkflows(projectId: $projectId) {
|
|
45
|
+
items {
|
|
46
|
+
id
|
|
47
|
+
name
|
|
48
|
+
description
|
|
49
|
+
projectId
|
|
50
|
+
status
|
|
51
|
+
triggerType
|
|
52
|
+
steps { name type }
|
|
53
|
+
createdAt
|
|
54
|
+
lastExecutedAt
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# 워크플로우 실행
|
|
60
|
+
mutation ExecuteWorkflow($workflowId: String!) {
|
|
61
|
+
executeLabelingWorkflow(input: { workflowId: $workflowId }) {
|
|
62
|
+
executionId
|
|
63
|
+
status
|
|
64
|
+
summary
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# 워크플로우 일시정지
|
|
69
|
+
mutation PauseWorkflow($workflowId: String!) {
|
|
70
|
+
pauseLabelingWorkflow(workflowId: $workflowId) {
|
|
71
|
+
id
|
|
72
|
+
status
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
# 워크플로우 재개
|
|
77
|
+
mutation ResumeWorkflow($workflowId: String!) {
|
|
78
|
+
resumeLabelingWorkflow(workflowId: $workflowId) {
|
|
79
|
+
id
|
|
80
|
+
status
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
# 워크플로우 삭제
|
|
85
|
+
mutation DeleteWorkflow($workflowId: String!) {
|
|
86
|
+
deleteLabelingWorkflow(workflowId: $workflowId)
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 2. Workflow Builder Page (`workflow-builder.ts`)
|
|
91
|
+
|
|
92
|
+
워크플로우를 생성하거나 편집하는 인터랙티브 빌더 페이지입니다.
|
|
93
|
+
|
|
94
|
+
#### Features
|
|
95
|
+
|
|
96
|
+
**Layout**:
|
|
97
|
+
- **Sidebar (Left)**: 워크플로우 기본 정보 및 스텝 목록
|
|
98
|
+
- **Main Area (Right)**: 선택된 스텝의 상세 설정
|
|
99
|
+
|
|
100
|
+
**Workflow Settings (Sidebar)**:
|
|
101
|
+
- Name: 워크플로우 이름 (필수)
|
|
102
|
+
- Description: 설명
|
|
103
|
+
- Project ID: Label Studio 프로젝트 ID (필수)
|
|
104
|
+
- Trigger Type: Manual/Scheduled/Event-based
|
|
105
|
+
- Auto Start: 생성 후 자동 시작 여부
|
|
106
|
+
|
|
107
|
+
**Step Management**:
|
|
108
|
+
- Add Step: 새 스텝 추가
|
|
109
|
+
- Reorder: 스텝 순서 변경 (↑↓ 버튼)
|
|
110
|
+
- Delete: 스텝 삭제 (× 버튼)
|
|
111
|
+
- Select: 스텝 클릭으로 편집
|
|
112
|
+
|
|
113
|
+
**Step Editor (Main Area)**:
|
|
114
|
+
- Step Name: 스텝 이름
|
|
115
|
+
- Step Type: 스텝 유형 선택
|
|
116
|
+
- `import_data`: 데이터 가져오기
|
|
117
|
+
- `generate_predictions`: AI 예측 생성
|
|
118
|
+
- `wait_for_annotations`: 수동 레이블링 대기
|
|
119
|
+
- `sync_annotations`: 레이블 동기화
|
|
120
|
+
- `validate_quality`: 품질 검증
|
|
121
|
+
- `export_results`: 결과 내보내기
|
|
122
|
+
- `notification`: 알림 전송
|
|
123
|
+
- Configuration: JSON 형식의 스텝 설정
|
|
124
|
+
- Continue on Error: 에러 발생 시 계속 진행 여부
|
|
125
|
+
|
|
126
|
+
#### Usage
|
|
127
|
+
|
|
128
|
+
**Creating New Workflow**:
|
|
129
|
+
```typescript
|
|
130
|
+
import '@things-factory/labeling/dist-client/pages/workflow-builder'
|
|
131
|
+
|
|
132
|
+
// 새 워크플로우 생성
|
|
133
|
+
<workflow-builder-page></workflow-builder-page>
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**Editing Existing Workflow**:
|
|
137
|
+
```typescript
|
|
138
|
+
// 기존 워크플로우 편집
|
|
139
|
+
<workflow-builder-page workflowId="workflow-uuid"></workflow-builder-page>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
#### GraphQL Mutations Used
|
|
143
|
+
|
|
144
|
+
```graphql
|
|
145
|
+
# 워크플로우 생성
|
|
146
|
+
mutation CreateWorkflow($input: CreateWorkflowRequest!) {
|
|
147
|
+
createLabelingWorkflow(input: $input) {
|
|
148
|
+
id
|
|
149
|
+
name
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
# 워크플로우 업데이트
|
|
154
|
+
mutation UpdateWorkflow($workflowId: String!, $input: UpdateWorkflowRequest!) {
|
|
155
|
+
updateLabelingWorkflow(workflowId: $workflowId, input: $input) {
|
|
156
|
+
id
|
|
157
|
+
name
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
# 워크플로우 상세 조회 (편집 모드)
|
|
162
|
+
query GetWorkflow($workflowId: String!) {
|
|
163
|
+
labelingWorkflow(workflowId: $workflowId) {
|
|
164
|
+
id
|
|
165
|
+
name
|
|
166
|
+
description
|
|
167
|
+
projectId
|
|
168
|
+
triggerType
|
|
169
|
+
triggerConfig
|
|
170
|
+
steps {
|
|
171
|
+
name
|
|
172
|
+
type
|
|
173
|
+
order
|
|
174
|
+
config
|
|
175
|
+
continueOnError
|
|
176
|
+
}
|
|
177
|
+
status
|
|
178
|
+
autoStart
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Route Configuration
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
// packages/labeling/client/route.ts
|
|
187
|
+
export default function route(page: string) {
|
|
188
|
+
switch (page) {
|
|
189
|
+
case 'workflows':
|
|
190
|
+
import('./pages/workflow-list')
|
|
191
|
+
return page
|
|
192
|
+
|
|
193
|
+
case 'workflow-builder':
|
|
194
|
+
import('./pages/workflow-builder')
|
|
195
|
+
return page
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### URL Patterns
|
|
201
|
+
|
|
202
|
+
- `/workflows` - 워크플로우 목록
|
|
203
|
+
- `/workflow-builder` - 새 워크플로우 생성
|
|
204
|
+
- `/workflow-builder/{workflowId}` - 기존 워크플로우 편집
|
|
205
|
+
|
|
206
|
+
## Menu Configuration
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
// packages/labeling/client/menu.ts
|
|
210
|
+
import i18next from 'i18next'
|
|
211
|
+
|
|
212
|
+
export function getMenuTemplate(owner: boolean = false) {
|
|
213
|
+
return [
|
|
214
|
+
{
|
|
215
|
+
name: 'LABELING WORKFLOWS',
|
|
216
|
+
type: 'group'
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
name: i18next.t('title.workflows'),
|
|
220
|
+
icon: 'account_tree',
|
|
221
|
+
path: 'workflows',
|
|
222
|
+
description: 'Manage labeling workflows'
|
|
223
|
+
},
|
|
224
|
+
owner && {
|
|
225
|
+
name: i18next.t('title.create workflow'),
|
|
226
|
+
icon: 'add_circle',
|
|
227
|
+
path: 'workflow-builder',
|
|
228
|
+
description: 'Create new labeling workflow'
|
|
229
|
+
}
|
|
230
|
+
].filter(Boolean)
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## Step Type Configuration Examples
|
|
235
|
+
|
|
236
|
+
### Import Data Step
|
|
237
|
+
|
|
238
|
+
```json
|
|
239
|
+
{
|
|
240
|
+
"sourceType": "dataset",
|
|
241
|
+
"dataSetId": "my-dataset-uuid",
|
|
242
|
+
"imageField": "image",
|
|
243
|
+
"limit": 1000
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Generate Predictions Step
|
|
248
|
+
|
|
249
|
+
```json
|
|
250
|
+
{
|
|
251
|
+
"modelId": "resnet50-v2",
|
|
252
|
+
"confidenceThreshold": 0.7,
|
|
253
|
+
"forceRegenerate": false
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Wait for Annotations Step
|
|
258
|
+
|
|
259
|
+
```json
|
|
260
|
+
{
|
|
261
|
+
"minCompletionRate": 0.9,
|
|
262
|
+
"autoProceed": false
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Sync Annotations Step
|
|
267
|
+
|
|
268
|
+
```json
|
|
269
|
+
{
|
|
270
|
+
"completedOnly": true,
|
|
271
|
+
"targetDataSet": "my-dataset-uuid"
|
|
272
|
+
}
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Validate Quality Step
|
|
276
|
+
|
|
277
|
+
```json
|
|
278
|
+
{
|
|
279
|
+
"minQualityScore": 0.8,
|
|
280
|
+
"validationRules": ["consistency", "completeness"]
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Export Results Step
|
|
285
|
+
|
|
286
|
+
```json
|
|
287
|
+
{
|
|
288
|
+
"format": "coco",
|
|
289
|
+
"destination": "s3://my-bucket/exports/"
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### Notification Step
|
|
294
|
+
|
|
295
|
+
```json
|
|
296
|
+
{
|
|
297
|
+
"channels": ["email", "slack"],
|
|
298
|
+
"recipients": ["admin@example.com"],
|
|
299
|
+
"message": "Workflow completed successfully"
|
|
300
|
+
}
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## Styling and Theming
|
|
304
|
+
|
|
305
|
+
UI 컴포넌트는 Material Design 3 테마 변수를 사용합니다:
|
|
306
|
+
|
|
307
|
+
```css
|
|
308
|
+
/* Primary colors */
|
|
309
|
+
--md-sys-color-primary
|
|
310
|
+
--md-sys-color-on-primary
|
|
311
|
+
--md-sys-color-primary-container
|
|
312
|
+
--md-sys-color-on-primary-container
|
|
313
|
+
|
|
314
|
+
/* Surface colors */
|
|
315
|
+
--md-sys-color-surface
|
|
316
|
+
--md-sys-color-on-surface
|
|
317
|
+
--md-sys-color-surface-variant
|
|
318
|
+
--md-sys-color-on-surface-variant
|
|
319
|
+
|
|
320
|
+
/* Outline */
|
|
321
|
+
--md-sys-color-outline
|
|
322
|
+
|
|
323
|
+
/* Secondary */
|
|
324
|
+
--md-sys-color-secondary
|
|
325
|
+
--md-sys-color-on-secondary
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
테마를 커스터마이징하려면 CSS 변수를 오버라이드하세요:
|
|
329
|
+
|
|
330
|
+
```css
|
|
331
|
+
:root {
|
|
332
|
+
--md-sys-color-primary: #6750a4;
|
|
333
|
+
--md-sys-color-on-primary: #ffffff;
|
|
334
|
+
}
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
## Integration with Other Packages
|
|
338
|
+
|
|
339
|
+
### Option 1: Direct Usage
|
|
340
|
+
|
|
341
|
+
다른 패키지에서 직접 사용:
|
|
342
|
+
|
|
343
|
+
```typescript
|
|
344
|
+
import '@things-factory/labeling/dist-client/pages/workflow-list'
|
|
345
|
+
import '@things-factory/labeling/dist-client/pages/workflow-builder'
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### Option 2: Re-export with Customization
|
|
349
|
+
|
|
350
|
+
다른 패키지에서 래퍼 컴포넌트 생성:
|
|
351
|
+
|
|
352
|
+
```typescript
|
|
353
|
+
// packages/operato-dataset/client/pages/dataset-workflow-builder.ts
|
|
354
|
+
import { html } from 'lit'
|
|
355
|
+
import { customElement } from 'lit/decorators.js'
|
|
356
|
+
import '@things-factory/labeling/dist-client/pages/workflow-builder'
|
|
357
|
+
|
|
358
|
+
@customElement('dataset-workflow-builder')
|
|
359
|
+
export class DatasetWorkflowBuilder extends LitElement {
|
|
360
|
+
render() {
|
|
361
|
+
return html`
|
|
362
|
+
<workflow-builder-page
|
|
363
|
+
.presetConfig=${{
|
|
364
|
+
triggerType: 'event',
|
|
365
|
+
steps: [
|
|
366
|
+
// DataSet-specific preset steps
|
|
367
|
+
]
|
|
368
|
+
}}
|
|
369
|
+
></workflow-builder-page>
|
|
370
|
+
`
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
```
|
|
374
|
+
|
|
375
|
+
### Option 3: Menu Integration
|
|
376
|
+
|
|
377
|
+
다른 패키지의 메뉴에 통합:
|
|
378
|
+
|
|
379
|
+
```typescript
|
|
380
|
+
// packages/operato-dataset/client/menu.ts
|
|
381
|
+
import { getMenuTemplate as getLabelingMenu } from '@things-factory/labeling'
|
|
382
|
+
|
|
383
|
+
export function getMenuTemplate(owner: boolean, dataSets: any[]) {
|
|
384
|
+
return [
|
|
385
|
+
// ... other menu items
|
|
386
|
+
...getLabelingMenu(owner),
|
|
387
|
+
// ... more menu items
|
|
388
|
+
]
|
|
389
|
+
}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
## Best Practices
|
|
393
|
+
|
|
394
|
+
### 1. Validation
|
|
395
|
+
|
|
396
|
+
워크플로우 저장 전 검증:
|
|
397
|
+
|
|
398
|
+
```typescript
|
|
399
|
+
validateWorkflow(workflow: any): string[] {
|
|
400
|
+
const errors: string[] = []
|
|
401
|
+
|
|
402
|
+
if (!workflow.name) errors.push('Workflow name is required')
|
|
403
|
+
if (!workflow.projectId) errors.push('Project ID is required')
|
|
404
|
+
if (workflow.steps.length === 0) errors.push('At least one step is required')
|
|
405
|
+
|
|
406
|
+
workflow.steps.forEach((step: any, index: number) => {
|
|
407
|
+
if (!step.name) errors.push(`Step ${index + 1}: Name is required`)
|
|
408
|
+
if (!step.type) errors.push(`Step ${index + 1}: Type is required`)
|
|
409
|
+
|
|
410
|
+
try {
|
|
411
|
+
JSON.parse(step.config)
|
|
412
|
+
} catch (e) {
|
|
413
|
+
errors.push(`Step ${index + 1}: Invalid JSON configuration`)
|
|
414
|
+
}
|
|
415
|
+
})
|
|
416
|
+
|
|
417
|
+
return errors
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
### 2. Error Handling
|
|
422
|
+
|
|
423
|
+
사용자에게 명확한 에러 메시지 표시:
|
|
424
|
+
|
|
425
|
+
```typescript
|
|
426
|
+
async saveWorkflow() {
|
|
427
|
+
try {
|
|
428
|
+
const errors = this.validateWorkflow(this.workflow)
|
|
429
|
+
if (errors.length > 0) {
|
|
430
|
+
this.error = errors.join('\n')
|
|
431
|
+
return
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
await client.mutate({ mutation, variables })
|
|
435
|
+
alert('Workflow saved successfully!')
|
|
436
|
+
} catch (error) {
|
|
437
|
+
console.error('Failed to save workflow:', error)
|
|
438
|
+
this.error = 'Failed to save workflow. Please check your inputs.'
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
### 3. Loading States
|
|
444
|
+
|
|
445
|
+
긴 작업 중 로딩 상태 표시:
|
|
446
|
+
|
|
447
|
+
```typescript
|
|
448
|
+
async loadWorkflows() {
|
|
449
|
+
this.loading = true
|
|
450
|
+
try {
|
|
451
|
+
const response = await client.query({ query })
|
|
452
|
+
this.workflows = response.data.labelingWorkflows.items
|
|
453
|
+
} finally {
|
|
454
|
+
this.loading = false
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
### 4. Navigation
|
|
460
|
+
|
|
461
|
+
페이지 간 이동:
|
|
462
|
+
|
|
463
|
+
```typescript
|
|
464
|
+
import { navigate } from '@operato/shell'
|
|
465
|
+
|
|
466
|
+
// 워크플로우 목록으로
|
|
467
|
+
navigate('workflows')
|
|
468
|
+
|
|
469
|
+
// 워크플로우 편집
|
|
470
|
+
navigate(`workflow-builder/${workflowId}`)
|
|
471
|
+
|
|
472
|
+
// 이전 페이지로
|
|
473
|
+
history.back()
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
## Accessibility
|
|
477
|
+
|
|
478
|
+
### Keyboard Navigation
|
|
479
|
+
|
|
480
|
+
- Tab: 폼 필드 간 이동
|
|
481
|
+
- Enter: 버튼 클릭, 폼 제출
|
|
482
|
+
- Escape: 다이얼로그 닫기
|
|
483
|
+
- Arrow keys: 스텝 목록 탐색
|
|
484
|
+
|
|
485
|
+
### Screen Readers
|
|
486
|
+
|
|
487
|
+
모든 인터랙티브 요소에 적절한 ARIA 레이블 제공:
|
|
488
|
+
|
|
489
|
+
```html
|
|
490
|
+
<button aria-label="Add new step">+ Add Step</button>
|
|
491
|
+
<input aria-label="Workflow name" placeholder="Enter workflow name" />
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
## Performance Optimization
|
|
495
|
+
|
|
496
|
+
### Lazy Loading
|
|
497
|
+
|
|
498
|
+
페이지 컴포넌트는 동적으로 임포트됩니다:
|
|
499
|
+
|
|
500
|
+
```typescript
|
|
501
|
+
// route.ts
|
|
502
|
+
case 'workflows':
|
|
503
|
+
import('./pages/workflow-list') // Lazy loaded
|
|
504
|
+
return page
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
### Virtual Scrolling
|
|
508
|
+
|
|
509
|
+
많은 워크플로우를 표시할 때는 가상 스크롤링 고려:
|
|
510
|
+
|
|
511
|
+
```typescript
|
|
512
|
+
// 큰 리스트에 적용 (향후 개선 사항)
|
|
513
|
+
import { virtualScroll } from '@lit-labs/virtualizer'
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
## Troubleshooting
|
|
517
|
+
|
|
518
|
+
### Common Issues
|
|
519
|
+
|
|
520
|
+
**Issue 1: GraphQL Query Fails**
|
|
521
|
+
- **Solution**: GraphQL 서버 연결 확인, 쿼리 구문 검증
|
|
522
|
+
|
|
523
|
+
**Issue 2: Workflow Not Saving**
|
|
524
|
+
- **Solution**: JSON 설정 형식 확인, 필수 필드 입력 확인
|
|
525
|
+
|
|
526
|
+
**Issue 3: Step Configuration Error**
|
|
527
|
+
- **Solution**: config 필드가 유효한 JSON 문자열인지 확인
|
|
528
|
+
|
|
529
|
+
**Issue 4: Navigation Not Working**
|
|
530
|
+
- **Solution**: route.ts가 올바르게 설정되었는지, shell 패키지 임포트 확인
|
|
531
|
+
|
|
532
|
+
## Future Enhancements
|
|
533
|
+
|
|
534
|
+
### Planned Features
|
|
535
|
+
|
|
536
|
+
- [ ] Drag & Drop 스텝 재정렬
|
|
537
|
+
- [ ] 비주얼 워크플로우 다이어그램
|
|
538
|
+
- [ ] 스텝 템플릿 라이브러리
|
|
539
|
+
- [ ] 실시간 워크플로우 실행 모니터링
|
|
540
|
+
- [ ] 워크플로우 복제 기능
|
|
541
|
+
- [ ] 워크플로우 비교 도구
|
|
542
|
+
- [ ] 스텝별 설정 검증기
|
|
543
|
+
- [ ] 다국어 지원 확장
|
|
544
|
+
- [ ] 워크플로우 시뮬레이션 모드
|
|
545
|
+
- [ ] 조건부 분기 지원
|
|
546
|
+
|
|
547
|
+
## References
|
|
548
|
+
|
|
549
|
+
- [Lit Documentation](https://lit.dev/)
|
|
550
|
+
- [Material Design 3](https://m3.material.io/)
|
|
551
|
+
- [GraphQL Best Practices](https://graphql.org/learn/best-practices/)
|
|
552
|
+
- [Things-Factory Architecture](../../../docs/architecture.md)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.route = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
tslib_1.__exportStar(require("./pages/labeling-workflow-list.js"), exports);
|
|
6
|
+
tslib_1.__exportStar(require("./pages/labeling-workflow-builder.js"), exports);
|
|
7
|
+
var route_js_1 = require("./route.js");
|
|
8
|
+
Object.defineProperty(exports, "route", { enumerable: true, get: function () { return tslib_1.__importDefault(route_js_1).default; } });
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../client/index.ts"],"names":[],"mappings":";;;;AAAA,4EAAiD;AACjD,+EAAoD;AAEpD,uCAA6C;AAApC,0HAAA,OAAO,OAAS","sourcesContent":["export * from './pages/labeling-workflow-list.js'\nexport * from './pages/labeling-workflow-builder.js'\n\nexport { default as route } from './route.js'\n"]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { PageView } from '@operato/shell';
|
|
2
|
+
/**
|
|
3
|
+
* Workflow Builder Page
|
|
4
|
+
*
|
|
5
|
+
* Generic workflow builder UI for creating and editing labeling workflows
|
|
6
|
+
*/
|
|
7
|
+
export declare class LabelingWorkflowBuilderPage extends PageView {
|
|
8
|
+
static styles: import("lit").CSSResult[];
|
|
9
|
+
workflowId?: string;
|
|
10
|
+
workflow: any;
|
|
11
|
+
selectedStepIndex: number;
|
|
12
|
+
loading: boolean;
|
|
13
|
+
error: string;
|
|
14
|
+
firstUpdated(): Promise<void>;
|
|
15
|
+
loadWorkflow(): Promise<void>;
|
|
16
|
+
saveWorkflow(): Promise<void>;
|
|
17
|
+
addStep(): void;
|
|
18
|
+
removeStep(index: number): void;
|
|
19
|
+
moveStepUp(index: number): void;
|
|
20
|
+
moveStepDown(index: number): void;
|
|
21
|
+
selectStep(index: number): void;
|
|
22
|
+
updateStep(index: number, field: string, value: any): void;
|
|
23
|
+
updateWorkflow(field: string, value: any): void;
|
|
24
|
+
renderStepEditor(): import("lit-html").TemplateResult<1>;
|
|
25
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
26
|
+
}
|