Skip to content

Commit 7f7ba91

Browse files
committed
feat: multi-tab discovery edit modal with sequential create flow
Split DiscoveryTypeForm into DiscoveryTypeConfigForm (type-specific settings) and DiscoveryScheduleForm (schedule fields). Move discovery type and run type selectors to Details tab. Remove tags from modal. Create mode uses sequential tab navigation with Next/Back buttons and progressive tab unlocking. Edit mode allows free tab navigation. Tabs dynamically adapt: Configuration tab hidden for SelfReport, Schedule tab hidden for AdHoc runs.
1 parent 0673c9b commit 7f7ba91

File tree

9 files changed

+794
-692
lines changed

9 files changed

+794
-692
lines changed

messages/de.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
"common_close": "",
7373
"common_closeModal": "",
7474
"common_color": "",
75+
"common_configuration": "",
7576
"common_confirm": "",
7677
"common_confirmAction": "",
7778
"common_confirmDeleteName": "",
@@ -226,6 +227,7 @@
226227
"common_revoke": "Widerrufen",
227228
"common_role": "Rolle",
228229
"common_save": "Speichern",
230+
"common_schedule": "",
229231
"common_saveChanges": "Änderungen speichern",
230232
"common_saving": "Wird gespeichert...",
231233
"common_schedule": "",
@@ -389,7 +391,6 @@
389391
"discovery_adHocDescription": "",
390392
"discovery_allSubnetsScanned": "",
391393
"discovery_bestService": "",
392-
"discovery_configuration": "",
393394
"discovery_confirmDeleteHistorical": "",
394395
"discovery_confirmDeleteScheduled": "",
395396
"discovery_couldNotGetNetworkId": "",
@@ -399,7 +400,6 @@
399400
"discovery_daemonHostMissing": "",
400401
"discovery_daemonHostMissingHelp": "",
401402
"discovery_daemonSelect": "",
402-
"discovery_details": "",
403403
"discovery_discoveryType": "",
404404
"discovery_dockerScan": "",
405405
"discovery_edit": "",
@@ -426,7 +426,6 @@
426426
"discovery_oneDay": "",
427427
"discovery_oneHour": "",
428428
"discovery_runType": "",
429-
"discovery_scheduleConfiguration": "",
430429
"discovery_scheduleHelp": "",
431430
"discovery_scheduled": "",
432431
"discovery_scheduledDescription": "",

