Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion app/assets/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -27,3 +27,11 @@ html {
gap: 1rem;
margin-bottom: 2rem;
}

[class='flame-light'] {
--icon-warning: #eaa001;
}

[class='flame-dark'] {
--icon-warning: #d8cc00;
}
11 changes: 11 additions & 0 deletions app/assets/css/preferences.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,17 @@
gap: 1rem;
}

.settings-subsection {
margin-left: 2rem;
margin-top: 1.5rem;
}

.disabled {
opacity: 0.4;
pointer-events: none;
cursor: not-allowed;
}
Comment thread
brucetony marked this conversation as resolved.

.settings-description-title {
font-weight: 600;
margin-bottom: 0.75rem;
Expand Down
76 changes: 50 additions & 26 deletions app/components/preferences/AutostartField.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ const autostartModel = defineModel<AutostartSettings>({ required: true });
<div class="settings-field autostart-enabled-field">
<div class="settings-description">
<h3 class="settings-description-title">Autostart Analyses</h3>
<span class="setting-description-text"
>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.</span
>
<span class="setting-description-text">
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.
</span>
</div>
<div class="settings-control autostart-toggle">
<ToggleSwitch
Expand All @@ -21,28 +23,50 @@ const autostartModel = defineModel<AutostartSettings>({ required: true });
/>
</div>
</div>
<Divider />
<div class="settings-field autostart-interval-field">
<div class="settings-description">
<h3 class="settings-description-title">Autostart Interval</h3>
<span class="setting-description-text"
>How often (in seconds) the Hub should be queried to check for new
analyses.</span
>
<div class="settings-subsection">
<Divider type="dashed" />
<div
class="settings-field autostart-default-policy-field"
:class="{ disabled: !autostartModel.enabled }"
>
<div class="settings-description">
<h4 class="settings-description-title">Default Autostart Policy</h4>
<span class="setting-description-text">
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.
</span>
</div>
<div class="settings-control autostart-toggle">
<ToggleSwitch
v-model="autostartModel.default_project_autostart_policy!"
label="Default Autostart Policy"
/>
</div>
</div>
<div class="settings-control autostart-interval-input">
<InputNumber
v-model="autostartModel.interval"
v-tooltip.top="{
value: 'Autostart is disabled!',
disabled: autostartModel.enabled,
}"
:disabled="!autostartModel.enabled"
:min="1"
:invalid="autostartModel.enabled === null"
label="Autostart Analyses"
fluid
/>
<Divider type="dashed" />
<div
class="settings-field autostart-interval-field"
:class="{ disabled: !autostartModel.enabled }"
>
<div class="settings-description">
<h4 class="settings-description-title">Autostart Interval</h4>
<span class="setting-description-text">
How often (in seconds) the Hub should be queried to check for new
analyses.
</span>
</div>
<div class="settings-control autostart-interval-input">
<InputNumber
v-model="autostartModel.interval"
:disabled="!autostartModel.enabled"
:min="1"
:invalid="autostartModel.enabled === null"
label="Autostart Analyses"
fluid
/>
</div>
</div>
</div>
</template>
Expand Down
6 changes: 6 additions & 0 deletions app/components/preferences/PreferencesDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const requireDataStoreDisabled = computed(
const draftAutostart = ref<AutostartSettings>({
enabled: store.autostartEnabled,
interval: store.autostartInterval,
default_project_autostart_policy: store.autostartDefaultPolicy,
});

const settingsLoaded = computed(() => store.settings !== null);
Expand All @@ -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) => {
Expand All @@ -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,
},
});

