Re-organize webcfg structure

This commit is contained in:
JustArchi
2018-04-27 04:50:21 +02:00
parent a624927b8a
commit 0ca1fbd4ff
107 changed files with 17 additions and 100 deletions

View File

@@ -0,0 +1,48 @@
<template>
<form method="post" action="" class="form" id="asf-form" onsubmit="return false;">
<div class="row align-center" v-if="versions.length > 1">
<div class="col col-2">
<div class="form-input">
<select v-model="selectedVersion" id="version">
<option v-for="version in versions" :value="version">{{ version }}</option>
</select>
</div>
</div>
</div>
<fieldset v-for="group in schema" v-if="!group.advanced || displayAdvanced">
<legend>{{ $t(group.legend) }}</legend>
<component v-for="inputSchema in group.fields" :is="inputSchema.type" :schema="inputSchema" :key="inputSchema.field" v-if="!inputSchema.advanced || displayAdvanced"
@update="updateModel"></component>
</fieldset>
<div class="form-item">
<button @click.prevent="downloadJSON" class="button">{{ $t('button.download') }}</button>
<button @click.prevent="toggleAdvanced" class="button secondary" :class="{ outline: !displayAdvanced }">{{ $t('button.advanced') }}</button>
</div>
</form>
</template>
<script>
import { each } from 'lodash';
import Config from './mixin/Config.vue';
export default {
name: 'ASFConfig',
mixins: [Config],
data() { return { type: 'asf', filename: 'ASF.json' }; },
methods: {
processModelToJSON(model) {
if (model.Blacklist && model.Blacklist.length) {
model.Blacklist = model.Blacklist.map(item => parseInt(item, 10)).filter(item => !isNaN(item) && item > 0);
}
each(model, (value, key) => {
if (typeof value === 'string' && value === '') delete model[key];
});
return model;
}
}
};
</script>

View File

@@ -0,0 +1,53 @@
<template>
<form method="post" action="" class="form" id="asf-form" onsubmit="return false;">
<div class="row align-center" v-if="versions.length > 1">
<div class="col col-2">
<div class="form-input">
<select v-model="selectedVersion" id="version">
<option v-for="version in versions" :value="version">{{ version }}</option>
</select>
</div>
</div>
</div>
<fieldset v-for="group in schema" v-if="!group.advanced || displayAdvanced">
<legend>{{ $t(group.legend) }}</legend>
<component v-for="inputSchema in group.fields" :is="inputSchema.type" :schema="inputSchema" :key="inputSchema.field" v-if="!inputSchema.advanced || displayAdvanced"
@update="updateModel"></component>
</fieldset>
<div class="form-item">
<button @click.prevent="downloadJSON" class="button">{{ $t('button.download') }}</button>
<button @click.prevent="toggleAdvanced" class="button secondary" :class="{ outline: !displayAdvanced }">{{ $t('button.advanced') }}</button>
</div>
</form>
</template>
<script>
import { each } from 'lodash';
import Config from './mixin/Config.vue';
export default {
name: 'BotConfig',
mixins: [Config],
data() { return { type: 'bot' }; },
computed: { filename() { return `${this.model.name}.json`; } },
methods: {
processModelToJSON(originalModel) {
const model = { ...originalModel }; // Need to clone that so we don't destroy `model.name`
if (model.GamesPlayedWhileIdle && model.GamesPlayedWhileIdle.length) {
model.GamesPlayedWhileIdle = model.GamesPlayedWhileIdle.map(value => parseInt(value, 10)).filter(value => !isNaN(value) && value > 0);
}
each(model, (value, key) => {
if (typeof value === 'string' && value === '') delete model[key];
});
if (model.name) delete model.name;
return model;
}
}
};
</script>

View File

@@ -0,0 +1,15 @@
<template>
<div class="home">
<p class="text-justify" v-html="$t('home.topic')"></p>
</div>
</template>
<script>
export default {};
</script>
<style>
.text-justify {
text-align: justify;
}
</style>

