From 37362acbb00751363c931d64eede7b5029860ae2 Mon Sep 17 00:00:00 2001 From: Bruce Schultz Date: Thu, 22 Oct 2026 09:24:50 +0200 Subject: [PATCH 1/6] feat(settings): add default autostart policy to settings menu --- app/assets/css/preferences.css | 10 ++++ app/components/preferences/AutostartField.vue | 49 ++++++++++++++----- .../preferences/PreferencesDialog.vue | 5 ++ app/services/Api.ts | 12 ++++- app/services/hub_adapter_swagger.json | 27 +++++++--- app/stores/nodeSettingsStore.ts | 5 ++ 6 files changed, 90 insertions(+), 18 deletions(-) diff --git a/app/assets/css/preferences.css b/app/assets/css/preferences.css index 2df677e0..0c8578f4 100644 --- a/app/assets/css/preferences.css +++ b/app/assets/css/preferences.css @@ -5,6 +5,16 @@ gap: 1rem; } +.settings-subsection { + margin-left: 2rem; +} + +.disabled { + opacity: 0.4; + pointer-events: none; + cursor: not-allowed; +} + .settings-description-title { font-weight: 600; margin-bottom: 0.75rem; diff --git a/app/components/preferences/AutostartField.vue b/app/components/preferences/AutostartField.vue index a87b0152..33116b6e 100644 --- a/app/components/preferences/AutostartField.vue +++ b/app/components/preferences/AutostartField.vue @@ -8,11 +8,13 @@ const autostartModel = defineModel({ required: true });

Autostart Analyses

- Immediately start an analysis as soon as it is detected in the hub for - the current node. If a data store is required for the analysis to run - and is not yet configured, it will not be started. + + Toggle the autostart feature. When enabled, individual projects can be + configured to allow associated analyses to immediately start as soon as + their image arrives at your node. If a data store is required for the + analysis to run and is not yet configured for the project, the analysis + will not start. +
({ required: true });
-
+
-

Autostart Interval

- How often (in seconds) the Hub should be queried to check for new - analyses. +

Default Autostart Policy

+ + The default autostart policy assigned to new projects registered in the + Hub for this node. When enabled, new projects will have autostart + enabled for their analyses by default. Autostart settings for individual + projects can still be configured in the Projects table. + +
+
+ +
+
+ +
+
+

Autostart Interval

+ + How often (in seconds) the Hub should be queried to check for new + analyses. +
({ enabled: store.autostartEnabled, interval: store.autostartInterval, + default_project_autostart_policy: store.autostartDefaultPolicy, }); const settingsLoaded = computed(() => store.settings !== null); @@ -24,6 +25,8 @@ function resetDrafts() { draftRequireDataStore.value = store.requireDataStoreSetting; draftAutostart.value.enabled = store.autostartEnabled; draftAutostart.value.interval = store.autostartInterval; + draftAutostart.value.default_project_autostart_policy = + store.autostartDefaultPolicy; } watch(preferencesVisible, (visible) => { @@ -43,6 +46,8 @@ async function onSubmitPreferences() { autostart: { enabled: draftAutostart.value.enabled, interval: draftAutostart.value.interval, + default_project_autostart_policy: + draftAutostart.value.default_project_autostart_policy, }, }); diff --git a/app/services/Api.ts b/app/services/Api.ts index f1f78e81..677a34b2 100644 --- a/app/services/Api.ts +++ b/app/services/Api.ts @@ -404,6 +404,16 @@ export interface AutostartSettings { * @default 60 */ interval?: number | null; + /** + * Default Project Autostart Policy + * @default false + */ + default_project_autostart_policy?: boolean; + /** + * Projects + * @default {} + */ + projects?: Record; } /** Body_get_token_token_post */ @@ -1854,7 +1864,7 @@ export interface UserSettings { * @default true */ require_data_store?: boolean | null; - /** @default {"enabled":false,"interval":60} */ + /** @default {"enabled":false,"interval":60,"default_project_autostart_policy":false,"projects":{}} */ autostart?: AutostartSettings | null; /** @default {"interval":600} */ kong_cleanup?: KongCleanupSettings | null; diff --git a/app/services/hub_adapter_swagger.json b/app/services/hub_adapter_swagger.json index 565a309a..329f7fcb 100644 --- a/app/services/hub_adapter_swagger.json +++ b/app/services/hub_adapter_swagger.json @@ -3164,7 +3164,7 @@ "anyOf": [ { "type": "integer", - "exclusiveMinimum": 0 + "exclusiveMinimum": 0.0 }, { "type": "null" @@ -3172,6 +3172,19 @@ ], "title": "Interval", "default": 60 + }, + "default_project_autostart_policy": { + "type": "boolean", + "title": "Default Project Autostart Policy", + "default": false + }, + "projects": { + "additionalProperties": { + "type": "boolean" + }, + "type": "object", + "title": "Projects", + "default": {} } }, "additionalProperties": false, @@ -3414,7 +3427,7 @@ }, "type": "object", "title": "CreateServiceRequestClientCertificate", - "description": "Certificate to be used as client certificate while TLS handshaking to the upstream server. With form-encoded, the notation is `client_certificate.id=\u003Cclient_certificate id\u003E`. With JSON, use `\"client_certificate\":{\"id\":\"\u003Cclient_certificate id\u003E\"}`." + "description": "Certificate to be used as client certificate while TLS handshaking to the upstream server. With form-encoded, the notation is `client_certificate.id=`. With JSON, use `\"client_certificate\":{\"id\":\"\"}`." }, "DataStoreType": { "type": "string", @@ -4315,7 +4328,7 @@ "anyOf": [ { "type": "integer", - "exclusiveMinimum": 0 + "exclusiveMinimum": 0.0 }, { "type": "null" @@ -6282,7 +6295,7 @@ "anyOf": [ { "type": "integer", - "exclusiveMinimum": 0 + "exclusiveMinimum": 0.0 }, { "type": "null" @@ -6295,7 +6308,7 @@ "anyOf": [ { "type": "integer", - "exclusiveMinimum": 0 + "exclusiveMinimum": 0.0 }, { "type": "null" @@ -6775,7 +6788,9 @@ ], "default": { "enabled": false, - "interval": 60 + "interval": 60, + "default_project_autostart_policy": false, + "projects": {} } }, "kong_cleanup": { diff --git a/app/stores/nodeSettingsStore.ts b/app/stores/nodeSettingsStore.ts index 8ecf832e..77f91eb6 100644 --- a/app/stores/nodeSettingsStore.ts +++ b/app/stores/nodeSettingsStore.ts @@ -21,6 +21,11 @@ export const useNodeSettingsStore = defineStore("nodeSettings", { autostartInterval(state): number { return state.settings?.autostart?.interval ?? 60; }, + autostartDefaultPolicy(state): boolean { + return Boolean( + state.settings?.autostart?.default_project_autostart_policy ?? false, + ); + }, }, actions: { async fetchSettings() { From e8cda05783672befae346755b02db0123a3f5d47 Mon Sep 17 00:00:00 2001 From: Bruce Schultz Date: Thu, 22 Oct 2026 10:58:38 +0200 Subject: [PATCH 2/6] feat(project): add autostart column and toggle to table --- app/assets/css/preferences.css | 1 + app/components/preferences/AutostartField.vue | 94 +++++------ .../preferences/PreferencesDialog.vue | 1 + app/components/projects/AutostartToggle.vue | 156 ++++++++++++++++++ app/components/projects/ProjectsTable.vue | 26 +++ app/stores/nodeSettingsStore.ts | 3 + 6 files changed, 235 insertions(+), 46 deletions(-) create mode 100644 app/components/projects/AutostartToggle.vue diff --git a/app/assets/css/preferences.css b/app/assets/css/preferences.css index 0c8578f4..9d0f877e 100644 --- a/app/assets/css/preferences.css +++ b/app/assets/css/preferences.css @@ -7,6 +7,7 @@ .settings-subsection { margin-left: 2rem; + margin-top: 1.5rem; } .disabled { diff --git a/app/components/preferences/AutostartField.vue b/app/components/preferences/AutostartField.vue index 33116b6e..d5ccd31e 100644 --- a/app/components/preferences/AutostartField.vue +++ b/app/components/preferences/AutostartField.vue @@ -23,53 +23,55 @@ const autostartModel = defineModel({ required: true }); />
- -
-
-

Default Autostart Policy

- - The default autostart policy assigned to new projects registered in the - Hub for this node. When enabled, new projects will have autostart - enabled for their analyses by default. Autostart settings for individual - projects can still be configured in the Projects table. - -
-
- +
+ +
+
+

Default Autostart Policy

+ + The default autostart policy assigned to new projects registered in + the Hub for this node. When enabled, new projects will have autostart + enabled for their analyses by default. Autostart settings for + individual projects can still be configured in the Projects table. + +
+
+ +
-
- -
-
-

Autostart Interval

- - How often (in seconds) the Hub should be queried to check for new - analyses. - -
-
- + +
+
+

Autostart Interval

+ + How often (in seconds) the Hub should be queried to check for new + analyses. + +
+
+ +
diff --git a/app/components/preferences/PreferencesDialog.vue b/app/components/preferences/PreferencesDialog.vue index ae1324c2..89814d7a 100644 --- a/app/components/preferences/PreferencesDialog.vue +++ b/app/components/preferences/PreferencesDialog.vue @@ -58,6 +58,7 @@ async function onSubmitPreferences() { detail: "Settings were successfully updated", life: 5000, }); + preferencesVisible.value = false; } else { toast.add({ severity: "error", diff --git a/app/components/projects/AutostartToggle.vue b/app/components/projects/AutostartToggle.vue new file mode 100644 index 00000000..8ba7d85b --- /dev/null +++ b/app/components/projects/AutostartToggle.vue @@ -0,0 +1,156 @@ + + + + + diff --git a/app/components/projects/ProjectsTable.vue b/app/components/projects/ProjectsTable.vue index fdc51119..9d3f7518 100644 --- a/app/components/projects/ProjectsTable.vue +++ b/app/components/projects/ProjectsTable.vue @@ -7,8 +7,10 @@ import { meterSegments, meterTooltip, } from "~/utils/project-status-meter"; +import { useNodeSettingsStore } from "~/stores/nodeSettingsStore.ts"; const filters = ref(); +const store = useNodeSettingsStore(); const { nodeType, requireDataStore } = useDataStoreRequirement(); const { projects, status, summariesLoading, truncated, refreshTable } = @@ -44,6 +46,7 @@ const updateFilters = (filterText: string) => { + + + + diff --git a/app/stores/nodeSettingsStore.ts b/app/stores/nodeSettingsStore.ts index 77f91eb6..669b6e34 100644 --- a/app/stores/nodeSettingsStore.ts +++ b/app/stores/nodeSettingsStore.ts @@ -26,6 +26,9 @@ export const useNodeSettingsStore = defineStore("nodeSettings", { state.settings?.autostart?.default_project_autostart_policy ?? false, ); }, + projectAutostartSettings(state): Record { + return state.settings?.autostart?.projects ?? {}; + }, }, actions: { async fetchSettings() { From 04a54d704c3e9321c39ab0c640c984397a59e812 Mon Sep 17 00:00:00 2001 From: Bruce Schultz Date: Thu, 22 Oct 2026 12:35:10 +0200 Subject: [PATCH 3/6] style(project): autostart confirmation dialog formatting --- app/assets/css/main.css | 10 +++- app/components/preferences/AutostartField.vue | 3 +- app/components/projects/AutostartToggle.vue | 54 +++++++++++++++---- 3 files changed, 54 insertions(+), 13 deletions(-) diff --git a/app/assets/css/main.css b/app/assets/css/main.css index f150e382..e591ed35 100644 --- a/app/assets/css/main.css +++ b/app/assets/css/main.css @@ -16,8 +16,8 @@ html { --page-padding-y: 1.5rem; /* PrimeVue runtime tokens — overridden by PrimeVue at hydration; declared here for IDE resolution */ - --p-primary-color: #ea580c; --p-text-muted-color: #64748b; + --p-primary-color: #ea580c; } .description-box { @@ -27,3 +27,11 @@ html { gap: 1rem; margin-bottom: 2rem; } + +[class='flame-light'] { + --icon-warning: #eaa001; +} + +[class='flame-dark'] { + --icon-warning: #d8cc00; +} \ No newline at end of file diff --git a/app/components/preferences/AutostartField.vue b/app/components/preferences/AutostartField.vue index d5ccd31e..802f305e 100644 --- a/app/components/preferences/AutostartField.vue +++ b/app/components/preferences/AutostartField.vue @@ -26,7 +26,7 @@ const autostartModel = defineModel({ required: true });
@@ -49,7 +49,6 @@ const autostartModel = defineModel({ required: true });

Autostart Interval

diff --git a/app/components/projects/AutostartToggle.vue b/app/components/projects/AutostartToggle.vue index 8ba7d85b..49d25def 100644 --- a/app/components/projects/AutostartToggle.vue +++ b/app/components/projects/AutostartToggle.vue @@ -14,6 +14,8 @@ const confirm = useConfirm(); const loading = ref(false); const store = useNodeSettingsStore(); +const confirmGroup = `autostart-${props.projectId}`; +const confirmAction = ref<"enable" | "disable">("enable"); const checked = ref( store.projectAutostartSettings[props.projectId] ?? @@ -21,10 +23,6 @@ const checked = ref( ); const toggleAutostart = () => { - if (loading.value) { - checked.value = !checked.value; - return; - } if (checked.value) { confirmRejection(); } else { @@ -33,10 +31,12 @@ const toggleAutostart = () => { }; const confirmApproval = () => { + confirmAction.value = "enable"; confirm.require({ - message: `Are you sure you want to enable autostart for project ${props.projectName}?\n\nAll future analyses for this project will be automatically started as soon as this node receives the image.`, + group: confirmGroup, + message: + "All future analyses for this project will be automatically started as soon as this node receives the image.", header: "Enable Autostart Confirmation", - icon: "pi pi-exclamation-triangle", rejectProps: { label: "Cancel", severity: "secondary", @@ -59,10 +59,11 @@ const confirmApproval = () => { }; const confirmRejection = () => { + confirmAction.value = "disable"; confirm.require({ - message: `Are you sure you want to disable autostart for project ${props.projectName}?\n\nAll future analyses will need to be manually started.`, + group: confirmGroup, + message: "All future analyses will need to be manually started.", header: "Disable Autostart Confirmation", - icon: "pi pi-exclamation-triangle", rejectProps: { label: "Cancel", severity: "secondary", @@ -115,6 +116,23 @@ async function onSubmitAutostartPreference(isEnabled: boolean) {