messages/en.json

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@
7070
"common_close": "Close",
7171
"common_closeModal": "Close modal",
7272
"common_color": "Color",
73+
"common_configuration": "Configuration",
7374
"common_configure": "Setup",
7475
"common_confirm": "Confirm",
7576
"common_confirmAction": "Confirm Action",
@@ -233,6 +234,7 @@
233234
"common_save": "Save",
234235
"common_saveChanges": "Save Changes",
235236
"common_saving": "Saving...",
237+
"common_schedule": "Schedule",
236238
"common_searchPlaceholder": "Search...",
237239
"common_seats": "Seats",
238240
"common_selectAll": "Select all",
@@ -424,7 +426,6 @@
424426
"discovery_adHocDescription": "This discovery will only run when manually triggered",
425427
"discovery_allSubnetsScanned": "All subnets in network will be scanned",
426428
"discovery_bestService": "Best Service",
427-
"discovery_configuration": "Discovery Configuration",
428429
"discovery_confirmDeleteHistorical": "Are you sure you want to delete {count} Historical Discoveries?",
429430
"discovery_confirmDeleteScheduled": "Are you sure you want to delete {count} Scheduled Discoveries?",
430431
"discovery_couldNotGetNetworkId": "Could not get network ID from selected daemon. Please try a different daemon.",
@@ -434,7 +435,6 @@
434435
"discovery_daemonHostMissing": "Daemon host is missing",
435436
"discovery_daemonHostMissingHelp": "Could not find a host associated to the selected daemon. It may have been deleted or corrupted. Please recreate the daemon.",
436437
"discovery_daemonSelect": "Select daemon to run discovery...",
437-
"discovery_details": "Discovery Details",
438438
"discovery_discoveryType": "Discovery Type",
439439
"discovery_dockerScan": "Docker Scan",
440440
"discovery_edit": "Edit Discovery: {name}",
@@ -457,7 +457,6 @@
457457
"discovery_nonInterfacedSubnetWarning": "The selected daemon does not have a direct network interface with the following subnets: \n{subnets}. \nYou can still include them, but hostnames and MAC addresses will not be available for any discovered hosts.",
458458
"discovery_notStarted": "Not Started",
459459
"discovery_runType": "Run Type",
460-
"discovery_scheduleConfiguration": "Schedule Configuration",
461460
"discovery_scheduleCronExpression": "Cron Expression",
462461
"discovery_scheduleCronInfo": "This schedule uses a custom cron expression. Click 'Reset to day picker' to use the visual editor.",
463462
"discovery_scheduleDaysOfWeek": "Days of Week",
@@ -586,10 +585,7 @@
586585
"hosts_ifEntries_aliasDescription": "Alias / Description",
587586
"hosts_ifEntries_cdpNeighbor": "CDP Neighbor Information",
588587
"hosts_ifEntries_chassisId": "Chassis ID",
589-
"hosts_ifEntries_details": "Interface Details",
590-
"hosts_ifEntries_ifName": "Interface Name",
591588
"hosts_ifEntries_index": "Index: {index}",
592-
"hosts_ifEntries_interfaceId": "Linked Interface",
593589
"hosts_ifEntries_lldpNeighbor": "LLDP Neighbor Information",
594590
"hosts_ifEntries_lldpSysDescr": "System Description",
595591
"hosts_ifEntries_managementAddress": "Management Address",
@@ -605,9 +601,7 @@
605601
"hosts_ifEntries_selectToView": "Select an interface to view details",
606602
"hosts_ifEntries_subtitle": "Entries from host's ifTable collected via SNMP",
607603
"hosts_ifEntries_title": "ifTable Entries",
608-
"hosts_ifEntries_type": "Interface Type",
609-
"hosts_ifEntries_unresolvedInterface": "Unresolved interface",
610-
"hosts_interfaces_deleteMessage": "This interface has bindings from the following services. Deleting it will remove those bindings.",
604+
"hosts_interfaces_deleteMessage": "This interface has bindings from the following services. Deleting it will remove those bindings.",
611605
"hosts_interfaces_deleteTitle": "Delete Interface",
612606
"hosts_interfaces_emptyMessage": "No interfaces configured. Add one to get started.",
613607
"hosts_interfaces_helpText": "Configure network interfaces and addresses. Drag to reorder.",
@@ -618,8 +612,7 @@
618612
"hosts_interfaces_placeholder": "Select subnet to create interface with...",
619613
"hosts_interfaces_selectToConfig": "Select an interface from the list to configure it",
620614
"hosts_interfaces_subnet": "Subnet {name}",
621-
"hosts_interfaces_subnetInterface": "Interface with subnet \"{name}\"",
622-
"hosts_noContainers": "No containers",
615+
"hosts_noContainers": "No containers",
623616
"hosts_noHostsYet": "No hosts configured yet",
624617
"hosts_noInterfaces": "No interfaces",
625618
"hosts_noServicesAssigned": "No services assigned",

messages/fr.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
"common_close": "Fermer",
7070
"common_closeModal": "Fermer modal",
7171
"common_color": "Couleur",
72+
"common_configuration": "",
7273
"common_confirm": "Confirmer",
7374
"common_confirmAction": "Confirmer Action",
7475
"common_confirmDeleteName": "Etes-vous sûr de vouloir supprimer \"{name}\"?",
@@ -224,6 +225,7 @@
224225
"common_revoke": "",
225226
"common_role": "",
226227
"common_save": "",
228+
"common_schedule": "",
227229
"common_saveChanges": "",
228230
"common_saving": "",
229231
"common_schedule": "",
@@ -397,7 +399,6 @@
397399
"discovery_adHocDescription": "",
398400
"discovery_allSubnetsScanned": "",
399401
"discovery_bestService": "",
400-
"discovery_configuration": "",
401402
"discovery_confirmDeleteHistorical": "",
402403
"discovery_confirmDeleteScheduled": "",
403404
"discovery_couldNotGetNetworkId": "",
@@ -407,7 +408,6 @@
407408
"discovery_daemonHostMissing": "",
408409
"discovery_daemonHostMissingHelp": "",
409410
"discovery_daemonSelect": "",
410-
"discovery_details": "",
411411
"discovery_discoveryType": "",
412412
"discovery_dockerScan": "",
413413
"discovery_edit": "",
@@ -434,7 +434,6 @@
434434
"discovery_oneDay": "",
435435
"discovery_oneHour": "",
436436
"discovery_runType": "",
437-
"discovery_scheduleConfiguration": "",
438437
"discovery_scheduleHelp": "",
439438
"discovery_scheduled": "",
440439
"discovery_scheduledDescription": "",