Expand All @@ -53,6 +58,7 @@ async function onSubmitPreferences() {
detail: "Settings were successfully updated",
life: 5000,
});
preferencesVisible.value = false;
} else {
toast.add({
severity: "error",
Expand Down
199 changes: 199 additions & 0 deletions app/components/projects/AutostartToggle.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
<script lang="ts" setup>
import { useToast } from "primevue/usetoast";
import Tag from "primevue/tag";
import { useConfirm } from "primevue/useconfirm";
import { useNodeSettingsStore } from "~/stores/nodeSettingsStore.ts";

const props = defineProps<{
projectId: string;
projectName?: string | null;
}>();

const toast = useToast();
const confirm = useConfirm();
const loading = ref(false);

const store = useNodeSettingsStore();
const confirmGroup = `autostart-${props.projectId}`;
const confirmAction = ref<"enable" | "disable">("enable");

const enabledAutostart = computed(
() =>
store.projectAutostartSettings[props.projectId] ??
store.autostartDefaultPolicy,
);

const checked = ref(enabledAutostart.value);

watch(enabledAutostart, (value) => {
checked.value = value;
});

const toggleAutostart = () => {
if (checked.value) {
confirmRejection();
} else {
confirmApproval();
}
};

const confirmApproval = () => {
confirmAction.value = "enable";
confirm.require({
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",
rejectProps: {
label: "Cancel",
severity: "secondary",
outlined: true,
},
acceptProps: {
label: "Submit",
},
accept: () => {
onSubmitAutostartPreference(true);
},
reject: () => {
// Reset the toggle
checked.value = !checked.value;
},
onHide: () => {
checked.value = !checked.value;
},
});
};

const confirmRejection = () => {
confirmAction.value = "disable";
confirm.require({
group: confirmGroup,
message: "All future analyses will need to be manually started.",
header: "Disable Autostart Confirmation",
rejectProps: {
label: "Cancel",
severity: "secondary",
outlined: true,
},
acceptProps: {
label: "Submit",
},
accept: () => {
onSubmitAutostartPreference(false);
},
reject: () => {
// Reset the toggle
checked.value = !checked.value;
},
onHide: () => {
checked.value = !checked.value;
},
});
};

async function onSubmitAutostartPreference(isEnabled: boolean) {
loading.value = true;

const updated = await store.updateSettings({
autostart: {
projects: { [props.projectId]: isEnabled },
},
});

if (updated) {
toast.add({
severity: "success",
summary: "Update Success",
detail: "Autostart settings were successfully updated",
life: 5000,
});
} else {
// If no response/failed, reset the updated toggle
checked.value = !checked.value;
toast.add({
severity: "error",
summary: "Update Failed",
detail: "Autostart settings could not be updated",
life: 5000,
});
}

loading.value = false;
}
Comment thread
brucetony marked this conversation as resolved.
</script>

<template>
<div class="autostart-toggle">
<ConfirmDialog :group="confirmGroup">
<template #message="{ message }">
<div class="autostart-confirm-message">
<i
class="pi pi-exclamation-triangle autostart-confirm-message-icon"
/>
<div>
<p>
Are you sure you want to {{ confirmAction }} autostart for project
<b class="autostart-confirm-project">{{ projectName }}</b
>?
</p>
<p>{{ message.message }}</p>
</div>
</div>
</template>
</ConfirmDialog>
<div
class="autostart-status-tag autostart-tag-disabled"
:class="{ dimmed: checked }"
>
<Tag severity="danger" value="disabled" />
</div>
<ToggleSwitch
v-model="checked"
:disabled="loading"
class="autostart-toggle-switch"
label="Autostart Toggle"
@click="toggleAutostart()"
/>
<div
class="autostart-status-tag autostart-tag-enabled"
:class="{ dimmed: !checked }"
>
<Tag severity="success" value="enabled" />
</div>
</div>
</template>

<style lang="scss" scoped>
.autostart-toggle {
display: flex;
align-items: center;
}

.autostart-status-tag {
padding: 0.3em;

&.dimmed {
opacity: 0.5;
}
}

.autostart-confirm-message {
display: flex;
gap: 1rem;
align-items: center;
}

.autostart-confirm-message-icon {
font-size: 4rem;
color: var(--icon-warning);
}

.autostart-confirm-icon {
font-size: 1.5rem;
}

.autostart-confirm-project {
color: var(--p-primary-color);
}
</style>
Loading
Loading