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
3 changes: 3 additions & 0 deletions client/src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import router from './router';
import setupHttpInterceptor from './js/http-interceptor';
import { getWebSocketURL, isElectron } from '@/js/platform';
import { initRemoteLogging } from '@/js/logger';
import log from 'loglevel';

import './assets/styles/dark.scss';
import 'vue-toast-notification/dist/theme-sugar.css';
Expand All @@ -22,6 +23,8 @@ import 'splitpanes/dist/splitpanes.css';
setupHttpInterceptor();
initRemoteLogging();

log.info(`Running in ${import.meta.env.MODE} mode.`);

Vue.use(BootstrapVue);
Vue.use(IconsPlugin);
Vue.component('MultiSelect', Multiselect);
Expand Down
16 changes: 16 additions & 0 deletions client/src/store/modules/system.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export default {
availableShows: [],
rawSettings: {},
rbacRoles: [],
settingsCategories: {},
},
mutations: {
UPDATE_SETTINGS(state, settings) {
Expand All @@ -23,6 +24,9 @@ export default {
UPDATE_RBAC_ROLES(state, rbac) {
state.rbacRoles = rbac;
},
UPDATE_SETTINGS_CATEGORIES(state, categories) {
state.settingsCategories = categories;
},
},
actions: {
async GET_AVAILABLE_SHOWS(context) {
Expand Down Expand Up @@ -88,6 +92,15 @@ export default {
log.error('Unable to fetch RBAC roles');
}
},
async GET_SETTINGS_CATEGORIES(context) {
const response = await fetch(makeURL('/api/v1/settings/categories'));
if (response.ok) {
const categories = await response.json();
await context.commit('UPDATE_SETTINGS_CATEGORIES', categories.categories);
} else {
log.error('Unable to fetch settings categories');
}
},
},
getters: {
AVAILABLE_SHOWS(state) {
Expand All @@ -102,5 +115,8 @@ export default {
RBAC_ROLES(state) {
return state.rbacRoles;
},
SETTINGS_CATEGORIES(state) {
return state.settingsCategories;
},
},
};
190 changes: 132 additions & 58 deletions client/src/vue_components/config/ConfigSettings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,62 +9,96 @@
@reset.stop.prevent="resetForm(true)"
>
<div>
<b-form-group
v-for="(setting, key) in visibleSettings"
:id="`${key}-input-group`"
:key="key"
:label-for="`${key}-input`"
:label-cols="true"
<b-card
v-for="(settings, category) in settingsByCategory"
:key="`settings-${category}`"
no-body
class="section-card mt-2"
>
<b-card-header class="section-card-header" @click="expandCategory(category)">
<div class="d-flex justify-content-between align-items-center">
<span>
{{ category }}
<b-badge variant="success" class="ml-1">
{{ Object.keys(settings).length - dirtySettingsByCategory[category] }}
</b-badge>
<b-badge
v-if="dirtySettingsByCategory[category] > 0"
variant="warning"
class="ml-1"
>
{{ dirtySettingsByCategory[category] }}
</b-badge>
</span>
<b-icon-chevron-down v-if="categoryExpanded(category)" font-scale="0.8" />
<b-icon-chevron-up v-else font-scale="0.8" />
</div>
</b-card-header>
<b-collapse :visible="categoryExpanded(category)">
<b-card-body>
<b-form-group
v-for="(setting, key) in settings"
:id="`${key}-input-group`"
:key="key"
:label-for="`${key}-input`"
:label-cols="true"
style="margin-bottom: 0"
>
<template #label>
<p>
<template v-if="setting.display_name !== ''">
{{ setting.display_name }}
</template>
<template v-else>
{{ key }}
</template>
<template v-if="setting.help_text !== ''">
<b-icon-question-circle-fill :id="`${key}-help-icon`" />
<b-tooltip :target="`${key}-help-icon`" triggers="hover">
{{ setting.help_text }}
</b-tooltip>
</template>
</p>
</template>
<b-form-select
v-if="setting.choice_options != null"
:id="`${key}-input`"
v-model="$v.editSettings[key].$model"
:name="`${key}-input`"
:options="getChoiceOptions(setting)"
:state="validateState(key)"
:disabled="!setting.can_edit"
>
</b-form-select>
<b-form-input
v-else-if="setting.type !== 'bool'"
:id="`${key}-input`"
v-model="$v.editSettings[key].$model"
:name="`${key}-input`"
:type="inputType(setting.type)"
:state="validateState(key)"
:readonly="!setting.can_edit"
:number="setting.type === 'int'"
/>
<b-form-checkbox
v-else-if="setting.type === 'bool'"
:id="`${key}-input`"
v-model="$v.editSettings[key].$model"
:name="`${key}-input`"
:disabled="!setting.can_edit"
:switch="true"
/>
<b-alert v-else show variant="danger">
Unknown setting type {{ setting.type }} for setting {{ key }}.
</b-alert>
</b-form-group>
</b-card-body>
</b-collapse>
</b-card>
<b-button-group
size="md"
style="float: right; padding-top: 1rem; padding-bottom: 0.5rem"
>
<template #label>
<p>
<template v-if="setting.display_name !== ''">
{{ setting.display_name }}
</template>
<template v-else>
{{ key }}
</template>
<template v-if="setting.help_text !== ''">
<b-icon-question-circle-fill :id="`${key}-help-icon`" />
<b-tooltip :target="`${key}-help-icon`" triggers="hover">
{{ setting.help_text }}
</b-tooltip>
</template>
</p>
</template>
<b-form-select
v-if="setting.choice_options != null"
:id="`${key}-input`"
v-model="$v.editSettings[key].$model"
:name="`${key}-input`"
:options="getChoiceOptions(setting)"
:state="validateState(key)"
:disabled="!setting.can_edit"
>
</b-form-select>
<b-form-input
v-else-if="setting.type !== 'bool'"
:id="`${key}-input`"
v-model="$v.editSettings[key].$model"
:name="`${key}-input`"
:type="inputType(setting.type)"
:state="validateState(key)"
:readonly="!setting.can_edit"
:number="setting.type === 'int'"
/>
<b-form-checkbox
v-else-if="setting.type === 'bool'"
:id="`${key}-input`"
v-model="$v.editSettings[key].$model"
:name="`${key}-input`"
:disabled="!setting.can_edit"
:switch="true"
/>
<b-alert v-else show variant="danger">
Unknown setting type {{ setting.type }} for setting {{ key }}.
</b-alert>
</b-form-group>
<b-button-group size="md" style="float: right">
<b-button type="reset" variant="danger"> Reset </b-button>
<b-button type="submit" variant="primary"> Submit </b-button>
</b-button-group>
Expand All @@ -80,7 +114,7 @@
</template>

<script>
import { mapGetters } from 'vuex';
import { mapGetters, mapActions } from 'vuex';
import { required, integer } from 'vuelidate/lib/validators';
import log from 'loglevel';

Expand All @@ -93,10 +127,11 @@ export default {
loaded: false,
editSettings: {},
toggle: 0,
expandedCategories: ['General'],
};
},
computed: {
...mapGetters(['RAW_SETTINGS']),
...mapGetters(['RAW_SETTINGS', 'SETTINGS_CATEGORIES']),
visibleSettings() {
const visibleSettings = {};
Object.keys(this.RAW_SETTINGS).forEach((x) => {
Expand All @@ -106,6 +141,32 @@ export default {
});
return visibleSettings;
},
settingsByCategory() {
const settingsByCategory = {};
Object.keys(this.SETTINGS_CATEGORIES).forEach((category) => {
settingsByCategory[category] = {};
this.SETTINGS_CATEGORIES[category].forEach((setting) => {
if (Object.keys(this.visibleSettings).includes(setting)) {
settingsByCategory[category][setting] = this.visibleSettings[setting];
}
});
});
return settingsByCategory;
},
dirtySettingsByCategory() {
const dirtySettingsByCategory = {};
Object.keys(this.SETTINGS_CATEGORIES).forEach((category) => {
dirtySettingsByCategory[category] = 0;
this.SETTINGS_CATEGORIES[category].forEach((setting) => {
if (Object.keys(this.visibleSettings).includes(setting)) {
if (this.$v.editSettings[setting].$dirty) {
dirtySettingsByCategory[category] += 1;
}
}
});
});
return dirtySettingsByCategory;
},
},
watch: {
RAW_SETTINGS() {
Expand All @@ -115,7 +176,8 @@ export default {
this.loaded = true;
},
},
mounted() {
async mounted() {
await this.GET_SETTINGS_CATEGORIES();
this.resetEditSettings();
this.loaded = true;
},
Expand Down Expand Up @@ -186,10 +248,22 @@ export default {
Object.keys(this.RAW_SETTINGS).forEach(function resetEditSettings(x) {
this.editSettings[x] = this.RAW_SETTINGS[x].value;
}, this);
this.$v.editSettings.$reset();
if (toggleLoaded) {
this.loaded = true;
}
},
expandCategory(category) {
if (this.categoryExpanded(category)) {
this.expandedCategories = this.expandedCategories.filter((x) => x !== category);
} else {
this.expandedCategories.push(category);
}
},
categoryExpanded(category) {
return this.expandedCategories.includes(category);
},
...mapActions(['GET_SETTINGS_CATEGORIES']),
},
};
</script>
7 changes: 7 additions & 0 deletions server/controllers/api/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ async def patch(self):
self.write({"message": "Settings updated"})


@ApiRoute("settings/categories", ApiVersion.V1)
class SettingsCategoriesController(BaseAPIController):
@allow_when_password_required
async def get(self):
await self.finish({"categories": self.application.digi_settings.categories})


@ApiRoute("settings/raw", ApiVersion.V1)
class RawSettingsController(BaseAPIController):
async def get(self):
Expand Down
Loading
Loading