View File

@@ -0,0 +1,18 @@
<template>
<div class="form-item form-checkboxes">
<label v-for="checkbox in schema.fields" :for="checkbox.field" class="checkbox">
<input type="checkbox" :id="checkbox.field" :name="checkbox.field" :required="checkbox.required" v-model="value">
{{ checkbox.label }}
<span v-if="checkbox.required" class="req">*</span>
</label>
</div>
</template>
<script>
import Input from '../mixin/Input.vue';
export default {
mixins: [Input],
name: 'CheckboxGroup'
};
</script>

View File

@@ -0,0 +1,36 @@
<template>
<div class="form-item">
<label :for="schema.field" class="checkbox" @click.self="value = !value">
<button class="button small" :class="{ outline: value }" @click.prevent="value = false"></button>
<button class="button small" :class="{ outline: !value }" @click.prevent="value = true"></button>
{{ schema.label }}
<span v-if="schema.required" class="req">*</span>
</label>
</div>
</template>
<script>
import Input from '../mixin/Input.vue';
export default {
mixins: [Input],
name: 'InputCheckbox'
};
</script>
<style lang="scss">
.checkbox {
button {
transition: all .3s;
margin-right: 2px;
&:last-of-type {
margin-right: 5px;
}
}
}
.checkbox-toggle {
height: 100%;
}
</style>

View File

@@ -0,0 +1,82 @@
<template>
<div class="form-item">
<label :for="schema.field">
{{ schema.label }}
<span v-if="schema.required" class="req">*</span>
<span v-if="schema.description" class="desc">{{ schema.description }}</span>
</label>
<div class="row gutters">
<div class="col col-10">
<div class="form-input">
<select v-model="flagValue" :id="schema.field">
<option v-for="val in schema.values" :value="val.value">{{ $t(val.name) }}</option>
</select>
</div>
</div>
<div class="col col-2">
<div class="form-input">
<button class="button outline w100" @click.prevent="addElement">{{ $t("static.add") }}</button>
</div>
</div>
</div>
<p class="label-list">
<span v-for="(item, index) in items" class="label outline" @click.prevent="removeElement(index)">{{ resolveOption(item, schema.values) }}</span>
</p>
</div>
</template>
<script>
import Input from '../mixin/Input.vue';
export default {
mixins: [Input],
name: 'InputFlag',
data() {
return {
items: [], // Vue doesn't work well with Sets...
flagValue: this.schema.defaultValue
};
},
methods: {
addElement() {
if (!this.flagValue && this.flagValue !== 0) return;
if (!this.items.includes(this.flagValue)) this.items.push(this.flagValue);
this.flagValue = this.schema.defaultValue;
this.value = this.items.reduce((el, sum) => { return el + sum; });
},
removeElement(index) {
this.items.splice(index, 1);
this.value = this.items.reduce((el, sum) => { return el + sum; });
},
resolveOption(toResolve, options) {
if (!options) return toResolve;
options.forEach(({ value, name }) => {
if (toResolve === value) toResolve = name;
});
return toResolve;
}
}
};
</script>
<style lang="scss">
.label-list {
margin-top: 5px;
margin-bottom: 0;
.label {
margin: 0 5px;
cursor: pointer;
transition: all 0.1s;
&:hover {
background: black;
color: white;
}
}
}
</style>

View File

