lula2 0.5.1-nightly.5 → 0.6.0
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/_app/immutable/chunks/CayEUOo2.js +3 -0
- package/dist/_app/immutable/chunks/{-QrdrDH3.js → DOBcdfgj.js} +1 -1
- package/dist/_app/immutable/chunks/aIhTG3uO.js +65 -0
- package/dist/_app/immutable/entry/{app.BWZxpS12.js → app.B7_zLkkx.js} +2 -2
- package/dist/_app/immutable/entry/start.CJGnwt1e.js +1 -0
- package/dist/_app/immutable/nodes/{0.BiH4WIws.js → 0.B9CL1cKq.js} +1 -1
- package/dist/_app/immutable/nodes/{1.C_oKiD_Q.js → 1.B5-Ctyqh.js} +1 -1
- package/dist/_app/immutable/nodes/{2.DyvyPU88.js → 2.hJcgnwrH.js} +1 -1
- package/dist/_app/immutable/nodes/{3.0az1kCvx.js → 3.Cln2iH1E.js} +1 -1
- package/dist/_app/immutable/nodes/{4.DdSc8cDv.js → 4.CcGLqOYE.js} +1 -1
- package/dist/_app/version.json +1 -1
- package/dist/cli/commands/crawl.js +2 -52
- package/dist/index.html +6 -6
- package/dist/index.js +2 -51
- package/package.json +21 -20
- package/src/lib/components/controls/ControlsList.svelte +82 -9
- package/src/lib/components/ui/FilterBuilder.svelte +167 -27
- package/src/lib/types.ts +1 -0
- package/src/stores/compliance.ts +13 -0
- package/dist/_app/immutable/chunks/DKTpWYsg.js +0 -65
- package/dist/_app/immutable/chunks/rqvYJuq0.js +0 -3
- package/dist/_app/immutable/entry/start.2E1kShC9.js +0 -1
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
type FilterValue,
|
|
10
10
|
activeFilters,
|
|
11
11
|
FILTER_OPERATORS,
|
|
12
|
-
getOperatorLabel
|
|
12
|
+
getOperatorLabel,
|
|
13
|
+
MAPPING_STATUS_OPTIONS
|
|
13
14
|
} from '$stores/compliance';
|
|
14
15
|
import { appState } from '$lib/websocket';
|
|
15
16
|
import { Filter, Add, TrashCan, ChevronDown, ChevronUp } from 'carbon-icons-svelte';
|
|
@@ -37,13 +38,18 @@
|
|
|
37
38
|
|
|
38
39
|
// Get field type for the selected field
|
|
39
40
|
$: selectedFieldSchema = $fieldSchema[newFilterField] || null;
|
|
40
|
-
$: selectedFieldType =
|
|
41
|
-
|
|
41
|
+
$: selectedFieldType =
|
|
42
|
+
getMappingFieldType(newFilterField) || selectedFieldSchema?.type || 'string';
|
|
43
|
+
$: selectedFieldUiType =
|
|
44
|
+
getMappingFieldUiType(newFilterField) || selectedFieldSchema?.ui_type || 'short_text';
|
|
42
45
|
$: isSelectField = selectedFieldUiType === 'select';
|
|
43
|
-
$: fieldOptions =
|
|
46
|
+
$: fieldOptions =
|
|
47
|
+
getMappingFieldOptions(newFilterField) ||
|
|
48
|
+
(isSelectField ? selectedFieldSchema?.options || [] : []);
|
|
44
49
|
|
|
45
|
-
// Force equals operator for select fields
|
|
46
|
-
|
|
50
|
+
// Force equals operator for select fields (but allow operators for mapping fields)
|
|
51
|
+
// Also force equals operator for has_mappings field, but allow operators for mapping_status
|
|
52
|
+
$: if (isSelectField && newFilterField !== 'mapping_status') {
|
|
47
53
|
newFilterOperator = 'equals';
|
|
48
54
|
}
|
|
49
55
|
|
|
@@ -51,6 +57,7 @@
|
|
|
51
57
|
$: fieldsByTab = {
|
|
52
58
|
overview: [] as string[],
|
|
53
59
|
implementation: [] as string[],
|
|
60
|
+
mappings: [] as string[],
|
|
54
61
|
custom: [] as string[]
|
|
55
62
|
};
|
|
56
63
|
|
|
@@ -58,19 +65,25 @@
|
|
|
58
65
|
// Reset arrays before populating
|
|
59
66
|
fieldsByTab.overview = [];
|
|
60
67
|
fieldsByTab.implementation = [];
|
|
68
|
+
fieldsByTab.mappings = [];
|
|
61
69
|
fieldsByTab.custom = [];
|
|
62
70
|
|
|
63
71
|
// Group fields by tab
|
|
64
72
|
availableFields.forEach((field) => {
|
|
65
|
-
|
|
66
|
-
if (
|
|
67
|
-
|
|
68
|
-
if (tab === 'overview') fieldsByTab.overview.push(field);
|
|
69
|
-
else if (tab === 'implementation') fieldsByTab.implementation.push(field);
|
|
70
|
-
else fieldsByTab.custom.push(field);
|
|
73
|
+
// Handle mapping-related fields specially
|
|
74
|
+
if (field === 'has_mappings' || field === 'mapping_status') {
|
|
75
|
+
fieldsByTab.mappings.push(field);
|
|
71
76
|
} else {
|
|
72
|
-
|
|
73
|
-
|
|
77
|
+
const schema = $fieldSchema[field];
|
|
78
|
+
if (schema) {
|
|
79
|
+
const tab = schema.tab || getDefaultTabForCategory(schema.category);
|
|
80
|
+
if (tab === 'overview') fieldsByTab.overview.push(field);
|
|
81
|
+
else if (tab === 'implementation') fieldsByTab.implementation.push(field);
|
|
82
|
+
else fieldsByTab.custom.push(field);
|
|
83
|
+
} else {
|
|
84
|
+
// If no schema, default to custom
|
|
85
|
+
fieldsByTab.custom.push(field);
|
|
86
|
+
}
|
|
74
87
|
}
|
|
75
88
|
});
|
|
76
89
|
}
|
|
@@ -81,12 +94,23 @@
|
|
|
81
94
|
// Create a new array from the readonly constant to make it mutable for Svelte
|
|
82
95
|
const operatorOptions = FILTER_OPERATORS.map((op) => ({ value: op.value, label: op.label }));
|
|
83
96
|
|
|
97
|
+
// Limited operators for mapping status field
|
|
98
|
+
const mappingStatusOperatorOptions = [
|
|
99
|
+
{ value: 'equals' as const, label: 'Equals' },
|
|
100
|
+
{ value: 'not_equals' as const, label: 'Not equals' }
|
|
101
|
+
];
|
|
102
|
+
|
|
84
103
|
// Add a new filter
|
|
85
104
|
function addFilter() {
|
|
86
105
|
if (!newFilterField) return;
|
|
87
106
|
|
|
88
107
|
let value = newFilterValue;
|
|
89
108
|
|
|
109
|
+
// Extract value from dropdown objects if necessary
|
|
110
|
+
if (typeof value === 'object' && value !== null && 'value' in value) {
|
|
111
|
+
value = (value as any).value;
|
|
112
|
+
}
|
|
113
|
+
|
|
90
114
|
// Convert value based on field type
|
|
91
115
|
let processedValue: FilterValue = value;
|
|
92
116
|
if (selectedFieldType === 'boolean' && typeof value === 'string') {
|
|
@@ -125,8 +149,54 @@
|
|
|
125
149
|
}
|
|
126
150
|
}
|
|
127
151
|
|
|
152
|
+
// Centralized mapping field metadata
|
|
153
|
+
const mappingFieldConfig: Record<
|
|
154
|
+
string,
|
|
155
|
+
{
|
|
156
|
+
type: string;
|
|
157
|
+
ui_type: string;
|
|
158
|
+
options?: Array<{ value: string; label: string }>;
|
|
159
|
+
}
|
|
160
|
+
> = {
|
|
161
|
+
has_mappings: {
|
|
162
|
+
type: 'boolean',
|
|
163
|
+
ui_type: 'select',
|
|
164
|
+
options: [
|
|
165
|
+
{ value: 'true', label: 'Yes' },
|
|
166
|
+
{ value: 'false', label: 'No' }
|
|
167
|
+
]
|
|
168
|
+
},
|
|
169
|
+
mapping_status: {
|
|
170
|
+
type: 'string',
|
|
171
|
+
ui_type: 'select',
|
|
172
|
+
options: MAPPING_STATUS_OPTIONS
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
function getMappingFieldType(fieldName: string): string | null {
|
|
177
|
+
return mappingFieldConfig[fieldName]?.type ?? null;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function getMappingFieldUiType(fieldName: string): string | null {
|
|
181
|
+
return mappingFieldConfig[fieldName]?.ui_type ?? null;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function getMappingFieldOptions(
|
|
185
|
+
fieldName: string
|
|
186
|
+
): Array<{ value: string; label: string }> | null {
|
|
187
|
+
return mappingFieldConfig[fieldName]?.options ?? null;
|
|
188
|
+
}
|
|
189
|
+
|
|
128
190
|
// Get display name for a field
|
|
129
191
|
function getFieldDisplayName(fieldName: string): string {
|
|
192
|
+
// Handle mapping fields specially
|
|
193
|
+
switch (fieldName) {
|
|
194
|
+
case 'has_mappings':
|
|
195
|
+
return 'Has Mappings';
|
|
196
|
+
case 'mapping_status':
|
|
197
|
+
return 'Mapping Status';
|
|
198
|
+
}
|
|
199
|
+
|
|
130
200
|
const schema = $fieldSchema[fieldName];
|
|
131
201
|
|
|
132
202
|
// Use schema names if available
|
|
@@ -145,9 +215,27 @@
|
|
|
145
215
|
if (filter.operator === 'exists') return 'exists';
|
|
146
216
|
if (filter.operator === 'not_exists') return 'does not exist';
|
|
147
217
|
|
|
218
|
+
// Convert value to string, handling objects and arrays properly
|
|
219
|
+
let displayValue = '';
|
|
220
|
+
if (filter.value === null || filter.value === undefined) {
|
|
221
|
+
displayValue = '';
|
|
222
|
+
} else if (typeof filter.value === 'object') {
|
|
223
|
+
// Handle objects by converting to JSON or getting a meaningful representation
|
|
224
|
+
if (Array.isArray(filter.value)) {
|
|
225
|
+
displayValue = filter.value.join(', ');
|
|
226
|
+
} else if ('value' in filter.value) {
|
|
227
|
+
// Handle dropdown option objects
|
|
228
|
+
displayValue = String((filter.value as any).value);
|
|
229
|
+
} else {
|
|
230
|
+
displayValue = JSON.stringify(filter.value);
|
|
231
|
+
}
|
|
232
|
+
} else {
|
|
233
|
+
displayValue = String(filter.value);
|
|
234
|
+
}
|
|
235
|
+
|
|
148
236
|
// Use the shared getOperatorLabel function
|
|
149
237
|
const operatorText = getOperatorLabel(filter.operator).toLowerCase();
|
|
150
|
-
return `${operatorText} "${
|
|
238
|
+
return `${operatorText} "${displayValue}"`;
|
|
151
239
|
}
|
|
152
240
|
|
|
153
241
|
// Handle click outside to close the panel
|
|
@@ -306,6 +394,33 @@
|
|
|
306
394
|
{/each}
|
|
307
395
|
{/if}
|
|
308
396
|
|
|
397
|
+
<!-- Mapping fields -->
|
|
398
|
+
{#if fieldsByTab.mappings.length > 0}
|
|
399
|
+
<!-- Divider -->
|
|
400
|
+
<div class="border-t border-gray-200 dark:border-gray-700 my-1"></div>
|
|
401
|
+
<div
|
|
402
|
+
class="px-3 py-1 text-xs font-semibold text-gray-500 dark:text-gray-400 bg-gray-50 dark:bg-gray-800/80"
|
|
403
|
+
>
|
|
404
|
+
Mapping Fields
|
|
405
|
+
</div>
|
|
406
|
+
{#each fieldsByTab.mappings as field (field)}
|
|
407
|
+
<button
|
|
408
|
+
class={twMerge(
|
|
409
|
+
'w-full text-left px-3 py-2 text-sm',
|
|
410
|
+
newFilterField === field
|
|
411
|
+
? 'bg-blue-50 dark:bg-blue-900/20 text-blue-700 dark:text-blue-300'
|
|
412
|
+
: 'hover:bg-gray-100 dark:hover:bg-gray-700/50'
|
|
413
|
+
)}
|
|
414
|
+
onclick={() => {
|
|
415
|
+
newFilterField = field;
|
|
416
|
+
showFieldDropdown = false;
|
|
417
|
+
}}
|
|
418
|
+
>
|
|
419
|
+
{getFieldDisplayName(field)}
|
|
420
|
+
</button>
|
|
421
|
+
{/each}
|
|
422
|
+
{/if}
|
|
423
|
+
|
|
309
424
|
<!-- Custom fields -->
|
|
310
425
|
{#if fieldsByTab.custom.length > 0}
|
|
311
426
|
<!-- Divider -->
|
|
@@ -343,8 +458,8 @@
|
|
|
343
458
|
>Operator</label
|
|
344
459
|
>
|
|
345
460
|
|
|
346
|
-
{#if isSelectField}
|
|
347
|
-
<!-- Disabled dropdown for select fields (always equals) -->
|
|
461
|
+
{#if (isSelectField && !['has_mappings', 'mapping_status'].includes(newFilterField)) || newFilterField === 'has_mappings'}
|
|
462
|
+
<!-- Disabled dropdown for select fields and has_mappings (always equals) -->
|
|
348
463
|
<div
|
|
349
464
|
class="px-3 py-2 text-sm rounded-md border border-gray-300 dark:border-gray-600 bg-gray-100 dark:bg-gray-800 text-gray-500 dark:text-gray-400"
|
|
350
465
|
>
|
|
@@ -354,7 +469,9 @@
|
|
|
354
469
|
<!-- Custom Operator Dropdown -->
|
|
355
470
|
<CustomDropdown
|
|
356
471
|
bind:value={newFilterOperator}
|
|
357
|
-
options={
|
|
472
|
+
options={newFilterField === 'mapping_status'
|
|
473
|
+
? mappingStatusOperatorOptions
|
|
474
|
+
: operatorOptions}
|
|
358
475
|
getDisplayValue={getOperatorLabel}
|
|
359
476
|
labelId="filter-operator"
|
|
360
477
|
/>
|
|
@@ -367,7 +484,30 @@
|
|
|
367
484
|
<label for="filter-value" class="block text-xs text-gray-600 dark:text-gray-400 mb-1"
|
|
368
485
|
>Value</label
|
|
369
486
|
>
|
|
370
|
-
{#if
|
|
487
|
+
{#if newFilterField === 'mapping_status'}
|
|
488
|
+
<!-- Special dropdown for mapping status -->
|
|
489
|
+
<select
|
|
490
|
+
id="filter-value"
|
|
491
|
+
bind:value={newFilterValue}
|
|
492
|
+
class="w-full rounded-md border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white text-sm"
|
|
493
|
+
>
|
|
494
|
+
<option value="">Select status...</option>
|
|
495
|
+
{#each MAPPING_STATUS_OPTIONS as status}
|
|
496
|
+
<option value={status.value}>{status.label}</option>
|
|
497
|
+
{/each}
|
|
498
|
+
</select>
|
|
499
|
+
{:else if newFilterField === 'has_mappings'}
|
|
500
|
+
<!-- Special dropdown for has_mappings -->
|
|
501
|
+
<select
|
|
502
|
+
id="filter-value"
|
|
503
|
+
bind:value={newFilterValue}
|
|
504
|
+
class="w-full rounded-md border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white text-sm"
|
|
505
|
+
>
|
|
506
|
+
<option value="">Select...</option>
|
|
507
|
+
<option value="true">Yes</option>
|
|
508
|
+
<option value="false">No</option>
|
|
509
|
+
</select>
|
|
510
|
+
{:else if isSelectField}
|
|
371
511
|
<!-- Custom dropdown for select fields -->
|
|
372
512
|
<CustomDropdown
|
|
373
513
|
bind:value={newFilterValue}
|
|
@@ -377,15 +517,15 @@
|
|
|
377
517
|
/>
|
|
378
518
|
{:else if selectedFieldType === 'boolean'}
|
|
379
519
|
<!-- Boolean field with CustomDropdown -->
|
|
380
|
-
<
|
|
520
|
+
<select
|
|
521
|
+
id="filter-value"
|
|
381
522
|
bind:value={newFilterValue}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
/>
|
|
523
|
+
class="w-full rounded-md border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-900 dark:text-white text-sm"
|
|
524
|
+
>
|
|
525
|
+
<option value="">Select...</option>
|
|
526
|
+
<option value="true">Yes</option>
|
|
527
|
+
<option value="false">No</option>
|
|
528
|
+
</select>
|
|
389
529
|
{:else}
|
|
390
530
|
<!-- Text input for other fields -->
|
|
391
531
|
<input
|
package/src/lib/types.ts
CHANGED
package/src/stores/compliance.ts
CHANGED
|
@@ -5,6 +5,15 @@ import type { Control, Mapping } from '$lib/types';
|
|
|
5
5
|
import { appState } from '$lib/websocket';
|
|
6
6
|
import { get, writable } from 'svelte/store';
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Shared mapping status options used across the application
|
|
10
|
+
*/
|
|
11
|
+
export const MAPPING_STATUS_OPTIONS = [
|
|
12
|
+
{ value: 'planned', label: 'Planned' },
|
|
13
|
+
{ value: 'implemented', label: 'Implemented' },
|
|
14
|
+
{ value: 'verified', label: 'Verified' }
|
|
15
|
+
];
|
|
16
|
+
|
|
8
17
|
/**
|
|
9
18
|
* Shared filter operator options used across the application
|
|
10
19
|
*/
|
|
@@ -133,6 +142,10 @@ export const complianceStore = {
|
|
|
133
142
|
});
|
|
134
143
|
}
|
|
135
144
|
|
|
145
|
+
// Add mapping-related fields for filtering
|
|
146
|
+
fieldSet.add('has_mappings');
|
|
147
|
+
fieldSet.add('mapping_status');
|
|
148
|
+
|
|
136
149
|
return Array.from(fieldSet).sort();
|
|
137
150
|
}
|
|
138
151
|
};
|