messages/pt.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
"common_close": "",
7373
"common_closeModal": "",
7474
"common_color": "",
75+
"common_configuration": "",
7576
"common_confirm": "",
7677
"common_confirmAction": "",
7778
"common_confirmDeleteName": "",
@@ -226,6 +227,7 @@
226227
"common_revoke": "",
227228
"common_role": "",
228229
"common_save": "",
230+
"common_schedule": "",
229231
"common_saveChanges": "",
230232
"common_saving": "",
231233
"common_schedule": "",
@@ -389,7 +391,6 @@
389391
"discovery_adHocDescription": "",
390392
"discovery_allSubnetsScanned": "",
391393
"discovery_bestService": "",
392-
"discovery_configuration": "",
393394
"discovery_confirmDeleteHistorical": "",
394395
"discovery_confirmDeleteScheduled": "",
395396
"discovery_couldNotGetNetworkId": "",
@@ -399,7 +400,6 @@
399400
"discovery_daemonHostMissing": "",
400401
"discovery_daemonHostMissingHelp": "",
401402
"discovery_daemonSelect": "",
402-
"discovery_details": "",
403403
"discovery_discoveryType": "",
404404
"discovery_dockerScan": "",
405405
"discovery_edit": "",
@@ -426,7 +426,6 @@
426426
"discovery_oneDay": "",
427427
"discovery_oneHour": "",
428428
"discovery_runType": "",
429-
"discovery_scheduleConfiguration": "",
430429
"discovery_scheduleHelp": "",
431430
"discovery_scheduled": "",
432431
"discovery_scheduledDescription": "",

ui/src/lib/features/discovery/components/DiscoveryModal/DiscoveryDetailsForm.svelte

Lines changed: 145 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,34 @@
22
import type { AnyFieldApi } from '@tanstack/svelte-form';
33
import { required, max } from '$lib/shared/components/forms/validators';
44
import TextInput from '$lib/shared/components/forms/input/TextInput.svelte';
5+
import SelectInput from '$lib/shared/components/forms/input/SelectInput.svelte';
56
import RichSelect from '$lib/shared/components/forms/selection/RichSelect.svelte';
67
import { DaemonDisplay } from '$lib/shared/components/forms/selection/display/DaemonDisplay.svelte';
8+
import {
9+
SimpleOptionDisplay,
10+
type SimpleOption
11+
} from '$lib/shared/components/forms/selection/display/SimpleOptionDisplay';
12+
import type { DockerDiscovery, NetworkDiscovery, SelfReportDiscovery } from '../../types/api';
713
import type { Discovery } from '../../types/base';
814
import type { Daemon } from '$lib/features/daemons/types/base';
9-
import TagPicker from '$lib/features/tags/components/TagPicker.svelte';
15+
import { discoveryTypes } from '$lib/shared/stores/metadata';
16+
import { openModal } from '$lib/shared/stores/modal-registry';
17+
import { ArrowUpCircle } from 'lucide-svelte';
1018
import {
1119
common_daemon,
20+
discovery_adHoc,
21+
discovery_adHocDescription,
1222
discovery_daemonHelp,
1323
discovery_daemonSelect,
14-
discovery_details,
24+
discovery_discoveryType,
25+
discovery_dockerScan,
1526
discovery_name,
16-
discovery_namePlaceholder
27+
discovery_namePlaceholder,
28+
discovery_networkScan,
29+
discovery_runType,
30+
discovery_scheduled,
31+
discovery_scheduledDescription,
32+
discovery_selfReport
1733
} from '$lib/paraglide/messages';
1834
1935
interface Props {
@@ -22,14 +38,90 @@
2238
formData: Discovery;
2339
daemons?: Daemon[];
2440
readOnly?: boolean;
41+
hasScheduledDiscovery?: boolean;
42+
daemonHostId?: string | null;
43+
daemon?: Daemon | null;
44+
}
45+
46+
let {
47+
form,
48+
formData = $bindable(),
49+
daemons = [],
50+
readOnly = false,
51+
hasScheduledDiscovery = true,
52+
daemonHostId = null,
53+
daemon = null
54+
}: Props = $props();
55+
56+
let discoveryTypeOptions = $derived([
57+
{ value: 'Network', label: discovery_networkScan(), disabled: false },
58+
{
59+
value: 'Docker',
60+
label: discovery_dockerScan(),
61+
disabled: daemonHostId == null || !daemon?.capabilities.has_docker_socket
62+
},
63+
{ value: 'SelfReport', label: discovery_selfReport(), disabled: daemonHostId == null }
64+
]);
65+
66+
function handleDiscoveryTypeChange(value: string) {
67+
if (value === 'Network' && formData.discovery_type.type !== 'Network') {
68+
formData.discovery_type = {
69+
type: 'Network',
70+
subnet_ids: daemon?.capabilities.interfaced_subnet_ids ?? [],
71+
host_naming_fallback: 'BestService',
72+
probe_raw_socket_ports: false
73+
} as NetworkDiscovery;
74+
} else if (value === 'Docker' && formData.discovery_type.type !== 'Docker') {
75+
formData.discovery_type = {
76+
type: 'Docker',
77+
host_id: daemonHostId,
78+
host_naming_fallback: 'BestService'
79+
} as DockerDiscovery;
80+
} else if (value === 'SelfReport' && formData.discovery_type.type !== 'SelfReport') {
81+
formData.discovery_type = {
82+
type: 'SelfReport',
83+
host_id: daemonHostId
84+
} as SelfReportDiscovery;
85+
}
2586
}
2687
27-
let { form, formData = $bindable(), daemons = [], readOnly = false }: Props = $props();
88+
let runTypeOptions: SimpleOption[] = $derived([
89+
{ value: 'AdHoc', label: discovery_adHoc() },
90+
{
91+
value: 'Scheduled',
92+
label: discovery_scheduled(),
93+
disabled: !hasScheduledDiscovery,
94+
tags: !hasScheduledDiscovery
95+
? [
96+
{
97+
label: 'Upgrade',
98+
color: 'Yellow',
99+
icon: ArrowUpCircle
100+
}
101+
]
102+
: []
103+
}
104+
]);
105+
106+
function handleRunTypeChange(value: string) {
107+
if (value === 'AdHoc' && formData.run_type.type !== 'AdHoc') {
108+
formData.run_type = {
109+
type: 'AdHoc',
110+
last_run: null
111+
};
112+
} else if (value === 'Scheduled' && formData.run_type.type !== 'Scheduled') {
113+
formData.run_type = {
114+
type: 'Scheduled',
115+
cron_schedule: '0 0 0 * * *',
116+
last_run: null,
117+
enabled: true,
118+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone
119+
};
120+
}
121+
}
28122
</script>
29123