@@ -0,0 +1,130 @@
<template>
<div class="form-item">
<label :for="schema.field">
{{ schema.label }}
<span v-if="schema.required" class="req">*</span>
<span v-if="schema.description" class="desc">{{ $t(schema.description) }}</span>
</label>
<div class="row gutters">
<div class="col col-5">
<div class="form-item">
<input v-if="!schema.keys" type="text" :placeholder="schema.keyPlaceholder" class="map-key" :class="{ error: keyInvalid }" v-model="mapKey">
<span v-if="!schema.keys && keyInvalid" class="error">{{ keyErrors.join(' ') }}</span>
<select v-if="schema.keys" v-model="mapKey">
<option v-for="key in schema.keys" :value="key.value">{{ $t(key.name) }}</option>
</select>
</div>
</div>
<div class="col col-5">
<div class="form-item">
<input v-if="!schema.values" type="text" :placeholder="schema.valuePlaceholder" class="map-value" :class="{ error: valueInvalid }" v-model="mapValue">
<span v-if="!schema.values && valueInvalid" class="error">{{ valueErrors.join(' ') }}</span>
<select v-if="schema.values" v-model="mapValue">
<option v-for="val in schema.values" :value="val.value">{{ $t(val.name) }}</option>
</select>
</div>
</div>
<div class="col col-2">
<div class="form-input">
<button class="button outline w100" @click.prevent="addElement">{{ $t("static.add") }}</button>
</div>
</div>
</div>
<p class="label-list">
<span v-for="(value, key) in items" class="label outline" @click.prevent="removeElement(key)">{{ resolveOption(key, schema.keys)
}} => {{ resolveOption(value, schema.values) }}</span>
</p>
</div>
</template>
<script>
import { each } from 'lodash';
import Input from '../mixin/Input.vue';
export default {
mixins: [Input],
name: 'InputMap',
computed: {
keyErrors() {
if (!this.schema.keyValidator) return [];
return this.validate(this.mapKey, this.schema.keyValidator);
},
keyInvalid() {
return this.keyErrors.length !== 0;
},
valueErrors() {
if (!this.schema.valueValidator) return [];
return this.validate(this.mapValue, this.schema.valueValidator);
},
valueInvalid() {
return this.valueErrors.length !== 0;
}
},
data() {
return {
items: {}, // Vue doesn't work well with Maps...
mapKey: this.schema.defaultKey,
mapValue: this.schema.defaultValue
};
},
methods: {
addElement() {
if (!this.mapValue && this.mapValue !== 0 || !this.mapKey && this.mapKey !== 0) return;
if (this.hasErrors()) return;
this.items[this.mapKey] = this.mapValue;
this.mapValue = this.schema.defaultValue;
this.mapKey = this.schema.defaultKey;
this.$emit('update', this.items, this.schema.field);
},
removeElement(key) {
this.$delete(this.items, key);
this.$emit('update', this.items, this.schema.field);
},
resolveOption(toResolve, options) {
if (!options) return toResolve;
options.forEach(({ value, name }) => {
if (toResolve === value) toResolve = name;
});
return toResolve;
},
hasErrors() {
const invalid = this.keyInvalid || this.valueInvalid;
if (!invalid) return false;
const fields = [];
if (this.keyInvalid) each(this.$el.getElementsByClassName('map-key'), field => fields.push(field));
if (this.valueInvalid) each(this.$el.getElementsByClassName('map-value'), field => fields.push(field));
clearTimeout(this.shakeTimeout);
each(fields, field => { field.classList.add('shake'); });
this.shakeTimeout = setTimeout(() => { each(fields, field => { field.classList.remove('shake'); }); }, 500);
return true;
}
}
};
</script>
<style lang="scss">
.label-list {
margin-top: 5px;
margin-bottom: 0;
.label {
margin: 0 5px;
cursor: pointer;
transition: all 0.1s;
&:hover {
background: black;
color: white;
}
}
}
</style>

View File

@@ -0,0 +1,32 @@
<template>
<div class="form-item">
<label :for="schema.field">
{{ schema.label }}
<span v-if="schema.required" class="req">*</span>
<span v-if="schema.description" class="desc">{{ schema.description }}</span>
</label>
<input type="number" :name="schema.field" :id="schema.field" :placeholder="schema.placeholder" :required="schema.required" :class="{ error: invalid }"
v-model.number="value">
<span v-if="invalid" class="error">{{ errors.join(' ') }}</span>
</div>
</template>
<script>
import Input from '../mixin/Input.vue';
export default {
mixins: [Input],
name: 'InputNumber',
computed: {
errors() {
return this.validate(this.value);
},
valid() {
return this.errors.length === 0;
},
invalid() {
return this.errors.length !== 0;
}
}
};
</script>

