@vue-skuilder/edit-ui 0.1.17 → 0.1.20
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/dist/assets/index.css +1 -1
- package/dist/edit-ui.es.js +1 -71753
- package/dist/edit-ui.es.js.map +1 -1
- package/dist/edit-ui.umd.js +1 -82
- package/dist/edit-ui.umd.js.map +1 -1
- package/package.json +12 -12
- package/src/components/NavigationStrategy/HardcodedOrderConfigForm.vue +108 -0
- package/src/components/NavigationStrategy/HierarchyConfigForm.vue +453 -0
- package/src/components/NavigationStrategy/InterferenceConfigForm.vue +460 -0
- package/src/components/NavigationStrategy/NavigationStrategyEditor.vue +345 -84
- package/src/components/NavigationStrategy/NavigationStrategyList.vue +19 -22
- package/src/components/NavigationStrategy/RelativePriorityConfigForm.vue +379 -0
|
@@ -1,59 +1,131 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="navigation-strategy-editor">
|
|
3
|
-
<div v-if="loading">
|
|
3
|
+
<div v-if="loading" class="text-center pa-4">
|
|
4
4
|
<v-progress-circular indeterminate color="secondary"></v-progress-circular>
|
|
5
5
|
</div>
|
|
6
|
-
<div v-else>
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
<div v-else class="editor-layout">
|
|
7
|
+
<!-- Left Column: Strategy List -->
|
|
8
|
+
<div class="strategy-list-column">
|
|
9
|
+
<div class="d-flex align-center mb-2">
|
|
10
|
+
<h3 class="text-h6">Strategies</h3>
|
|
11
|
+
<v-spacer></v-spacer>
|
|
12
|
+
<v-btn size="small" color="primary" @click="startNewStrategy" density="compact">
|
|
13
|
+
<v-icon start size="small">mdi-plus</v-icon>
|
|
14
|
+
New
|
|
15
|
+
</v-btn>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<v-alert v-if="strategies.length === 0" type="info" density="compact">
|
|
19
|
+
No strategies defined
|
|
20
|
+
</v-alert>
|
|
21
|
+
|
|
22
|
+
<navigation-strategy-list
|
|
23
|
+
v-else
|
|
24
|
+
:strategies="strategies"
|
|
25
|
+
:default-strategy-id="defaultStrategyId"
|
|
26
|
+
@update:default-strategy="setDefaultStrategy"
|
|
27
|
+
@edit="editStrategy"
|
|
28
|
+
@delete="confirmDeleteStrategy"
|
|
29
|
+
/>
|
|
11
30
|
</div>
|
|
12
31
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
32
|
+
<!-- Right Column: Strategy Form -->
|
|
33
|
+
<div class="strategy-form-column">
|
|
34
|
+
<div class="form-header d-flex align-center mb-3">
|
|
35
|
+
<h3 class="text-h6">{{ editingStrategy ? 'Edit Strategy' : 'New Strategy' }}</h3>
|
|
36
|
+
<v-spacer></v-spacer>
|
|
37
|
+
<v-btn
|
|
38
|
+
v-if="editingStrategy"
|
|
39
|
+
size="small"
|
|
40
|
+
variant="text"
|
|
41
|
+
@click="cancelEdit"
|
|
42
|
+
density="compact"
|
|
43
|
+
>
|
|
44
|
+
Cancel
|
|
45
|
+
</v-btn>
|
|
46
|
+
</div>
|
|
47
|
+
|
|
48
|
+
<v-select
|
|
49
|
+
v-model="newStrategy.type"
|
|
50
|
+
label="Type"
|
|
51
|
+
:items="strategyTypes"
|
|
52
|
+
item-title="label"
|
|
53
|
+
item-value="value"
|
|
54
|
+
density="compact"
|
|
55
|
+
class="mb-3"
|
|
56
|
+
></v-select>
|
|
57
|
+
|
|
58
|
+
<v-text-field
|
|
59
|
+
v-model="newStrategy.name"
|
|
60
|
+
label="Name"
|
|
61
|
+
density="compact"
|
|
62
|
+
class="mb-3"
|
|
63
|
+
></v-text-field>
|
|
64
|
+
|
|
65
|
+
<v-text-field
|
|
66
|
+
v-model="newStrategy.description"
|
|
67
|
+
label="Description"
|
|
68
|
+
density="compact"
|
|
69
|
+
class="mb-3"
|
|
70
|
+
></v-text-field>
|
|
71
|
+
|
|
72
|
+
<!-- Strategy-specific configuration forms -->
|
|
73
|
+
<hardcoded-order-config-form
|
|
74
|
+
v-if="newStrategy.type === 'hardcoded'"
|
|
75
|
+
v-model="newStrategy.config"
|
|
76
|
+
/>
|
|
77
|
+
|
|
78
|
+
<hierarchy-config-form
|
|
79
|
+
v-else-if="newStrategy.type === 'hierarchy'"
|
|
80
|
+
v-model="newStrategy.config"
|
|
81
|
+
:course-id="courseId"
|
|
82
|
+
/>
|
|
83
|
+
|
|
84
|
+
<interference-config-form
|
|
85
|
+
v-else-if="newStrategy.type === 'interference'"
|
|
86
|
+
v-model="newStrategy.config"
|
|
87
|
+
:course-id="courseId"
|
|
88
|
+
/>
|
|
89
|
+
|
|
90
|
+
<relative-priority-config-form
|
|
91
|
+
v-else-if="newStrategy.type === 'relativePriority'"
|
|
92
|
+
v-model="newStrategy.config"
|
|
93
|
+
:course-id="courseId"
|
|
94
|
+
/>
|
|
95
|
+
|
|
96
|
+
<v-alert v-else type="warning" density="compact">
|
|
97
|
+
Unknown strategy type: {{ newStrategy.type }}
|
|
98
|
+
</v-alert>
|
|
99
|
+
|
|
100
|
+
<div class="form-actions mt-4">
|
|
101
|
+
<v-btn
|
|
102
|
+
color="primary"
|
|
103
|
+
@click="saveStrategy"
|
|
104
|
+
:disabled="!newStrategy.name"
|
|
105
|
+
size="small"
|
|
106
|
+
>
|
|
107
|
+
{{ editingStrategy ? 'Update' : 'Create' }}
|
|
108
|
+
</v-btn>
|
|
109
|
+
<v-btn
|
|
110
|
+
v-if="editingStrategy"
|
|
111
|
+
variant="text"
|
|
112
|
+
@click="cancelEdit"
|
|
113
|
+
size="small"
|
|
114
|
+
>
|
|
115
|
+
Cancel
|
|
116
|
+
</v-btn>
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
48
119
|
|
|
120
|
+
<!-- Delete Confirmation (still a dialog, but small) -->
|
|
49
121
|
<v-dialog v-model="showDeleteConfirm" max-width="400px">
|
|
50
122
|
<v-card>
|
|
51
|
-
<v-card-title class="text-
|
|
52
|
-
<v-card-text>
|
|
123
|
+
<v-card-title class="text-subtitle-1">Delete Strategy</v-card-title>
|
|
124
|
+
<v-card-text>Delete "{{ strategyToDelete?.name }}"?</v-card-text>
|
|
53
125
|
<v-card-actions>
|
|
54
126
|
<v-spacer></v-spacer>
|
|
55
|
-
<v-btn
|
|
56
|
-
<v-btn
|
|
127
|
+
<v-btn size="small" @click="showDeleteConfirm = false">Cancel</v-btn>
|
|
128
|
+
<v-btn size="small" color="error" @click="deleteStrategy">Delete</v-btn>
|
|
57
129
|
</v-card-actions>
|
|
58
130
|
</v-card>
|
|
59
131
|
</v-dialog>
|
|
@@ -65,14 +137,21 @@
|
|
|
65
137
|
import { defineComponent } from 'vue';
|
|
66
138
|
import type { ContentNavigationStrategyData } from '@vue-skuilder/db/src/core/types/contentNavigationStrategy';
|
|
67
139
|
import NavigationStrategyList from './NavigationStrategyList.vue';
|
|
140
|
+
import HardcodedOrderConfigForm from './HardcodedOrderConfigForm.vue';
|
|
141
|
+
import HierarchyConfigForm from './HierarchyConfigForm.vue';
|
|
142
|
+
import InterferenceConfigForm from './InterferenceConfigForm.vue';
|
|
143
|
+
import RelativePriorityConfigForm from './RelativePriorityConfigForm.vue';
|
|
68
144
|
import { getDataLayer, DocType, Navigators } from '@vue-skuilder/db';
|
|
69
|
-
import { DocTypePrefixes } from '@vue-skuilder/db/src/core/types/types-legacy';
|
|
70
145
|
|
|
71
146
|
export default defineComponent({
|
|
72
147
|
name: 'NavigationStrategyEditor',
|
|
73
148
|
|
|
74
149
|
components: {
|
|
75
150
|
NavigationStrategyList,
|
|
151
|
+
HardcodedOrderConfigForm,
|
|
152
|
+
HierarchyConfigForm,
|
|
153
|
+
InterferenceConfigForm,
|
|
154
|
+
RelativePriorityConfigForm,
|
|
76
155
|
},
|
|
77
156
|
|
|
78
157
|
props: {
|
|
@@ -88,12 +167,19 @@ export default defineComponent({
|
|
|
88
167
|
loading: true,
|
|
89
168
|
showDeleteConfirm: false,
|
|
90
169
|
strategyToDelete: null as ContentNavigationStrategyData | null,
|
|
91
|
-
|
|
170
|
+
strategyTypes: [
|
|
171
|
+
{ label: 'Hardcoded Order', value: 'hardcoded' },
|
|
172
|
+
{ label: 'Hierarchy Definition', value: 'hierarchy' },
|
|
173
|
+
{ label: 'Interference Mitigator', value: 'interference' },
|
|
174
|
+
{ label: 'Relative Priority', value: 'relativePriority' },
|
|
175
|
+
],
|
|
92
176
|
newStrategy: {
|
|
177
|
+
type: 'hardcoded' as string,
|
|
93
178
|
name: '',
|
|
94
179
|
description: '',
|
|
95
|
-
cardIds:
|
|
180
|
+
config: { cardIds: [] } as any,
|
|
96
181
|
},
|
|
182
|
+
editingStrategy: null as ContentNavigationStrategyData | null,
|
|
97
183
|
defaultStrategyId: null as string | null,
|
|
98
184
|
};
|
|
99
185
|
},
|
|
@@ -102,7 +188,80 @@ export default defineComponent({
|
|
|
102
188
|
await this.loadStrategies();
|
|
103
189
|
},
|
|
104
190
|
|
|
191
|
+
watch: {
|
|
192
|
+
'newStrategy.type'(newType: string) {
|
|
193
|
+
// Reset config when strategy type changes
|
|
194
|
+
this.newStrategy.config = this.getDefaultConfig(newType);
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
|
|
105
198
|
methods: {
|
|
199
|
+
getDefaultConfig(strategyType: string) {
|
|
200
|
+
switch (strategyType) {
|
|
201
|
+
case 'hardcoded':
|
|
202
|
+
return { cardIds: [] };
|
|
203
|
+
case 'hierarchy':
|
|
204
|
+
return {
|
|
205
|
+
prerequisites: {},
|
|
206
|
+
delegateStrategy: 'elo',
|
|
207
|
+
};
|
|
208
|
+
case 'interference':
|
|
209
|
+
return {
|
|
210
|
+
interferenceSets: [],
|
|
211
|
+
maturityThreshold: {
|
|
212
|
+
minCount: 10,
|
|
213
|
+
minElapsedDays: 3,
|
|
214
|
+
},
|
|
215
|
+
defaultDecay: 0.8,
|
|
216
|
+
delegateStrategy: 'elo',
|
|
217
|
+
};
|
|
218
|
+
case 'relativePriority':
|
|
219
|
+
return {
|
|
220
|
+
tagPriorities: {},
|
|
221
|
+
defaultPriority: 0.5,
|
|
222
|
+
combineMode: 'max',
|
|
223
|
+
priorityInfluence: 0.5,
|
|
224
|
+
delegateStrategy: 'elo',
|
|
225
|
+
};
|
|
226
|
+
default:
|
|
227
|
+
return {};
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
|
|
231
|
+
// Map implementing class to strategy type
|
|
232
|
+
getStrategyTypeFromClass(implementingClass: string): string {
|
|
233
|
+
switch (implementingClass) {
|
|
234
|
+
case Navigators.HARDCODED:
|
|
235
|
+
return 'hardcoded';
|
|
236
|
+
case Navigators.HIERARCHY:
|
|
237
|
+
return 'hierarchy';
|
|
238
|
+
case Navigators.INTERFERENCE:
|
|
239
|
+
return 'interference';
|
|
240
|
+
case Navigators.RELATIVE_PRIORITY:
|
|
241
|
+
return 'relativePriority';
|
|
242
|
+
default:
|
|
243
|
+
return 'hardcoded';
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
|
|
247
|
+
// Parse serialized data back to config object
|
|
248
|
+
parseSerializedData(strategyType: string, serializedData: string): any {
|
|
249
|
+
try {
|
|
250
|
+
const parsed = JSON.parse(serializedData);
|
|
251
|
+
|
|
252
|
+
if (strategyType === 'hardcoded') {
|
|
253
|
+
// Hardcoded stores just the array, wrap it
|
|
254
|
+
return { cardIds: Array.isArray(parsed) ? parsed : [] };
|
|
255
|
+
} else {
|
|
256
|
+
// Other strategies store the full config object
|
|
257
|
+
return parsed;
|
|
258
|
+
}
|
|
259
|
+
} catch (error) {
|
|
260
|
+
console.error('Failed to parse strategy data:', error);
|
|
261
|
+
return this.getDefaultConfig(strategyType);
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
|
|
106
265
|
async loadStrategies() {
|
|
107
266
|
this.loading = true;
|
|
108
267
|
try {
|
|
@@ -156,18 +315,68 @@ export default defineComponent({
|
|
|
156
315
|
}
|
|
157
316
|
},
|
|
158
317
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
this.
|
|
318
|
+
startNewStrategy() {
|
|
319
|
+
const defaultType = 'hardcoded';
|
|
320
|
+
this.newStrategy = {
|
|
321
|
+
type: defaultType,
|
|
322
|
+
name: '',
|
|
323
|
+
description: '',
|
|
324
|
+
config: this.getDefaultConfig(defaultType),
|
|
325
|
+
};
|
|
326
|
+
this.editingStrategy = null;
|
|
162
327
|
},
|
|
163
328
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
329
|
+
cancelEdit() {
|
|
330
|
+
this.startNewStrategy();
|
|
331
|
+
},
|
|
332
|
+
|
|
333
|
+
editStrategy(strategy: ContentNavigationStrategyData) {
|
|
334
|
+
const strategyType = this.getStrategyTypeFromClass(strategy.implementingClass);
|
|
335
|
+
const config = this.parseSerializedData(strategyType, strategy.serializedData);
|
|
336
|
+
|
|
337
|
+
this.newStrategy = {
|
|
338
|
+
type: strategyType,
|
|
339
|
+
name: strategy.name,
|
|
340
|
+
description: strategy.description,
|
|
341
|
+
config,
|
|
342
|
+
};
|
|
343
|
+
this.editingStrategy = strategy;
|
|
344
|
+
},
|
|
345
|
+
|
|
346
|
+
async saveStrategy() {
|
|
347
|
+
if (!this.newStrategy.name) {
|
|
348
|
+
alert('Strategy Name is required.');
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Validate config based on strategy type
|
|
353
|
+
if (this.newStrategy.type === 'hardcoded' && this.newStrategy.config.cardIds.length === 0) {
|
|
354
|
+
alert('At least one card ID is required for hardcoded order strategy.');
|
|
168
355
|
return;
|
|
169
356
|
}
|
|
170
357
|
|
|
358
|
+
if (this.newStrategy.type === 'hierarchy') {
|
|
359
|
+
const prereqCount = Object.keys(this.newStrategy.config.prerequisites || {}).length;
|
|
360
|
+
if (prereqCount === 0) {
|
|
361
|
+
alert('At least one prerequisite rule is required for hierarchy strategy.');
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (this.newStrategy.type === 'interference') {
|
|
367
|
+
if (!this.newStrategy.config.interferenceSets || this.newStrategy.config.interferenceSets.length === 0) {
|
|
368
|
+
alert('At least one interference group is required for interference strategy.');
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (this.newStrategy.type === 'relativePriority') {
|
|
374
|
+
if (!this.newStrategy.config.tagPriorities || Object.keys(this.newStrategy.config.tagPriorities).length === 0) {
|
|
375
|
+
alert('At least one tag priority must be set for relative priority strategy.');
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
171
380
|
this.loading = true;
|
|
172
381
|
try {
|
|
173
382
|
const dataLayer = getDataLayer();
|
|
@@ -175,27 +384,53 @@ export default defineComponent({
|
|
|
175
384
|
const userName = userDB.getUsername();
|
|
176
385
|
const courseDB = dataLayer.getCourseDB(this.courseId);
|
|
177
386
|
|
|
178
|
-
//
|
|
179
|
-
const
|
|
180
|
-
.
|
|
181
|
-
|
|
182
|
-
.
|
|
183
|
-
|
|
184
|
-
const strategyData: ContentNavigationStrategyData = {
|
|
185
|
-
_id: `NAVIGATION_STRATEGY-${Date.now()}`,
|
|
186
|
-
docType: DocType.NAVIGATION_STRATEGY,
|
|
187
|
-
name: this.newStrategy.name,
|
|
188
|
-
description: this.newStrategy.description,
|
|
189
|
-
implementingClass: Navigators.HARDCODED,
|
|
190
|
-
author: userName,
|
|
191
|
-
course: this.courseId,
|
|
192
|
-
serializedData: JSON.stringify(cardIdArray),
|
|
387
|
+
// Map strategy type to implementing class
|
|
388
|
+
const implementingClassMap: Record<string, string> = {
|
|
389
|
+
hardcoded: Navigators.HARDCODED,
|
|
390
|
+
hierarchy: Navigators.HIERARCHY,
|
|
391
|
+
interference: Navigators.INTERFERENCE,
|
|
392
|
+
relativePriority: Navigators.RELATIVE_PRIORITY,
|
|
193
393
|
};
|
|
194
394
|
|
|
195
|
-
|
|
395
|
+
// Serialize config based on strategy type
|
|
396
|
+
let serializedData: string;
|
|
397
|
+
if (this.newStrategy.type === 'hardcoded') {
|
|
398
|
+
// Hardcoded stores just the array of card IDs
|
|
399
|
+
serializedData = JSON.stringify(this.newStrategy.config.cardIds);
|
|
400
|
+
} else {
|
|
401
|
+
// Other strategies store their full config object
|
|
402
|
+
serializedData = JSON.stringify(this.newStrategy.config);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (this.editingStrategy) {
|
|
406
|
+
// Update existing strategy
|
|
407
|
+
const strategyData: ContentNavigationStrategyData = {
|
|
408
|
+
...this.editingStrategy,
|
|
409
|
+
name: this.newStrategy.name,
|
|
410
|
+
description: this.newStrategy.description,
|
|
411
|
+
implementingClass: implementingClassMap[this.newStrategy.type],
|
|
412
|
+
serializedData,
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
await courseDB.updateNavigationStrategy(this.editingStrategy._id, strategyData);
|
|
416
|
+
} else {
|
|
417
|
+
// Create new strategy
|
|
418
|
+
const strategyData: ContentNavigationStrategyData = {
|
|
419
|
+
_id: `NAVIGATION_STRATEGY-${Date.now()}`,
|
|
420
|
+
docType: DocType.NAVIGATION_STRATEGY,
|
|
421
|
+
name: this.newStrategy.name,
|
|
422
|
+
description: this.newStrategy.description,
|
|
423
|
+
implementingClass: implementingClassMap[this.newStrategy.type],
|
|
424
|
+
author: userName,
|
|
425
|
+
course: this.courseId,
|
|
426
|
+
serializedData,
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
await courseDB.addNavigationStrategy(strategyData);
|
|
430
|
+
}
|
|
196
431
|
|
|
197
|
-
this.showCreateDialog = false;
|
|
198
432
|
await this.loadStrategies(); // Refresh the list
|
|
433
|
+
this.startNewStrategy(); // Reset form
|
|
199
434
|
} catch (error) {
|
|
200
435
|
console.error('Failed to save new strategy:', error);
|
|
201
436
|
alert('Error saving strategy. See console for details.');
|
|
@@ -203,11 +438,6 @@ export default defineComponent({
|
|
|
203
438
|
this.loading = false;
|
|
204
439
|
},
|
|
205
440
|
|
|
206
|
-
editStrategy(strategy: ContentNavigationStrategyData) {
|
|
207
|
-
// Strategy editing is not yet implemented
|
|
208
|
-
console.log(`Editing strategy ${strategy._id} is not yet implemented`);
|
|
209
|
-
},
|
|
210
|
-
|
|
211
441
|
confirmDeleteStrategy(strategy: ContentNavigationStrategyData) {
|
|
212
442
|
this.strategyToDelete = strategy;
|
|
213
443
|
this.showDeleteConfirm = true;
|
|
@@ -254,14 +484,45 @@ export default defineComponent({
|
|
|
254
484
|
|
|
255
485
|
<style scoped>
|
|
256
486
|
.navigation-strategy-editor {
|
|
257
|
-
|
|
487
|
+
height: 100%;
|
|
488
|
+
overflow: hidden;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
.editor-layout {
|
|
492
|
+
display: grid;
|
|
493
|
+
grid-template-columns: 400px 1fr;
|
|
494
|
+
gap: 24px;
|
|
495
|
+
height: 100%;
|
|
496
|
+
overflow: hidden;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
.strategy-list-column {
|
|
500
|
+
overflow-y: auto;
|
|
501
|
+
padding-right: 12px;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
.strategy-form-column {
|
|
505
|
+
overflow-y: auto;
|
|
506
|
+
padding-left: 12px;
|
|
507
|
+
border-left: 1px solid #e0e0e0;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
.form-actions {
|
|
511
|
+
display: flex;
|
|
512
|
+
gap: 8px;
|
|
258
513
|
}
|
|
259
514
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
515
|
+
@media (max-width: 960px) {
|
|
516
|
+
.editor-layout {
|
|
517
|
+
grid-template-columns: 1fr;
|
|
518
|
+
grid-template-rows: auto 1fr;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
.strategy-form-column {
|
|
522
|
+
border-left: none;
|
|
523
|
+
border-top: 1px solid #e0e0e0;
|
|
524
|
+
padding-left: 0;
|
|
525
|
+
padding-top: 12px;
|
|
526
|
+
}
|
|
266
527
|
}
|
|
267
528
|
</style>
|
|
@@ -1,41 +1,45 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="navigation-strategy-list">
|
|
3
|
-
<v-radio-group :model-value="defaultStrategyId" @update:modelValue="$emit('update:defaultStrategy', $event)">
|
|
4
|
-
<v-list>
|
|
3
|
+
<v-radio-group :model-value="defaultStrategyId" @update:modelValue="$emit('update:defaultStrategy', $event)" density="compact">
|
|
4
|
+
<v-list density="compact">
|
|
5
5
|
<v-list-item
|
|
6
6
|
v-for="strategy in strategies"
|
|
7
7
|
:key="strategy._id"
|
|
8
|
-
lines="
|
|
8
|
+
lines="two"
|
|
9
9
|
>
|
|
10
10
|
<template #prepend>
|
|
11
11
|
<v-radio :value="strategy._id"></v-radio>
|
|
12
12
|
</template>
|
|
13
13
|
|
|
14
|
-
<v-list-item-title class="text-
|
|
14
|
+
<v-list-item-title class="text-subtitle-2">
|
|
15
15
|
{{ strategy.name }}
|
|
16
16
|
</v-list-item-title>
|
|
17
17
|
|
|
18
|
-
<v-list-item-subtitle
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
<div><strong>Type:</strong> {{ strategy.implementingClass }}</div>
|
|
22
|
-
<div v-if="strategy.serializedData"><strong>Configuration:</strong> {{ getDisplayConfig(strategy) }}</div>
|
|
18
|
+
<v-list-item-subtitle class="text-caption">
|
|
19
|
+
{{ strategy.implementingClass }}
|
|
20
|
+
<span v-if="strategy.description"> • {{ strategy.description }}</span>
|
|
23
21
|
</v-list-item-subtitle>
|
|
24
22
|
|
|
25
23
|
<template #append>
|
|
26
24
|
<div class="d-flex">
|
|
27
|
-
<v-btn
|
|
28
|
-
|
|
25
|
+
<v-btn
|
|
26
|
+
icon
|
|
27
|
+
size="x-small"
|
|
28
|
+
variant="text"
|
|
29
|
+
title="Edit Strategy"
|
|
30
|
+
@click="$emit('edit', strategy)"
|
|
31
|
+
>
|
|
32
|
+
<v-icon size="small">mdi-pencil</v-icon>
|
|
29
33
|
</v-btn>
|
|
30
34
|
|
|
31
35
|
<v-btn
|
|
32
36
|
icon
|
|
33
|
-
size="small"
|
|
37
|
+
size="x-small"
|
|
38
|
+
variant="text"
|
|
34
39
|
title="Delete Strategy"
|
|
35
|
-
class="mr-1"
|
|
36
40
|
@click="$emit('delete', strategy)"
|
|
37
41
|
>
|
|
38
|
-
<v-icon>mdi-delete</v-icon>
|
|
42
|
+
<v-icon size="small">mdi-delete</v-icon>
|
|
39
43
|
</v-btn>
|
|
40
44
|
</div>
|
|
41
45
|
</template>
|
|
@@ -89,13 +93,6 @@ export default defineComponent({
|
|
|
89
93
|
|
|
90
94
|
<style scoped>
|
|
91
95
|
.navigation-strategy-list {
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
.strategy-details {
|
|
98
|
-
font-size: 0.9em;
|
|
99
|
-
color: rgba(0, 0, 0, 0.6);
|
|
96
|
+
/* Compact list styling */
|
|
100
97
|
}
|
|
101
98
|
</style>
|