diff --git a/app/assets/css/preferences.css b/app/assets/css/preferences.css index 5e1eb24..2df677e 100644 --- a/app/assets/css/preferences.css +++ b/app/assets/css/preferences.css @@ -36,3 +36,11 @@ height: 2.5rem; width: 8rem; } + +.setting-not-applicable-text { + display: block; + margin-top: 0.5rem; + font-size: 0.875rem; + font-style: italic; + color: var(--p-text-muted-color); +} diff --git a/app/components/preferences/PreferencesDialog.vue b/app/components/preferences/PreferencesDialog.vue index 2c8977f..5b8c468 100644 --- a/app/components/preferences/PreferencesDialog.vue +++ b/app/components/preferences/PreferencesDialog.vue @@ -12,18 +12,31 @@ const loading = ref(false); const toast = useToast(); const store = useNodeSettingsStore(); -const draftRequireDataStore = ref(store.requireDataStore); +const draftRequireDataStore = ref(store.requireDataStoreSetting); +// Aggregator nodes never require a data store +const requireDataStoreDisabled = computed( + () => store.nodeType === "aggregator", +); const draftAutostart = ref({ enabled: store.autostartEnabled, interval: store.autostartInterval, }); +const settingsLoaded = computed(() => store.settings !== null); + +function resetDrafts() { + draftRequireDataStore.value = store.requireDataStoreSetting; + draftAutostart.value.enabled = store.autostartEnabled; + draftAutostart.value.interval = store.autostartInterval; +} + watch(preferencesVisible, (visible) => { - if (visible) { - draftRequireDataStore.value = store.requireDataStore; - draftAutostart.value.enabled = store.autostartEnabled; - draftAutostart.value.interval = store.autostartInterval; - } + if (visible) resetDrafts(); +}); + +// Update settings once fetched +watch(settingsLoaded, (loaded) => { + if (loaded) resetDrafts(); }); async function onSubmitPreferences() { @@ -68,7 +81,10 @@ async function onSubmitPreferences() { >
- +
@@ -81,6 +97,7 @@ async function onSubmitPreferences() { size="small" icon="pi pi-save" :loading="loading" + :disabled="!settingsLoaded" @click="onSubmitPreferences()" >
diff --git a/app/components/preferences/RequireDataStoreField.vue b/app/components/preferences/RequireDataStoreField.vue index e7262f9..6eb96f4 100644 --- a/app/components/preferences/RequireDataStoreField.vue +++ b/app/components/preferences/RequireDataStoreField.vue @@ -2,6 +2,8 @@ import ToggleSwitch from "primevue/toggleswitch"; const model = defineModel({ required: true }); + +withDefaults(defineProps<{ disabled?: boolean }>(), { disabled: false }); diff --git a/app/stores/nodeSettingsStore.ts b/app/stores/nodeSettingsStore.ts index 6b7d914..8ecf832 100644 --- a/app/stores/nodeSettingsStore.ts +++ b/app/stores/nodeSettingsStore.ts @@ -8,9 +8,12 @@ export const useNodeSettingsStore = defineStore("nodeSettings", { nodeType: null as string | null, }), getters: { + requireDataStoreSetting(state): boolean { + return Boolean(state.settings?.require_data_store ?? true); + }, requireDataStore(state): boolean { if (state.nodeType === "aggregator") return false; - return Boolean(state.settings?.require_data_store ?? true); + return this.requireDataStoreSetting; }, autostartEnabled(state): boolean { return Boolean(state.settings?.autostart?.enabled ?? false); diff --git a/test/components/preferences/PreferencesDialog.spec.ts b/test/components/preferences/PreferencesDialog.spec.ts index df04dd4..68bdb14 100644 --- a/test/components/preferences/PreferencesDialog.spec.ts +++ b/test/components/preferences/PreferencesDialog.spec.ts @@ -56,6 +56,7 @@ describe("PreferencesDialog.vue", () => { mockUpdateSettings = vi.fn().mockResolvedValue(defaultSettingsResponse); vi.mocked(useNodeSettingsStore).mockReturnValue({ requireDataStore: true, + requireDataStoreSetting: true, autostartEnabled: false, autostartInterval: 60, updateSettings: mockUpdateSettings, @@ -91,6 +92,7 @@ describe("PreferencesDialog.vue", () => { it("initializes RequireDataStoreField as false when store has false", () => { vi.mocked(useNodeSettingsStore).mockReturnValue({ requireDataStore: false, + requireDataStoreSetting: false, autostartEnabled: true, autostartInterval: 30, updateSettings: mockUpdateSettings, @@ -159,6 +161,7 @@ describe("PreferencesDialog.vue", () => { it("resets draft values from the store when the dialog reopens", async () => { vi.mocked(useNodeSettingsStore).mockReturnValue({ requireDataStore: false, + requireDataStoreSetting: false, autostartEnabled: true, autostartInterval: 30, updateSettings: mockUpdateSettings, diff --git a/test/components/preferences/PreferencesDialogStore.spec.ts b/test/components/preferences/PreferencesDialogStore.spec.ts new file mode 100644 index 0000000..28d80c4 --- /dev/null +++ b/test/components/preferences/PreferencesDialogStore.spec.ts @@ -0,0 +1,235 @@ +import { flushPromises, mount } from "@vue/test-utils"; +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { createPinia, setActivePinia } from "pinia"; +import PreferencesDialog from "~/components/preferences/PreferencesDialog.vue"; +import { useNodeSettingsStore } from "~/stores/nodeSettingsStore"; +import type { UserSettings } from "~/services/Api"; + +const DialogStub = { + props: ["visible", "header", "modal", "style"], + template: '
', +}; + +const RequireDataStoreFieldStub = { + name: "RequireDataStoreField", + props: ["modelValue", "disabled"], + emits: ["update:modelValue"], + template: + '