View File

@@ -0,0 +1,31 @@
<template>
<div class="form-item">
<label :for="schema.field">
{{ schema.label }}
<span v-if="schema.required" class="req">*</span>
<span v-if="schema.description" class="desc">{{ $t(schema.description) }}</span>
</label>
<input type="password" :name="schema.field" :id="schema.field" :placeholder="schema.placeholder" :required="schema.required" :class="{ error: invalid }" v-model="value">
<span v-if="invalid" class="error">{{ errors.join(' ') }}</span>
</div>
</template>
<script>
import Input from '../mixin/Input.vue';
export default {
mixins: [Input],
name: 'InputPassword',
computed: {
errors() {
return this.validate(this.value);
},
valid() {
return this.errors.length === 0;
},
invalid() {
return this.errors.length !== 0;
}
}
};
</script>

View File

@@ -0,0 +1,21 @@
<template>
<div class="form-item">
<label :for="schema.field">
{{ schema.label }}
<span v-if="schema.required" class="req">*</span>
<span v-if="schema.description" class="desc">{{ schema.description }}</span>
</label>
<select :name="schema.field" :id="schema.field" :required="schema.required" v-model="value">
<option v-for="option in schema.options" :value="option.value">{{ $t(option.name) }}</option>
</select>
</div>
</template>
<script>
import Input from '../mixin/Input.vue';
export default {
mixins: [Input],
name: 'InputSelect'
};
</script>

View File

@@ -0,0 +1,107 @@
<template>
<div class="form-item">
<label :for="schema.field">
{{ schema.label }}
<span v-if="schema.required" class="req">*</span>
<span v-if="schema.description" class="desc">{{ $t(schema.description) }}</span>
</label>
<div class="row gutters">
<div class="col col-10">
<div class="form-input">
<input v-if="!schema.values" type="text" :name="schema.field" :placeholder="schema.placeholder" :id="schema.field" class="set-value" :class="{ error: invalid }"
v-model="setValue">
<span v-if="!schema.values && invalid" class="error">{{ errors.join(' ') }}</span>
<select v-if="schema.values" v-model="setValue" :id="schema.field">
<option v-for="val in schema.values" :value="val.value">{{ $t(val.name) }}</option>
</select>
</div>
</div>
<div class="col col-2">
<div class="form-input">
<button class="button outline w100" @click.prevent="addElement">{{ $t("static.add") }}</button>
</div>
</div>
</div>
<p class="label-list">
<span v-for="(item, index) in items" class="label outline" @click.prevent="removeElement(index)">{{ resolveOption(item, schema.values) }}</span>
</p>
</div>
</template>
<script>
import { each } from 'lodash';
import Input from '../mixin/Input.vue';
export default {
mixins: [Input],
name: 'InputSet',
computed: {
errors() {
return this.schema.values ? [] : this.validate(this.setValue);
},
invalid() {
return this.errors.length !== 0;
}
},
data() {
return {
items: [], // Vue doesn't work well with Sets...
setValue: this.schema.defaultValue
};
},
methods: {
addElement() {
if (!this.setValue && this.setValue !== 0) return;
if (this.hasErrors()) return;
if (!this.items.includes(this.setValue)) this.items.push(this.setValue);
this.setValue = this.schema.defaultValue;
this.$emit('update', this.items, this.schema.field);
},
removeElement(index) {
this.items.splice(index, 1);
this.$emit('update', this.items, this.schema.field);
},
resolveOption(toResolve, options) {
if (!options) return toResolve;
options.forEach(({ value, name }) => {
if (toResolve === value) toResolve = name;
});
return toResolve;
},
hasErrors() {
if (!this.invalid) return false;
const fields = [];
each(this.$el.getElementsByClassName('set-value'), field => fields.push(field));
clearTimeout(this.shakeTimeout);
each(fields, field => { field.classList.add('shake'); });
this.shakeTimeout = setTimeout(() => { each(fields, field => { field.classList.remove('shake'); }); }, 500);
return true;
}
}
};
</script>
<style lang="scss">
.label-list {
margin-top: 5px;
margin-bottom: 0;
.label {
margin: 0 5px;
cursor: pointer;
transition: all 0.1s;
&:hover {
background: black;
color: white;
}
}
}
</style>