30124
<div class="space-y-4">
31-
<h3 class="text-primary text-lg font-medium">{discovery_details()}</h3>
32-
33125
<form.Field
34126
name="name"
35127
validators={{
@@ -68,5 +160,51 @@
68160
<p class="text-tertiary text-xs">{discovery_daemonHelp()}</p>
69161
</div>
70162

71-
<TagPicker bind:selectedTagIds={formData.tags} />
163+
<!-- Run Type Selection -->
164+
<form.Field
165+
name="run_type_type"
166+
listeners={{
167+
onChange: ({ value }: { value: string }) => handleRunTypeChange(value)
168+
}}
169+
>
170+
{#snippet children(field: AnyFieldApi)}
171+
<RichSelect
172+
label={discovery_runType()}
173+
selectedValue={field.state.value}
174+
options={runTypeOptions}
175+
onSelect={(value) => field.handleChange(value)}
176+
onDisabledClick={() => openModal('billing-plan')}
177+
displayComponent={SimpleOptionDisplay}
178+
disabled={readOnly}
179+
/>
180+
<p class="text-tertiary mt-1 text-xs">
181+
{field.state.value === 'AdHoc'
182+
? discovery_adHocDescription()
183+
: discovery_scheduledDescription()}
184+
</p>
185+
{/snippet}
186+
</form.Field>
187+
188+
<!-- Discovery Type Selection -->
189+
{#if daemon}
190+
<form.Field
191+
name="discovery_type_type"
192+
listeners={{
193+
onChange: ({ value }: { value: string }) => handleDiscoveryTypeChange(value)
194+
}}
195+
>
196+
{#snippet children(field: AnyFieldApi)}
197+
<SelectInput
198+
label={discovery_discoveryType()}
199+
id="discovery_type"
200+
options={discoveryTypeOptions}
201+
{field}
202+
disabled={readOnly}
203+
/>
204+
<p class="text-tertiary mt-1 text-xs">
205+
{discoveryTypes.getDescription(field.state.value)}
206+
</p>
207+
{/snippet}
208+
</form.Field>
209+
{/if}
72210
</div>

0 commit comments

Comments
 (0)