Implement single choice value load/save

This commit is contained in:
Benedikt Kulmann
2020-05-07 11:29:51 +02:00
parent e44654dc9d
commit d6cd343b62
9 changed files with 188 additions and 93 deletions
+3 -3
View File
File diff suppressed because one or more lines are too long
+2
View File
@@ -24,6 +24,7 @@ func NewService(cfg *config.Config) Service {
} }
func (g Service) SaveSettingsBundle(c context.Context, req *proto.SaveSettingsBundleRequest, res *proto.SaveSettingsBundleResponse) error { func (g Service) SaveSettingsBundle(c context.Context, req *proto.SaveSettingsBundleRequest, res *proto.SaveSettingsBundleResponse) error {
req.SettingsBundle.Identifier = getFailsafeIdentifier(req.SettingsBundle.Identifier)
r, err := g.manager.WriteBundle(req.SettingsBundle) r, err := g.manager.WriteBundle(req.SettingsBundle)
if err != nil { if err != nil {
return err return err
@@ -51,6 +52,7 @@ func (g Service) ListSettingsBundles(c context.Context, req *proto.ListSettingsB
} }
func (g Service) SaveSettingsValue(c context.Context, req *proto.SaveSettingsValueRequest, res *proto.SaveSettingsValueResponse) error { func (g Service) SaveSettingsValue(c context.Context, req *proto.SaveSettingsValueRequest, res *proto.SaveSettingsValueResponse) error {
req.SettingsValue.Identifier = getFailsafeIdentifier(req.SettingsValue.Identifier)
r, err := g.manager.WriteValue(req.SettingsValue) r, err := g.manager.WriteValue(req.SettingsValue)
if err != nil { if err != nil {
return err return err
+8 -8
View File
@@ -30,10 +30,10 @@
<script> <script>
import { mapActions, mapGetters } from 'vuex' import { mapActions, mapGetters } from 'vuex'
import SettingsBundle from "./SettingsBundle.vue"; import SettingsBundle from './SettingsBundle.vue'
export default { export default {
name: 'SettingsApp', name: 'SettingsApp',
components: {SettingsBundle}, components: { SettingsBundle },
data () { data () {
return { return {
loading: true, loading: true,
@@ -46,10 +46,10 @@ export default {
'initialized', 'initialized',
'getSettingsBundlesByExtension' 'getSettingsBundlesByExtension'
]), ]),
extensionRouteParam() { extensionRouteParam () {
return this.$route.params.extension return this.$route.params.extension
}, },
selectedExtensionName() { selectedExtensionName () {
// TODO: extensions need to be registered with display names, separate from the settings bundles. until then: hardcoded translation // TODO: extensions need to be registered with display names, separate from the settings bundles. until then: hardcoded translation
if (this.selectedExtension === 'ocis-accounts') { if (this.selectedExtension === 'ocis-accounts') {
return 'Account' return 'Account'
@@ -58,7 +58,7 @@ export default {
} }
return this.selectedExtension return this.selectedExtension
}, },
selectedSettingsBundles() { selectedSettingsBundles () {
if (this.selectedExtension) { if (this.selectedExtension) {
return this.getSettingsBundlesByExtension(this.selectedExtension) return this.getSettingsBundlesByExtension(this.selectedExtension)
} }
@@ -67,7 +67,7 @@ export default {
}, },
methods: { methods: {
...mapActions('Settings', ['initialize']), ...mapActions('Settings', ['initialize']),
resetSelectedExtension() { resetSelectedExtension () {
if (this.extensions.length > 0) { if (this.extensions.length > 0) {
if (this.extensionRouteParam && this.extensions.includes(this.extensionRouteParam)) { if (this.extensionRouteParam && this.extensions.includes(this.extensionRouteParam)) {
this.selectedExtension = this.extensionRouteParam this.selectedExtension = this.extensionRouteParam
@@ -82,10 +82,10 @@ export default {
this.resetSelectedExtension() this.resetSelectedExtension()
}, },
watch: { watch: {
initialized() { initialized () {
this.resetSelectedExtension() this.resetSelectedExtension()
}, },
extensionRouteParam() { extensionRouteParam () {
this.resetSelectedExtension() this.resetSelectedExtension()
} }
} }
+25 -11
View File
@@ -12,6 +12,7 @@
:bundle="bundle" :bundle="bundle"
:setting="setting" :setting="setting"
:persisted-value="getSettingsValue(bundle, setting)" :persisted-value="getSettingsValue(bundle, setting)"
@onSave="onSaveSettingsValue"
/> />
</div> </div>
</oc-grid> </oc-grid>
@@ -19,13 +20,13 @@
</template> </template>
<script> <script>
import { mapGetters } from 'vuex' import { mapGetters, mapActions } from 'vuex'
import SettingBoolean from "./settings/SettingBoolean.vue"; import SettingBoolean from './settings/SettingBoolean.vue'
import SettingMultiChoice from "./settings/SettingMultiChoice.vue"; import SettingMultiChoice from './settings/SettingMultiChoice.vue'
import SettingNumber from "./settings/SettingNumber.vue"; import SettingNumber from './settings/SettingNumber.vue'
import SettingSingleChoice from "./settings/SettingSingleChoice.vue"; import SettingSingleChoice from './settings/SettingSingleChoice.vue'
import SettingString from "./settings/SettingString.vue"; import SettingString from './settings/SettingString.vue'
import SettingUnknown from "./settings/SettingUnknown.vue"; import SettingUnknown from './settings/SettingUnknown.vue'
export default { export default {
name: 'SettingsBundle', name: 'SettingsBundle',
@@ -37,19 +38,32 @@ export default {
}, },
computed: mapGetters('Settings', ['getSettingsValueByIdentifier']), computed: mapGetters('Settings', ['getSettingsValueByIdentifier']),
methods: { methods: {
getElementId(bundle, setting) { ...mapActions('Settings', ['saveSettingsValue']),
getElementId (bundle, setting) {
return `setting-${bundle.identifier.bundleKey}-${setting.settingKey}` return `setting-${bundle.identifier.bundleKey}-${setting.settingKey}`
}, },
getSettingComponent(setting) { getSettingComponent (setting) {
return 'Setting' + setting.type[0].toUpperCase() + setting.type.substr(1) return 'Setting' + setting.type[0].toUpperCase() + setting.type.substr(1)
}, },
getSettingsValue(bundle, setting) { getSettingsValue (bundle, setting) {
const identifier = { const identifier = {
extension: bundle.identifier.extension, extension: bundle.identifier.extension,
bundleKey: bundle.identifier.bundleKey, bundleKey: bundle.identifier.bundleKey,
settingKey: setting.settingKey, settingKey: setting.settingKey
} }
return this.getSettingsValueByIdentifier(identifier) return this.getSettingsValueByIdentifier(identifier)
},
async onSaveSettingsValue ({ bundle, setting, value }) {
const payload = {
identifier: {
accountUuid: 'me',
extension: bundle.identifier.extension,
bundleKey: bundle.identifier.bundleKey,
settingKey: setting.settingKey
},
...value
}
await this.saveSettingsValue(payload)
} }
}, },
components: { components: {
+13 -13
View File
@@ -1,11 +1,11 @@
<template> <template>
<div> <div>
<oc-checkbox v-model="value" :label="setting.boolValue.label" /> <oc-checkbox v-model="value" :label="setting.boolValue.label" @change="applyValue" />
</div> </div>
</template> </template>
<script> <script>
import isNil from "lodash/isNil" import isNil from 'lodash/isNil'
export default { export default {
name: 'SettingBoolean', name: 'SettingBoolean',
props: { props: {
@@ -22,31 +22,31 @@ export default {
required: false required: false
} }
}, },
data() { data () {
return { return {
initialValue: null,
value: null value: null
} }
}, },
computed: {
isChanged() {
return this.initialValue !== this.value
}
},
methods: { methods: {
applyValue() { async applyValue () {
// TODO: propagate value to parent const value = {
boolValue: this.value
}
await this.$emit('onSave', {
bundle: this.bundle,
setting: this.setting,
value
})
// TODO: show a spinner while the request for saving the value is running! // TODO: show a spinner while the request for saving the value is running!
} }
}, },
mounted() { mounted () {
if (!isNil(this.persistedValue)) { if (!isNil(this.persistedValue)) {
this.value = this.persistedValue.boolValue this.value = this.persistedValue.boolValue
} }
if (isNil(this.value) && !isNil(this.setting.boolValue.default)) { if (isNil(this.value) && !isNil(this.setting.boolValue.default)) {
this.value = this.setting.boolValue.default this.value = this.setting.boolValue.default
} }
this.initialValue = this.value
} }
} }
</script> </script>
+16 -8
View File
@@ -8,6 +8,7 @@
:placeholder="setting.intValue.placeholder" :placeholder="setting.intValue.placeholder"
:label="setting.description" :label="setting.description"
@keydown.enter="applyValue" @keydown.enter="applyValue"
@keydown.esc="cancel"
/> />
</div> </div>
<div v-if="isChanged"> <div v-if="isChanged">
@@ -22,7 +23,7 @@
</template> </template>
<script> <script>
import isNil from "lodash/isNil" import isNil from 'lodash/isNil'
export default { export default {
name: 'SettingNumber', name: 'SettingNumber',
props: { props: {
@@ -39,17 +40,17 @@ export default {
required: false required: false
} }
}, },
data() { data () {
return { return {
initialValue: null, initialValue: null,
value: null value: null
} }
}, },
computed: { computed: {
isChanged() { isChanged () {
return this.initialValue !== this.value return this.initialValue !== this.value
}, },
inputAttributes() { inputAttributes () {
const attributes = {} const attributes = {}
if (!isNil(this.setting.intValue.min)) { if (!isNil(this.setting.intValue.min)) {
attributes.min = this.setting.intValue.min attributes.min = this.setting.intValue.min
@@ -64,16 +65,23 @@ export default {
} }
}, },
methods: { methods: {
cancel() { cancel () {
this.value = this.initialValue this.value = this.initialValue
}, },
applyValue() { async applyValue () {
// TODO: propagate value to parent const value = {
intValue: this.value
}
await this.$emit('onSave', {
bundle: this.bundle,
setting: this.setting,
value
})
// TODO: show a spinner while the request for saving the value is running! // TODO: show a spinner while the request for saving the value is running!
this.initialValue = this.value this.initialValue = this.value
} }
}, },
mounted() { mounted () {
if (!isNil(this.persistedValue)) { if (!isNil(this.persistedValue)) {
this.value = this.persistedValue.intValue this.value = this.persistedValue.intValue
} }
+42 -12
View File
@@ -28,7 +28,7 @@
class="oc-radiobutton" class="oc-radiobutton"
v-model="selectedOption" v-model="selectedOption"
:value="option" :value="option"
@input="onSelectedOption" @change="onSelectedOption"
/> />
{{ option.displayValue }} {{ option.displayValue }}
</label> </label>
@@ -39,6 +39,7 @@
</template> </template>
<script> <script>
import isNil from 'lodash/isNil'
export default { export default {
name: 'SettingSingleChoice', name: 'SettingSingleChoice',
props: { props: {
@@ -55,33 +56,62 @@ export default {
required: false required: false
} }
}, },
data() { data () {
return { return {
selectedOption: null selectedOption: null
} }
}, },
computed: { computed: {
dropElementId() { dropElementId () {
return `single-choice-drop-${this.bundle.identifier.bundleKey}-${this.setting.settingKey}` return `single-choice-drop-${this.bundle.identifier.bundleKey}-${this.setting.settingKey}`
}, },
buttonElementId() { buttonElementId () {
return `single-choice-toggle-${this.bundle.identifier.bundleKey}-${this.setting.settingKey}` return `single-choice-toggle-${this.bundle.identifier.bundleKey}-${this.setting.settingKey}`
}, }
}, },
methods: { methods: {
getOptionElementId(index) { getOptionElementId (index) {
return `${this.bundle.identifier.bundleKey}-${this.setting.settingKey}-${index}` return `${this.bundle.identifier.bundleKey}-${this.setting.settingKey}-${index}`
}, },
onSelectedOption() { async onSelectedOption () {
// TODO: propagate selection to parent const value = {}
if (this.selectedOption) {
if (!isNil(this.selectedOption.intValue)) {
value.intListValue = {
value: [this.selectedOption ? this.selectedOption.intValue : null]
}
} else {
value.stringListValue = {
value: [this.selectedOption ? this.selectedOption.stringValue : null]
}
}
}
await this.$emit('onSave', {
bundle: this.bundle,
setting: this.setting,
value
})
// TODO: show a spinner while the request for saving the value is running! // TODO: show a spinner while the request for saving the value is running!
} }
}, },
mounted() { mounted () {
this.selectedOption = null if (!isNil(this.persistedValue)) {
// TODO: load the settings value of the authenticated user and set it in `selectedOption` if (!isNil(this.persistedValue.intListValue)) {
const selected = this.persistedValue.intListValue.value[0]
const filtered = this.setting.singleChoiceValue.options.filter(option => option.intValue === selected)
if (filtered.length > 0) {
this.selectedOption = filtered[0]
}
} else {
const selected = this.persistedValue.stringListValue.value[0]
const filtered = this.setting.singleChoiceValue.options.filter(option => option.stringValue === selected)
if (filtered.length > 0) {
this.selectedOption = filtered[0]
}
}
}
// if not set, yet, apply default from settings bundle definition // if not set, yet, apply default from settings bundle definition
if (this.selectedOption === null) { if (isNil(this.selectedOption)) {
const defaults = this.setting.singleChoiceValue.options.filter(option => option.default) const defaults = this.setting.singleChoiceValue.options.filter(option => option.default)
if (defaults.length === 1) { if (defaults.length === 1) {
this.selectedOption = defaults[0] this.selectedOption = defaults[0]
+15 -7
View File
@@ -6,6 +6,7 @@
:placeholder="setting.stringValue.placeholder" :placeholder="setting.stringValue.placeholder"
:label="setting.description" :label="setting.description"
@keydown.enter="applyValue" @keydown.enter="applyValue"
@keydown.esc="cancel"
/> />
</div> </div>
<div v-if="isChanged"> <div v-if="isChanged">
@@ -20,7 +21,7 @@
</template> </template>
<script> <script>
import isNil from "lodash/isNil" import isNil from 'lodash/isNil'
export default { export default {
name: 'SettingString', name: 'SettingString',
props: { props: {
@@ -37,28 +38,35 @@ export default {
required: false required: false
} }
}, },
data() { data () {
return { return {
initialValue: null, initialValue: null,
value: null value: null
} }
}, },
computed: { computed: {
isChanged() { isChanged () {
return this.initialValue !== this.value return this.initialValue !== this.value
} }
}, },
methods: { methods: {
applyValue() { async applyValue () {
// TODO: propagate value to parent const value = {
stringValue: this.value
}
await this.$emit('onSave', {
bundle: this.bundle,
setting: this.setting,
value
})
// TODO: show a spinner while the request for saving the value is running! // TODO: show a spinner while the request for saving the value is running!
this.initialValue = this.value this.initialValue = this.value
}, },
cancel() { cancel () {
this.value = this.initialValue this.value = this.initialValue
} }
}, },
mounted() { mounted () {
if (!isNil(this.persistedValue)) { if (!isNil(this.persistedValue)) {
this.value = this.persistedValue.stringValue this.value = this.persistedValue.stringValue
} }
+64 -31
View File
@@ -1,4 +1,11 @@
import {BundleService_ListSettingsBundles, ValueService_ListSettingsValues} from '../client/settings' import {
// eslint-disable-next-line camelcase
BundleService_ListSettingsBundles,
// eslint-disable-next-line camelcase
ValueService_ListSettingsValues,
// eslint-disable-next-line camelcase
ValueService_SaveSettingsValue
} from '../client/settings'
const state = { const state = {
config: null, config: null,
@@ -21,10 +28,10 @@ const getters = {
} }
return [] return []
}, },
getSettingsValueByIdentifier: state => ({extension, bundleKey, settingKey}) => { getSettingsValueByIdentifier: state => ({ extension, bundleKey, settingKey }) => {
if (state.settingsValues.has(extension) if (state.settingsValues.has(extension) &&
&& state.settingsValues.get(extension).has(bundleKey) state.settingsValues.get(extension).has(bundleKey) &&
&& state.settingsValues.get(extension).get(bundleKey).has(settingKey)) { state.settingsValues.get(extension).get(bundleKey).has(settingKey)) {
return state.settingsValues.get(extension).get(bundleKey).get(settingKey) return state.settingsValues.get(extension).get(bundleKey).get(settingKey)
} }
return null return null
@@ -32,12 +39,12 @@ const getters = {
} }
const mutations = { const mutations = {
SET_INITIALIZED(state, value) { SET_INITIALIZED (state, value) {
state.initialized = value state.initialized = value
}, },
SET_SETTINGS_BUNDLES(state, payload) { SET_SETTINGS_BUNDLES (state, settingsBundles) {
const map = new Map() const map = new Map()
Array.from(payload).forEach(bundle => { Array.from(settingsBundles).forEach(bundle => {
if (!map.has(bundle.identifier.extension)) { if (!map.has(bundle.identifier.extension)) {
map.set(bundle.identifier.extension, new Map()) map.set(bundle.identifier.extension, new Map())
} }
@@ -45,30 +52,25 @@ const mutations = {
}) })
state.settingsBundles = map state.settingsBundles = map
}, },
SET_SETTINGS_VALUES(state, payload) { SET_SETTINGS_VALUES (state, settingsValues) {
const map = new Map() const map = new Map()
Array.from(payload).forEach(value => { Array.from(settingsValues).forEach(value => applySettingsValueToMap(value, map))
if (!map.has(value.identifier.extension)) {
map.set(value.identifier.extension, new Map())
}
if (!map.get(value.identifier.extension).has(value.identifier.bundleKey)) {
map.get(value.identifier.extension).set(value.identifier.bundleKey, new Map())
}
map.get(value.identifier.extension).get(value.identifier.bundleKey).set(value.identifier.settingKey, value)
})
state.settingsValues = map state.settingsValues = map
}, },
LOAD_CONFIG(state, config) { SET_SETTINGS_VALUE (state, settingsValue) {
applySettingsValueToMap(settingsValue, state.settingsValues)
},
LOAD_CONFIG (state, config) {
state.config = config state.config = config
} }
} }
const actions = { const actions = {
loadConfig({commit}, config) { loadConfig ({ commit }, config) {
commit('LOAD_CONFIG', config) commit('LOAD_CONFIG', config)
}, },
async initialize({commit, dispatch}) { async initialize ({ commit, dispatch }) {
await Promise.all([ await Promise.all([
dispatch('fetchSettingsBundles'), dispatch('fetchSettingsBundles'),
dispatch('fetchSettingsValues') dispatch('fetchSettingsValues')
@@ -76,7 +78,7 @@ const actions = {
commit('SET_INITIALIZED', true) commit('SET_INITIALIZED', true)
}, },
async fetchSettingsBundles({commit, dispatch, getters}) { async fetchSettingsBundles ({ commit, dispatch, getters }) {
const response = await BundleService_ListSettingsBundles({ const response = await BundleService_ListSettingsBundles({
$domain: getters.config.url, $domain: getters.config.url,
body: {} body: {}
@@ -87,15 +89,15 @@ const actions = {
if (settingsBundles) { if (settingsBundles) {
settingsBundles.forEach(bundle => { settingsBundles.forEach(bundle => {
bundle.settings.forEach(setting => { bundle.settings.forEach(setting => {
if (setting['intValue']) { if (setting.intValue) {
setting.type = 'number' setting.type = 'number'
} else if (setting['stringValue']) { } else if (setting.stringValue) {
setting.type = 'string' setting.type = 'string'
} else if (setting['boolValue']) { } else if (setting.boolValue) {
setting.type = 'boolean' setting.type = 'boolean'
} else if (setting['singleChoiceValue']) { } else if (setting.singleChoiceValue) {
setting.type = 'singleChoice' setting.type = 'singleChoice'
} else if (setting['multiChoiceValue']) { } else if (setting.multiChoiceValue) {
setting.type = 'multiChoice' setting.type = 'multiChoice'
} else { } else {
setting.type = 'unknown' setting.type = 'unknown'
@@ -111,16 +113,16 @@ const actions = {
title: 'Failed to fetch settings bundles.', title: 'Failed to fetch settings bundles.',
desc: response.statusText, desc: response.statusText,
status: 'danger' status: 'danger'
}, {root: true}) }, { root: true })
} }
}, },
async fetchSettingsValues({commit, dispatch, getters}) { async fetchSettingsValues ({ commit, dispatch, getters }) {
const response = await ValueService_ListSettingsValues({ const response = await ValueService_ListSettingsValues({
$domain: getters.config.url, $domain: getters.config.url,
body: { body: {
identifier: { identifier: {
account_uuid: "me" account_uuid: 'me'
} }
} }
}) })
@@ -136,7 +138,27 @@ const actions = {
title: 'Failed to fetch settings values.', title: 'Failed to fetch settings values.',
desc: response.statusText, desc: response.statusText,
status: 'danger' status: 'danger'
}, {root: true}) }, { root: true })
}
},
async saveSettingsValue ({ commit, dispatch, getters }, payload) {
const response = await ValueService_SaveSettingsValue({
$domain: getters.config.url,
body: {
settingsValue: payload
}
})
if (response.status === 201) {
if (response.data.settingsValue) {
commit('SET_SETTINGS_VALUE', response.data.settingsValue)
}
} else {
dispatch('showMessage', {
title: 'Failed to save settings value.',
desc: response.statusText,
status: 'danger'
}, { root: true })
} }
} }
} }
@@ -148,3 +170,14 @@ export default {
actions, actions,
mutations mutations
} }
function applySettingsValueToMap (settingsValue, map) {
if (!map.has(settingsValue.identifier.extension)) {
map.set(settingsValue.identifier.extension, new Map())
}
if (!map.get(settingsValue.identifier.extension).has(settingsValue.identifier.bundleKey)) {
map.get(settingsValue.identifier.extension).set(settingsValue.identifier.bundleKey, new Map())
}
map.get(settingsValue.identifier.extension).get(settingsValue.identifier.bundleKey).set(settingsValue.identifier.settingKey, settingsValue)
return map
}