View File

@@ -0,0 +1,31 @@
<template>
<div class="form-item">
<label :for="schema.field">
{{ schema.label }}
<span v-if="schema.required" class="req">*</span>
<span v-if="schema.description" class="desc">{{ $t(schema.description) }}</span>
</label>
<input type="text" :name="schema.field" :id="schema.field" :placeholder="schema.placeholder" :required="schema.required" :class="{ error: invalid }" v-model="value">
<span v-if="invalid" class="error">{{ errors.join(' ') }}</span>
</div>
</template>
<script>
import Input from '../mixin/Input.vue';
export default {
mixins: [Input],
name: 'InputText',
computed: {
errors() {
return this.validate(this.value);
},
valid() {
return this.errors.length === 0;
},
invalid() {
return this.errors.length !== 0;
}
}
};
</script>

View File

@@ -0,0 +1,89 @@
<script>
import { each } from 'lodash';
import schema from '../../schema';
const fieldComponents = {};
const fields = require.context('../fields', false, /^\.\/([\w-_]+)\.vue$/);
each(fields.keys(), key => {
const name = key.replace(/^\.\//, '').replace(/\.vue/, '');
fieldComponents[name] = fields(key);
});
export default {
data() {
const versions = [];
for (const version in schema) versions.push(version);
const selectedVersion = sessionStorage.getItem('selectedVersion') || versions[0];
return {
model: {},
displayAdvanced: false,
selectedVersion,
versions,
type: ''
};
},
computed: {
schema() {
return schema[this.selectedVersion][this.type] || {};
}
},
methods: {
updateModel(value, field) {
this.model[field] = value;
},
downloadJSON() {
if (!this.validateForm()) return;
const json = this.processModelToJSON(this.model);
const text = JSON.stringify(json, null, 2);
this.downloadText(text, this.filename);
},
downloadText(text, filename) {
const element = document.createElement('a');
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
element.setAttribute('download', filename);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
},
toggleAdvanced() {
this.displayAdvanced = !this.displayAdvanced;
},
validateForm() {
const form = document.getElementsByTagName('form')[0];
const fields = document.getElementsByClassName('error');
if (!fields.length) return form.checkValidity();
clearTimeout(this.shakeTimeout);
each(fields, field => { field.classList.add('shake'); });
this.shakeTimeout = setTimeout(() => { each(fields, field => { field.classList.remove('shake'); }); }, 500);
return false;
},
processModelToJSON(model) {
return model;
}
},
watch: {
selectedVersion(version) {
sessionStorage.setItem('selectedVersion', version);
}
},
components: fieldComponents
};
</script>
<style lang="scss">
.form-item:last-child {
margin-bottom: 0;
}
</style>

View File

@@ -0,0 +1,26 @@
<script>
import Validators from '../../validators';
export default {
props: ['schema'],
watch: {
value() {
this.$emit('update', this.value, this.schema.field);
}
},
data() {
return { value: this.schema.defaultValue };
},
methods: {
validate(value, validator) {
if (!validator && !this.schema.validator) {
if (this.schema.required) return Validators.required(value, this.schema);
return [];
}
if (!validator) return this.schema.validator(value, this.schema);
return validator(value, this.schema);
}
}
};
</script>