Update to Vuelidate 2.x.

This commit is contained in:
Buster Neece 2022-12-09 20:53:10 -06:00
parent 77273d7db2
commit 288a20756d
No known key found for this signature in database
GPG Key ID: F1D2E64A0005E80E
46 changed files with 951 additions and 736 deletions

5
frontend/jsconfig.json Normal file
View File

@ -0,0 +1,5 @@
{
"vueCompilerOptions": {
"target": 2.7
}
}

View File

@ -16,6 +16,8 @@
"@fullcalendar/luxon2": "^5.10.2",
"@fullcalendar/timegrid": "^5.9.0",
"@fullcalendar/vue": "^5.9.0",
"@vuelidate/core": "^2.0.0",
"@vuelidate/validators": "^2.0.0",
"axios": "^1",
"bootstrap": "^4.6.0 <5",
"bootstrap-notify": "^3.1.3",
@ -62,12 +64,10 @@
"vue-clipboard2": "^0.3.3",
"vue-gettext": "^2.1.12",
"vue-loader": "^15 <16",
"vue-template-compiler": "^2.6.14",
"vue2-daterange-picker": "^0.6.6",
"vue2-leaflet": "^2.7.1",
"vue2-leaflet-fullscreen": "^1.0.1",
"vuedraggable": "^2.24.1",
"vuelidate": "^0.7.6",
"vuex": "^3 <4",
"wavesurfer.js": "^6",
"webpack": "^5.52.1",
@ -2068,6 +2068,40 @@
"integrity": "sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg==",
"optional": true
},
"node_modules/@vuelidate/core": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@vuelidate/core/-/core-2.0.0.tgz",
"integrity": "sha512-xIFgdQlScO0aaSZ0wTGPJh8YcTMNAj5veI8yPgiAyxOT+GV7vNQFiU1vpYWCL4cklkkhYvRRSC2OEX7YOZNmPQ==",
"dependencies": {
"vue-demi": "^0.13.11"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0-rc.1",
"vue": "^2.0.0 || >=3.0.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
}
},
"node_modules/@vuelidate/validators": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@vuelidate/validators/-/validators-2.0.0.tgz",
"integrity": "sha512-fQQcmDWfz7pyH5/JPi0Ng2GEgNK1pUHn/Z/j5rG/Q+HwhgIXvJblTPcZwKOj1ABL7V4UVuGKECvZCDHNGOwdrg==",
"dependencies": {
"vue-demi": "^0.13.11"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0-rc.1",
"vue": "^2.0.0 || >=3.0.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
}
},
"node_modules/@webassemblyjs/ast": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
@ -3720,11 +3754,6 @@
"type": "^1.0.1"
}
},
"node_modules/de-indent": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
"integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg=="
},
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@ -5380,14 +5409,6 @@
"resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz",
"integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA=="
},
"node_modules/he": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
"bin": {
"he": "bin/he"
}
},
"node_modules/hls.js": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.2.7.tgz",
@ -9649,6 +9670,31 @@
"clipboard": "^2.0.0"
}
},
"node_modules/vue-demi": {
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz",
"integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
"hasInstallScript": true,
"bin": {
"vue-demi-fix": "bin/vue-demi-fix.js",
"vue-demi-switch": "bin/vue-demi-switch.js"
},
"engines": {
"node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/antfu"
},
"peerDependencies": {
"@vue/composition-api": "^1.0.0-rc.1",
"vue": "^3.0.0-0 || ^2.6.0"
},
"peerDependenciesMeta": {
"@vue/composition-api": {
"optional": true
}
}
},
"node_modules/vue-functional-data-merge": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/vue-functional-data-merge/-/vue-functional-data-merge-3.1.0.tgz",
@ -9700,15 +9746,6 @@
"loader-utils": "^1.0.2"
}
},
"node_modules/vue-template-compiler": {
"version": "2.7.14",
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz",
"integrity": "sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==",
"dependencies": {
"de-indent": "^1.0.2",
"he": "^1.2.0"
}
},
"node_modules/vue-template-es2015-compiler": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz",
@ -9759,15 +9796,6 @@
"sortablejs": "1.10.2"
}
},
"node_modules/vuelidate": {
"version": "0.7.7",
"resolved": "https://registry.npmjs.org/vuelidate/-/vuelidate-0.7.7.tgz",
"integrity": "sha512-pT/U2lDI67wkIqI4tum7cMSIfGcAMfB+Phtqh2ttdXURwvHRBJEAQ0tVbUsW9Upg83Q5QH59bnCoXI7A9JDGnA==",
"engines": {
"node": ">= 4.0.0",
"npm": ">= 3.0.0"
}
},
"node_modules/vuex": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/vuex/-/vuex-3.6.2.tgz",
@ -11627,6 +11655,22 @@
"integrity": "sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg==",
"optional": true
},
"@vuelidate/core": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@vuelidate/core/-/core-2.0.0.tgz",
"integrity": "sha512-xIFgdQlScO0aaSZ0wTGPJh8YcTMNAj5veI8yPgiAyxOT+GV7vNQFiU1vpYWCL4cklkkhYvRRSC2OEX7YOZNmPQ==",
"requires": {
"vue-demi": "^0.13.11"
}
},
"@vuelidate/validators": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@vuelidate/validators/-/validators-2.0.0.tgz",
"integrity": "sha512-fQQcmDWfz7pyH5/JPi0Ng2GEgNK1pUHn/Z/j5rG/Q+HwhgIXvJblTPcZwKOj1ABL7V4UVuGKECvZCDHNGOwdrg==",
"requires": {
"vue-demi": "^0.13.11"
}
},
"@webassemblyjs/ast": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
@ -12907,11 +12951,6 @@
"type": "^1.0.1"
}
},
"de-indent": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
"integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg=="
},
"debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@ -14202,11 +14241,6 @@
"resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz",
"integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA=="
},
"he": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
},
"hls.js": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.2.7.tgz",
@ -17427,6 +17461,12 @@
"clipboard": "^2.0.0"
}
},
"vue-demi": {
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz",
"integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==",
"requires": {}
},
"vue-functional-data-merge": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/vue-functional-data-merge/-/vue-functional-data-merge-3.1.0.tgz",
@ -17463,15 +17503,6 @@
"loader-utils": "^1.0.2"
}
},
"vue-template-compiler": {
"version": "2.7.14",
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz",
"integrity": "sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==",
"requires": {
"de-indent": "^1.0.2",
"he": "^1.2.0"
}
},
"vue-template-es2015-compiler": {
"version": "1.9.1",
"resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz",
@ -17508,11 +17539,6 @@
"sortablejs": "1.10.2"
}
},
"vuelidate": {
"version": "0.7.7",
"resolved": "https://registry.npmjs.org/vuelidate/-/vuelidate-0.7.7.tgz",
"integrity": "sha512-pT/U2lDI67wkIqI4tum7cMSIfGcAMfB+Phtqh2ttdXURwvHRBJEAQ0tVbUsW9Upg83Q5QH59bnCoXI7A9JDGnA=="
},
"vuex": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/vuex/-/vuex-3.6.2.tgz",

View File

@ -17,6 +17,8 @@
"@fullcalendar/luxon2": "^5.10.2",
"@fullcalendar/timegrid": "^5.9.0",
"@fullcalendar/vue": "^5.9.0",
"@vuelidate/core": "^2.0.0",
"@vuelidate/validators": "^2.0.0",
"axios": "^1",
"bootstrap": "^4.6.0 <5",
"bootstrap-notify": "^3.1.3",
@ -63,12 +65,10 @@
"vue-clipboard2": "^0.3.3",
"vue-gettext": "^2.1.12",
"vue-loader": "^15 <16",
"vue-template-compiler": "^2.6.14",
"vue2-daterange-picker": "^0.6.6",
"vue2-leaflet": "^2.7.1",
"vue2-leaflet-fullscreen": "^1.0.1",
"vuedraggable": "^2.24.1",
"vuelidate": "^0.7.6",
"vuex": "^3 <4",
"wavesurfer.js": "^6",
"webpack": "^5.52.1",

View File

@ -6,7 +6,7 @@
<b-form v-if="newKey === null" class="form vue-form" @submit.prevent="doSubmit">
<b-form-fieldset>
<b-wrapped-form-group id="form_comments" :field="$v.form.comment" autofocus>
<b-wrapped-form-group id="form_comments" :field="v$.form.comment" autofocus>
<template #label="{lang}">
<translate :key="lang">API Key Description/Comments</translate>
</template>
@ -26,7 +26,7 @@
<b-button variant="default" type="button" @click="close">
<translate key="lang_btn_close">Close</translate>
</b-button>
<b-button v-if="newKey === null" :variant="($v.form.$invalid) ? 'danger' : 'primary'" type="submit"
<b-button v-if="newKey === null" :variant="(v$.$invalid) ? 'danger' : 'primary'" type="submit"
@click="doSubmit">
<translate key="lang_btn_create_key">Create New Key</translate>
</b-button>
@ -36,32 +36,34 @@
</template>
<script>
import {validationMixin} from 'vuelidate';
import {required} from 'vuelidate/dist/validators.min.js';
import BFormFieldset from "~/components/Form/BFormFieldset";
import InvisibleSubmitButton from "~/components/Common/InvisibleSubmitButton";
import AccountApiKeyNewKey from "./ApiKeyNewKey";
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
import {required} from '@vuelidate/validators';
import useVuelidate from "@vuelidate/core";
export default {
name: 'AccountApiKeyModal',
components: {BWrappedFormGroup, AccountApiKeyNewKey, InvisibleSubmitButton, BFormFieldset},
mixins: [validationMixin],
components: {BFormFieldset, InvisibleSubmitButton, AccountApiKeyNewKey, BWrappedFormGroup},
props: {
createUrl: String
},
validations() {
setup() {
return {
form: {
comment: {required}
}
v$: useVuelidate()
};
},
data() {
return {
error: null,
form: {},
newKey: null,
form: this.getBlankForm()
}
},
validations: {
form: {
comment: {required}
}
},
computed: {
@ -76,15 +78,18 @@ export default {
this.$refs.modal.show();
},
resetForm() {
this.newKey = null;
this.form = {
getBlankForm() {
return {
comment: ''
};
},
resetForm() {
this.newKey = null;
this.form = this.getBlankForm();
},
doSubmit() {
this.$v.form.$touch();
if (this.$v.form.$anyError) {
this.v$.$touch();
if (this.v$.$errors.length > 0) {
return;
}
@ -105,9 +110,10 @@ export default {
},
close() {
this.$refs.modal.hide();
this.clearContents();
},
clearContents() {
this.$v.form.$reset();
this.v$.$reset();
this.error = null;
this.resetForm();

View File

@ -32,14 +32,10 @@
</div>
</template>
<script>
<script setup>
import CopyToClipboardButton from "~/components/Common/CopyToClipboardButton";
export default {
name: 'AccountApiKeyNewKey',
components: {CopyToClipboardButton},
props: {
newKey: String
}
}
const props = defineProps({
newKey: String
});
</script>

View File

@ -1,21 +1,21 @@
<template>
<modal-form ref="modal" size="md" centered :title="langTitle" :disable-save-button="$v.form.$invalid"
<modal-form ref="modal" size="md" centered :title="langTitle" :disable-save-button="v$.$invalid"
@submit="onSubmit" @hidden="onHidden">
<b-form-fieldset>
<b-wrapped-form-group id="form_current_password" :field="$v.form.current_password"
<b-wrapped-form-group id="form_current_password" :field="v$.form.current_password"
input-type="password" autofocus>
<template #label="{lang}">
<translate :key="lang">Current Password</translate>
</template>
</b-wrapped-form-group>
<b-wrapped-form-group id="form_new_password" :field="$v.form.new_password" input-type="password">
<b-wrapped-form-group id="form_new_password" :field="v$.form.new_password" input-type="password">
<template #label="{lang}">
<translate :key="lang">New Password</translate>
</template>
</b-wrapped-form-group>
<b-wrapped-form-group id="form_current_password" :field="$v.form.new_password2" input-type="password">
<b-wrapped-form-group id="form_current_password" :field="v$.form.new_password2" input-type="password">
<template #label="{lang}">
<translate :key="lang">Confirm New Password</translate>
</template>
@ -27,29 +27,29 @@
</template>
</modal-form>
</template>
<script>
import {validationMixin} from 'vuelidate';
import {required, sameAs} from 'vuelidate/dist/validators.min.js';
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
import ModalForm from "~/components/Common/ModalForm";
import BFormFieldset from "~/components/Form/BFormFieldset";
import {required, sameAs} from "@vuelidate/validators";
import validatePassword from "~/functions/validatePassword";
import useVuelidate from "@vuelidate/core";
export default {
name: 'AccountChangePasswordModal',
components: {ModalForm, BWrappedFormGroup, BFormFieldset},
emits: ['relist'],
mixins: [validationMixin],
components: {BWrappedFormGroup, ModalForm, BFormFieldset},
props: {
changePasswordUrl: String
},
emits: ['relist'],
setup() {
return {
v$: useVuelidate()
};
},
data() {
return {
form: {
current_password: null,
new_password: null,
new_password2: null
}
form: this.getBlankForm(),
};
},
validations: {
@ -68,18 +68,29 @@ export default {
}
},
methods: {
getBlankForm() {
return {
current_password: null,
new_password: null,
new_password2: null
};
},
open() {
this.resetForm();
this.$refs.modal.show();
},
close() {
this.$refs.modal.hide();
},
onHidden() {
this.$v.form.$reset();
this.clearContents();
},
resetForm() {
this.form = this.getBlankForm();
},
onSubmit() {
this.$v.form.$touch();
if (this.$v.form.$anyError) {
this.v$.$touch();
if (this.v$.$errors.length > 0) {
return;
}
@ -89,7 +100,13 @@ export default {
this.$refs.modal.hide();
this.$emit('relist');
});
}
},
clearContents() {
this.v$.$reset();
this.error = null;
this.resetForm();
},
}
};
</script>

View File

@ -1,34 +1,39 @@
<template>
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="$v.form.$invalid"
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="v$.$invalid"
@submit="doSubmit" @hidden="clearContents">
<account-edit-form :form="$v.form" :supported-locales="supportedLocales"></account-edit-form>
<account-edit-form :form="v$.form" :supported-locales="supportedLocales"></account-edit-form>
</modal-form>
</template>
<script>
import ModalForm from "~/components/Common/ModalForm";
import {validationMixin} from "vuelidate";
import {email, required} from 'vuelidate/dist/validators.min.js';
import AccountEditForm from "./EditForm";
import mergeExisting from "~/functions/mergeExisting";
import {email, required} from '@vuelidate/validators';
import useVuelidate from "@vuelidate/core";
import AccountEditForm from "./EditForm.vue";
import ModalForm from "~/components/Common/ModalForm.vue";
export default {
name: 'AccountEditModal',
components: {AccountEditForm, ModalForm,},
mixins: [validationMixin],
components: {ModalForm, AccountEditForm},
emits: ['relist'],
props: {
userUrl: String,
supportedLocales: Object
},
setup() {
return {
v$: useVuelidate()
};
},
data() {
return {
loading: true,
error: null,
form: {}
form: {
...this.getBlankForm(),
}
};
},
validations() {
@ -48,8 +53,8 @@ export default {
}
},
methods: {
resetForm() {
this.form = {
getBlankForm() {
return {
name: '',
email: '',
locale: 'default',
@ -57,6 +62,11 @@ export default {
show_24_hour_time: null,
};
},
resetForm() {
this.form = {
...this.getBlankForm()
};
},
open() {
this.resetForm();
this.loading = false;
@ -69,13 +79,13 @@ export default {
).then((resp) => {
this.form = mergeExisting(this.form, resp.data);
this.loading = false;
}).catch((error) => {
}).catch(() => {
this.close();
});
},
doSubmit() {
this.$v.form.$touch();
if (this.$v.form.$anyError) {
this.v$.$touch();
if (this.v$.$errors.length > 0) {
return;
}
@ -87,7 +97,7 @@ export default {
url: this.userUrl,
data: this.form
})
).then((resp) => {
).then(() => {
this.$notifySuccess();
this.$emit('relist');
this.close();
@ -99,7 +109,7 @@ export default {
this.$refs.modal.hide();
},
clearContents() {
this.$v.form.$reset();
this.v$.$reset();
this.loading = false;
this.error = null;

View File

@ -1,5 +1,5 @@
<template>
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="$v.form.$invalid"
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="v$.$invalid"
@submit="doSubmit" @hidden="clearContents" no-enforce-focus>
<b-row>
@ -21,7 +21,7 @@
</p>
<b-form-fieldset>
<b-wrapped-form-group id="form_otp" :field="$v.form.otp" autofocus>
<b-wrapped-form-group id="form_otp" :field="v$.form.otp" autofocus>
<template #label="{lang}">
<translate :key="lang">Code from Authenticator App</translate>
</template>
@ -47,25 +47,28 @@
<translate key="lang_btn_submit">Submit Code</translate>
</template>
</modal-form>
</template>
<script>
import ModalForm from "~/components/Common/ModalForm";
import {validationMixin} from "vuelidate";
import {minLength, required} from 'vuelidate/dist/validators.min.js';
import CopyToClipboardButton from "~/components/Common/CopyToClipboardButton";
import BFormFieldset from "~/components/Form/BFormFieldset";
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
import {minLength, required} from "@vuelidate/validators";
import useVuelidate from "@vuelidate/core";
export default {
name: 'AccountTwoFactorModal',
components: {ModalForm, CopyToClipboardButton, BFormFieldset, BWrappedFormGroup},
mixins: [validationMixin],
emits: ['relist'],
props: {
twoFactorUrl: String
},
setup() {
return {
v$: useVuelidate()
};
},
data() {
return {
loading: true,
@ -76,19 +79,17 @@ export default {
qr_code: null
},
form: {
otp: null
otp: ''
}
};
},
validations() {
return {
form: {
otp: {
required,
minLength: minLength(6)
}
validations: {
form: {
otp: {
required,
minLength: minLength(6)
}
};
}
},
computed: {
langTitle() {
@ -118,13 +119,13 @@ export default {
).then((resp) => {
this.totp = resp.data;
this.loading = false;
}).catch((error) => {
}).catch(() => {
this.close();
});
},
doSubmit() {
this.$v.form.$touch();
if (this.$v.form.$anyError) {
this.v$.$touch();
if (this.v$.$errors.length > 0) {
return;
}
@ -139,7 +140,7 @@ export default {
otp: this.form.otp
}
})
).then((resp) => {
).then(() => {
this.$notifySuccess();
this.$emit('relist');
this.close();
@ -151,7 +152,7 @@ export default {
this.$refs.modal.hide();
},
clearContents() {
this.$v.form.$reset();
this.v$.$reset();
this.loading = false;
this.error = null;

View File

@ -1,10 +1,10 @@
<template>
<modal-form ref="modal" size="lg" :title="langTitle" :loading="loading" :disable-save-button="$v.form.$invalid"
<modal-form ref="modal" size="lg" :title="langTitle" :loading="loading" :disable-save-button="v$.form.$invalid"
@submit="submit" @hidden="clearContents">
<b-form-fieldset>
<b-form-row class="mb-3">
<b-wrapped-form-checkbox class="col-md-12" id="form_edit_backup_enabled"
:field="$v.form.backup_enabled">
:field="v$.form.backup_enabled">
<template #label="{lang}">
<translate :key="lang">Run Automatic Nightly Backups</translate>
</template>
@ -14,8 +14,8 @@
</b-wrapped-form-checkbox>
</b-form-row>
<b-form-row v-if="$v.form.backup_enabled.$model">
<b-wrapped-form-group class="col-md-6" id="form_backup_time_code" :field="$v.form.backup_time_code">
<b-form-row v-if="v$.form.backup_enabled.$model">
<b-wrapped-form-group class="col-md-6" id="form_backup_time_code" :field="v$.form.backup_time_code">
<template #label="{lang}">
<translate :key="lang">Scheduled Backup Time</translate>
</template>
@ -28,7 +28,7 @@
</b-wrapped-form-group>
<b-wrapped-form-checkbox class="col-md-6" id="form_edit_exclude_media"
:field="$v.form.backup_exclude_media">
:field="v$.form.backup_exclude_media">
<template #label="{lang}">
<translate :key="lang">Exclude Media from Backup</translate>
</template>
@ -37,7 +37,7 @@
</template>
</b-wrapped-form-checkbox>
<b-wrapped-form-group class="col-md-6" id="form_backup_keep_copies" :field="$v.form.backup_keep_copies"
<b-wrapped-form-group class="col-md-6" id="form_backup_keep_copies" :field="v$.form.backup_keep_copies"
input-type="number" :input-attrs="{min: '0', max: '365'}">
<template #label="{lang}">
<translate :key="lang">Number of Backup Copies to Keep</translate>
@ -48,7 +48,7 @@
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="edit_form_backup_storage_location"
:field="$v.form.backup_storage_location">
:field="v$.form.backup_storage_location">
<template #label="{lang}">
<translate :key="lang">Storage Location</translate>
</template>
@ -58,7 +58,7 @@
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="edit_form_backup_format" :field="$v.form.backup_format">
<b-wrapped-form-group class="col-md-6" id="edit_form_backup_format" :field="v$.form.backup_format">
<template #label="{lang}">
<translate :key="lang">Backup Format</translate>
</template>
@ -73,7 +73,7 @@
</template>
<script>
import {validationMixin} from "vuelidate";
import useVuelidate from "@vuelidate/core";
import CodemirrorTextarea from "~/components/Common/CodemirrorTextarea";
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
import ModalForm from "~/components/Common/ModalForm";
@ -86,6 +86,9 @@ import objectToFormOptions from "~/functions/objectToFormOptions";
export default {
name: 'AdminBackupsConfigureModal',
emits: ['relist'],
setup() {
return {v$: useVuelidate()}
},
props: {
settingsUrl: String,
storageLocations: Object
@ -98,9 +101,6 @@ export default {
CodemirrorTextarea,
TimeCode
},
mixins: [
validationMixin
],
data() {
return {
loading: true,
@ -157,7 +157,7 @@ export default {
});
},
clearContents() {
this.$v.form.$reset();
this.v$.$reset();
this.form = {
backup_enabled: false,
@ -173,8 +173,8 @@ export default {
this.$refs.modal.hide();
},
submit() {
this.$v.form.$touch();
if (this.$v.form.$anyError) {
this.v$.$touch();
if (this.v$.$errors.length > 0) {
return;
}

View File

@ -8,7 +8,7 @@
<b-form-fieldset>
<b-form-row>
<b-wrapped-form-group class="col-md-12" id="edit_form_storage_location"
:field="$v.form.storage_location">
:field="v$.form.storage_location">
<template #label="{lang}">
<translate :key="lang">Storage Location</translate>
</template>
@ -19,7 +19,7 @@
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-12" id="edit_form_path"
:field="$v.form.path">
:field="v$.form.path">
<template #label="{lang}">
<translate :key="lang">File Name</translate>
</template>
@ -42,7 +42,7 @@
</b-wrapped-form-group>
<b-wrapped-form-checkbox class="col-md-12" id="edit_form_exclude_media"
:field="$v.form.exclude_media">
:field="v$.form.exclude_media">
<template #label="{lang}">
<translate :key="lang">Exclude Media from Backup</translate>
</template>
@ -66,7 +66,7 @@
<b-button variant="default" type="button" @click="close">
<translate key="lang_btn_close">Close</translate>
</b-button>
<b-button v-if="logUrl === null" :variant="($v.form.$invalid) ? 'danger' : 'primary'" type="submit"
<b-button v-if="logUrl === null" :variant="(v$.form.$invalid) ? 'danger' : 'primary'" type="submit"
@click="submit">
<translate key="lang_btn_run_backup">Run Manual Backup</translate>
</b-button>
@ -76,10 +76,10 @@
</template>
<script>
import useVuelidate from "@vuelidate/core";
import BFormFieldset from "~/components/Form/BFormFieldset";
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
import InvisibleSubmitButton from "~/components/Common/InvisibleSubmitButton";
import {validationMixin} from "vuelidate";
import BWrappedFormCheckbox from "~/components/Form/BWrappedFormCheckbox";
import objectToFormOptions from "~/functions/objectToFormOptions";
import StreamingLogView from "~/components/Common/StreamingLogView";
@ -91,9 +91,9 @@ export default {
runBackupUrl: String,
storageLocations: Object
},
mixins: [
validationMixin
],
setup() {
return {v$: useVuelidate()}
},
components: {
BFormFieldset,
BWrappedFormGroup,
@ -132,8 +132,8 @@ export default {
this.$emit('relist');
},
submit() {
this.$v.form.$touch();
if (this.$v.form.$anyError) {
this.v$.$touch();
if (this.v$.$errors.length > 0) {
return;
}

View File

@ -14,7 +14,7 @@
<b-form-group>
<b-form-row>
<b-wrapped-form-group class="col-md-6" id="edit_form_public_theme"
:field="$v.form.public_theme">
:field="v$.form.public_theme">
<template #label="{lang}">
<translate :key="lang">Base Theme for Public Pages</translate>
</template>
@ -30,7 +30,7 @@
<b-col md="6">
<b-wrapped-form-checkbox class="mb-2" id="form_edit_hide_album_art"
:field="$v.form.hide_album_art">
:field="v$.form.hide_album_art">
<template #label="{lang}">
<translate :key="lang">Hide Album Art on Public Pages</translate>
</template>
@ -40,7 +40,7 @@
</b-wrapped-form-checkbox>
<b-wrapped-form-checkbox id="form_edit_hide_product_name"
:field="$v.form.hide_product_name">
:field="v$.form.hide_product_name">
<template #label="{lang}">
<translate :key="lang">Hide AzuraCast Branding on Public Pages</translate>
</template>
@ -51,7 +51,7 @@
</b-col>
<b-wrapped-form-group class="col-md-6" id="form_edit_homepage_redirect_url"
:field="$v.form.homepage_redirect_url">
:field="v$.form.homepage_redirect_url">
<template #label="{lang}">
<translate :key="lang">Homepage Redirect URL</translate>
</template>
@ -61,7 +61,7 @@
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-6" id="form_edit_default_album_art_url"
:field="$v.form.default_album_art_url">
:field="v$.form.default_album_art_url">
<template #label="{lang}">
<translate :key="lang">Default Album Art URL</translate>
</template>
@ -71,7 +71,7 @@
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-12" id="edit_form_public_custom_css"
:field="$v.form.public_custom_css">
:field="v$.form.public_custom_css">
<template #label="{lang}">
<translate :key="lang">Custom CSS for Public Pages</translate>
</template>
@ -85,7 +85,7 @@
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-12" id="edit_form_public_custom_js"
:field="$v.form.public_custom_js">
:field="v$.form.public_custom_js">
<template #label="{lang}">
<translate :key="lang">Custom JS for Public Pages</translate>
</template>
@ -99,7 +99,7 @@
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-12" id="edit_form_internal_custom_css"
:field="$v.form.internal_custom_css">
:field="v$.form.internal_custom_css">
<template #label="{lang}">
<translate :key="lang">Custom CSS for Internal Pages</translate>
</template>
@ -124,7 +124,7 @@
</template>
<script>
import {validationMixin} from "vuelidate";
import useVuelidate from "@vuelidate/core";
import CodemirrorTextarea from "~/components/Common/CodemirrorTextarea";
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
import BWrappedFormCheckbox from "~/components/Form/BWrappedFormCheckbox";
@ -139,9 +139,9 @@ export default {
BWrappedFormGroup,
CodemirrorTextarea,
},
mixins: [
validationMixin
],
setup() {
return {v$: useVuelidate()}
},
data() {
return {
loading: true,
@ -184,7 +184,7 @@ export default {
},
methods: {
relist() {
this.$v.form.$reset();
this.v$.$reset();
this.loading = true;
this.axios.get(this.apiUrl).then((resp) => {
@ -207,8 +207,8 @@ export default {
}
},
submit() {
this.$v.form.$touch();
if (this.$v.form.$anyError) {
this.v$.$touch();
if (this.v$.$errors.length > 0) {
return;
}

View File

@ -1,23 +1,26 @@
<template>
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="$v.form.$invalid"
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="v$.form.$invalid"
@submit="doSubmit" @hidden="clearContents">
<admin-custom-fields-form :form="$v.form" :auto-assign-types="autoAssignTypes">
<admin-custom-fields-form :form="v$.form" :auto-assign-types="autoAssignTypes">
</admin-custom-fields-form>
</modal-form>
</template>
<script>
import {validationMixin} from 'vuelidate';
import {required} from 'vuelidate/dist/validators.min.js';
import useVuelidate from "@vuelidate/core";
import {required} from '@vuelidate/validators';
import BaseEditModal from '~/components/Common/BaseEditModal';
import AdminCustomFieldsForm from "~/components/Admin/CustomFields/Form";
export default {
name: 'AdminCustomFieldsEditModal',
components: {AdminCustomFieldsForm},
mixins: [validationMixin, BaseEditModal],
setup() {
return {v$: useVuelidate()}
},
mixins: [BaseEditModal],
props: {
autoAssignTypes: Object
},

View File

@ -63,7 +63,7 @@
<form @submit.prevent="doUpdate">
<fieldset>
<b-wrapped-form-group id="edit_form_key" :field="$v.key">
<b-wrapped-form-group id="edit_form_key" :field="v$.key">
<template #label>
<translate key="lang_edit_form_key">MaxMind License Key</translate>
</template>
@ -87,18 +87,18 @@
</template>
<script>
import useVuelidate from "@vuelidate/core";
import BFormFieldset from "~/components/Form/BFormFieldset";
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
import {validationMixin} from "vuelidate";
import '~/vendor/sweetalert.js';
import InfoCard from "~/components/Common/InfoCard";
export default {
name: 'GeoLite',
components: {InfoCard, BWrappedFormGroup, BFormFieldset},
mixins: [
validationMixin
],
setup() {
return {v$: useVuelidate()}
},
props: {
apiUrl: String
},

View File

@ -1,12 +1,12 @@
<template>
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="$v.form.$invalid"
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="v$.form.$invalid"
@submit="doSubmit" @hidden="clearContents">
<b-tabs content-class="mt-3" pills>
<admin-permissions-global-form :form="$v.form" :global-permissions="globalPermissions">
<admin-permissions-global-form :form="v$.form" :global-permissions="globalPermissions">
</admin-permissions-global-form>
<admin-permissions-station-form :form="$v.form" :stations="stations"
<admin-permissions-station-form :form="v$.form" :stations="stations"
:station-permissions="stationPermissions">
</admin-permissions-station-form>
</b-tabs>
@ -15,8 +15,8 @@
</template>
<script>
import {validationMixin} from 'vuelidate';
import {required} from 'vuelidate/dist/validators.min.js';
import useVuelidate from "@vuelidate/core";
import {required} from '@vuelidate/validators';
import BaseEditModal from '~/components/Common/BaseEditModal';
import AdminPermissionsGlobalForm from "./Form/GlobalForm";
import AdminPermissionsStationForm from "./Form/StationForm";
@ -25,7 +25,10 @@ import _ from 'lodash';
export default {
name: 'AdminPermissionsEditModal',
components: {AdminPermissionsStationForm, AdminPermissionsGlobalForm},
mixins: [validationMixin, BaseEditModal],
setup() {
return {v$: useVuelidate()}
},
mixins: [BaseEditModal],
props: {
stations: Object,
globalPermissions: Object,
@ -44,12 +47,7 @@ export default {
'name': {required},
'permissions': {
'global': {},
'station': {
$each: {
'station_id': {},
'permissions': {},
}
},
'station': {},
}
}
};

View File

@ -1,41 +1,10 @@
<template>
<b-tab :title="langTabTitle">
<b-card v-for="(row, index) in form.permissions.station.$each.$iter" :key="index" class="mb-3" no-body>
<div class="card-header bg-primary-dark d-flex align-items-center">
<div class="flex-fill">
<h2 class="card-title">
{{ getStationName(row.station_id.$model) }}
</h2>
</div>
<div class="flex-shrink-0">
<b-button size="sm" variant="outline-light" class="py-2 pr-0" @click.prevent="remove(index)">
<icon icon="remove"></icon>
<translate key="lang_btn_remove">Remove</translate>
</b-button>
</div>
</div>
<b-card-body>
<b-form-group>
<b-form-row>
<b-wrapped-form-group class="col-md-12"
:id="'edit_form_station_permissions_'+row.station_id.$model"
:field="row.permissions">
<template #label="{lang}">
<translate :key="lang">Station Permissions</translate>
</template>
<template #description="{lang}">
<translate :key="lang">Users with this role will have these permissions for this single station.</translate>
</template>
<template #default="props">
<b-form-checkbox-group :id="props.id" :options="stationPermissionOptions"
v-model="props.field.$model">
</b-form-checkbox-group>
</template>
</b-wrapped-form-group>
</b-form-row>
</b-form-group>
</b-card-body>
</b-card>
<permissions-form-station-row
v-for="(row, index) in form.permissions.$model.station" :key="index"
:stations="stations" :station-permissions="stationPermissions"
:row.sync="row" @remove="remove(index)"
></permissions-form-station-row>
<b-button-group v-if="hasRemainingStations">
<b-dropdown size="sm" variant="outline-primary">
@ -56,10 +25,11 @@
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
import Icon from "~/components/Common/Icon";
import _ from 'lodash';
import PermissionsFormStationRow from "~/components/Admin/Permissions/Form/StationRow.vue";
export default {
name: 'AdminPermissionsStationForm',
components: {BWrappedFormGroup, Icon},
components: {PermissionsFormStationRow, BWrappedFormGroup, Icon},
props: {
form: Object,
stations: Object,
@ -69,17 +39,9 @@ export default {
langTabTitle() {
return this.$gettext('Station Permissions');
},
stationPermissionOptions() {
return _.map(this.stationPermissions, (permissionName, permissionKey) => {
return {
text: permissionName,
value: permissionKey
};
})
},
remainingStations() {
return _.pickBy(this.stations, (stationName, stationId) => {
return !_.find(this.form.permissions.station.$model, {'station_id': stationId});
return !_.find(this.form.permissions.$model.station, {'station_id': stationId});
});
},
hasRemainingStations() {
@ -91,10 +53,10 @@ export default {
return _.get(this.stations, stationId, null);
},
remove (index) {
this.form.permissions.station.$model.splice(index, 1);
this.form.permissions.$model.station.splice(index, 1);
},
add(stationId) {
this.form.permissions.station.$model.push({
this.form.permissions.$model.station.push({
'station_id': stationId,
'permissions': []
});

View File

@ -0,0 +1,82 @@
<template>
<b-card class="mb-3" no-body>
<div class="card-header bg-primary-dark d-flex align-items-center">
<div class="flex-fill">
<h2 class="card-title">
{{ getStationName(row.station_id) }}
</h2>
</div>
<div class="flex-shrink-0">
<b-button size="sm" variant="outline-light" class="py-2 pr-0" @click.prevent="$emit('remove')">
<icon icon="remove"></icon>
<translate key="lang_btn_remove">Remove</translate>
</b-button>
</div>
</div>
<b-card-body>
<b-form-group>
<b-form-row>
<b-wrapped-form-group class="col-md-12"
:id="'edit_form_station_permissions_'+row.station_id"
:field="v$.row.permissions">
<template #label="{lang}">
<translate :key="lang">Station Permissions</translate>
</template>
<template #description="{lang}">
<translate :key="lang">Users with this role will have these permissions for this single station.</translate>
</template>
<template #default="props">
<b-form-checkbox-group :id="props.id" :options="stationPermissionOptions"
v-model="props.field.$model">
</b-form-checkbox-group>
</template>
</b-wrapped-form-group>
</b-form-row>
</b-form-group>
</b-card-body>
</b-card>
</template>
<script>
import useVuelidate from "@vuelidate/core";
import _ from "lodash";
import Icon from "~/components/Common/Icon.vue";
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup.vue";
export default {
name: 'PermissionsFormStationRow',
components: {BWrappedFormGroup, Icon},
emits: ['remove'],
props: {
row: Object,
stations: Object,
stationPermissions: Object
},
setup() {
return {
v$: useVuelidate()
}
},
validations: {
row: {
'station_id': {},
'permissions': {},
}
},
computed: {
stationPermissionOptions() {
return _.map(this.stationPermissions, (permissionName, permissionKey) => {
return {
text: permissionName,
value: permissionKey
};
})
},
},
methods: {
getStationName(stationId) {
return _.get(this.stations, stationId, null);
},
}
}
</script>

View File

@ -17,28 +17,28 @@
<b-overlay variant="card" :show="loading">
<b-tabs pills card lazy>
<b-tab :title-link-class="getTabClass($v.generalTab)">
<b-tab :title-link-class="getTabClass(v$.$validationGroups.generalTab)">
<template #title>
<translate key="tab_general">Settings</translate>
</template>
<settings-general-tab :form="$v.form"></settings-general-tab>
<settings-general-tab :form="v$.form"></settings-general-tab>
</b-tab>
<b-tab :title-link-class="getTabClass($v.securityPrivacyTab)">
<b-tab :title-link-class="getTabClass(v$.$validationGroups.securityPrivacyTab)">
<template #title>
<translate key="tab_security_privacy">Security & Privacy</translate>
</template>
<settings-security-privacy-tab :form="$v.form"></settings-security-privacy-tab>
<settings-security-privacy-tab :form="v$.form"></settings-security-privacy-tab>
</b-tab>
<b-tab :title-link-class="getTabClass($v.servicesTab)">
<b-tab :title-link-class="getTabClass(v$.$validationGroups.servicesTab)">
<template #title>
<translate key="tab_services">Services</translate>
</template>
<settings-services-tab :form="$v.form"
<settings-services-tab :form="v$.form"
:release-channel="releaseChannel"
:test-message-url="testMessageUrl"
:acme-url="acmeUrl"></settings-services-tab>
@ -47,7 +47,7 @@
</b-overlay>
<b-card-body body-class="card-padding-sm">
<b-button size="lg" type="submit" :variant="($v.form.$invalid) ? 'danger' : 'primary'">
<b-button size="lg" type="submit" :variant="(v$.form.$invalid) ? 'danger' : 'primary'">
<slot name="submitButtonName">
<translate key="lang_btn_save_changes">Save Changes</translate>
</slot>
@ -58,15 +58,18 @@
</template>
<script>
import useVuelidate from "@vuelidate/core";
import {required} from '@vuelidate/validators';
import SettingsGeneralTab from "./Settings/GeneralTab";
import SettingsServicesTab from "./Settings/ServicesTab";
import {validationMixin} from "vuelidate";
import {required} from 'vuelidate/dist/validators.min.js';
import SettingsSecurityPrivacyTab from "~/components/Admin/Settings/SecurityPrivacyTab";
export default {
name: 'AdminSettings',
components: {SettingsSecurityPrivacyTab, SettingsServicesTab, SettingsGeneralTab},
setup() {
return {v$: useVuelidate()}
},
emits: ['saved'],
props: {
apiUrl: String,
@ -78,9 +81,6 @@ export default {
required: false
}
},
mixins: [
validationMixin
],
data() {
return {
loading: true,
@ -120,22 +120,24 @@ export default {
use_external_album_art_when_processing_media: {},
last_fm_api_key: {}
},
generalTab: [
'form.base_url', 'form.instance_name', 'form.prefer_browser_url', 'form.use_radio_proxy',
'form.history_keep_days', 'form.enable_static_nowplaying', 'form.enable_advanced_features'
],
securityPrivacyTab: [
'form.analytics', 'form.always_use_ssl', 'form.api_access_control'
],
servicesTab: [
'form.check_for_updates',
'form.acme_email', 'form.acme_domains',
'form.mail_enabled', 'form.mail_sender_name', 'form.mail_sender_email',
'form.mail_smtp_host', 'form.mail_smtp_port', 'form.mail_smtp_secure', 'form.mail_smtp_username',
'form.mail_smtp_password', 'form.avatar_service', 'form.avatar_default_url',
'form.use_external_album_art_in_apis', 'form.use_external_album_art_when_processing_media',
'form.last_fm_api_key',
]
$validationGroups: {
generalTab: [
'form.base_url', 'form.instance_name', 'form.prefer_browser_url', 'form.use_radio_proxy',
'form.history_keep_days', 'form.enable_static_nowplaying', 'form.enable_advanced_features'
],
securityPrivacyTab: [
'form.analytics', 'form.always_use_ssl', 'form.api_access_control'
],
servicesTab: [
'form.check_for_updates',
'form.acme_email', 'form.acme_domains',
'form.mail_enabled', 'form.mail_sender_name', 'form.mail_sender_email',
'form.mail_smtp_host', 'form.mail_smtp_port', 'form.mail_smtp_secure', 'form.mail_smtp_username',
'form.mail_smtp_password', 'form.avatar_service', 'form.avatar_default_url',
'form.use_external_album_art_in_apis', 'form.use_external_album_art_when_processing_media',
'form.last_fm_api_key',
]
}
},
mounted() {
this.relist();
@ -148,7 +150,7 @@ export default {
return null;
},
relist() {
this.$v.form.$reset();
this.v$.$reset();
this.loading = true;
this.axios.get(this.apiUrl).then((resp) => {
@ -190,8 +192,8 @@ export default {
}
},
submit() {
this.$v.form.$touch();
if (this.$v.form.$anyError) {
this.v$.$touch();
if (this.v$.$errors.length > 0) {
return;
}

View File

@ -1,7 +1,7 @@
<template>
<b-modal id="send_test_message" centered ref="modal" :title="langTitle">
<b-form @submit.prevent="doSendTest">
<b-wrapped-form-group id="email_address" :field="$v.emailAddress" autofocus>
<b-wrapped-form-group id="email_address" :field="v$.emailAddress" autofocus>
<template #label="{lang}">
<translate :key="lang">E-mail Address</translate>
</template>
@ -11,7 +11,7 @@
<b-button variant="default" @click="close">
<translate key="lang_btn_close">Close</translate>
</b-button>
<b-button :variant="($v.$invalid) ? 'danger' : 'primary'" @click="doSendTest">
<b-button :variant="(v$.$invalid) ? 'danger' : 'primary'" @click="doSendTest">
<translate key="lang_btn_send">Send Test Message</translate>
</b-button>
</template>
@ -19,14 +19,16 @@
</template>
<script>
import useVuelidate from "@vuelidate/core";
import {email, required} from '@vuelidate/validators';
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
import {validationMixin} from "vuelidate";
import {email, required} from 'vuelidate/dist/validators.min.js';
export default {
name: 'AdminSettingsTestMessageModal',
components: {BWrappedFormGroup},
mixins: [validationMixin],
setup() {
return {v$: useVuelidate()}
},
props: {
testMessageUrl: String
},
@ -46,12 +48,12 @@ export default {
methods: {
close() {
this.emailAddress = null;
this.$v.$reset();
this.v$.$reset();
this.$refs.modal.hide();
},
doSendTest() {
this.$v.$touch();
if (this.$v.$anyError) {
async doSendTest() {
this.v$.$touch();
if (this.v$.$errors.length > 0) {
return;
}

View File

@ -1,21 +1,24 @@
<template>
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="$v.form.$invalid"
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="v$.form.$invalid"
@submit="doSubmit" @hidden="clearContents">
<admin-stations-clone-modal-form :form="$v.form"></admin-stations-clone-modal-form>
<admin-stations-clone-modal-form :form="v$.form"></admin-stations-clone-modal-form>
</modal-form>
</template>
<script>
import useVuelidate from "@vuelidate/core";
import {required} from '@vuelidate/validators';
import ModalForm from "~/components/Common/ModalForm";
import {validationMixin} from "vuelidate";
import {required} from 'vuelidate/dist/validators.min.js';
import AdminStationsCloneModalForm from "~/components/Admin/Stations/CloneModalForm";
export default {
name: 'AdminStationsCloneModal',
components: {AdminStationsCloneModalForm, ModalForm},
setup() {
return {v$: useVuelidate()}
},
emits: ['relist'],
data() {
return {
@ -25,9 +28,6 @@ export default {
form: {},
}
},
mixins: [
validationMixin
],
validations() {
return {
form: {
@ -70,8 +70,8 @@ export default {
this.$refs.modal.hide();
},
doSubmit() {
this.$v.form.$touch();
if (this.$v.form.$anyError) {
this.v$.$touch();
if (this.v$.$errors.length > 0) {
return;
}

View File

@ -4,69 +4,69 @@
<b-form class="form vue-form" @submit.prevent="submit">
<b-tabs :card="!isModal" pills :content-class="tabContentClass">
<b-tab :title-link-class="getTabClass($v.profileTab)" active>
<b-tab :title-link-class="getTabClass(v$.$validationGroups.profileTab)" active>
<template #title>
<translate key="tab_profile">Profile</translate>
</template>
<admin-stations-profile-form :form="$v.form" :timezones="timezones"
<admin-stations-profile-form :form="v$.form" :timezones="timezones"
:show-advanced="showAdvanced"></admin-stations-profile-form>
</b-tab>
<b-tab :title-link-class="getTabClass($v.frontendTab)">
<b-tab :title-link-class="getTabClass(v$.$validationGroups.frontendTab)">
<template #title>
<translate key="tab_frontend">Broadcasting</translate>
</template>
<admin-stations-frontend-form :form="$v.form"
<admin-stations-frontend-form :form="v$.form"
:is-shoutcast-installed="isShoutcastInstalled"
:countries="countries"
:show-advanced="showAdvanced"></admin-stations-frontend-form>
</b-tab>
<b-tab :title-link-class="getTabClass($v.backendTab)">
<b-tab :title-link-class="getTabClass(v$.$validationGroups.backendTab)">
<template #title>
<translate key="tab_backend">AutoDJ</translate>
</template>
<admin-stations-backend-form :form="$v.form" :station="station"
<admin-stations-backend-form :form="v$.form" :station="station"
:is-stereo-tool-installed="isStereoToolInstalled"
:show-advanced="showAdvanced"></admin-stations-backend-form>
</b-tab>
<b-tab :title-link-class="getTabClass($v.hlsTab)">
<b-tab :title-link-class="getTabClass(v$.$validationGroups.hlsTab)">
<template #title>
<translate key="tab_hls">HLS</translate>
</template>
<admin-stations-hls-form :form="$v.form" :station="station" :show-advanced="showAdvanced">
<admin-stations-hls-form :form="v$.form" :station="station" :show-advanced="showAdvanced">
</admin-stations-hls-form>
</b-tab>
<b-tab :title-link-class="getTabClass($v.requestsTab)">
<b-tab :title-link-class="getTabClass(v$.$validationGroups.requestsTab)">
<template #title>
<translate key="tab_requests">Song Requests</translate>
</template>
<admin-stations-requests-form :form="$v.form" :station="station" :show-advanced="showAdvanced">
<admin-stations-requests-form :form="v$.form" :station="station" :show-advanced="showAdvanced">
</admin-stations-requests-form>
</b-tab>
<b-tab :title-link-class="getTabClass($v.streamersTab)">
<b-tab :title-link-class="getTabClass(v$.$validationGroups.streamersTab)">
<template #title>
<translate key="tab_streamers">Streamers/DJs</translate>
</template>
<admin-stations-streamers-form :form="$v.form" :station="station" :show-advanced="showAdvanced">
<admin-stations-streamers-form :form="v$.form" :station="station" :show-advanced="showAdvanced">
</admin-stations-streamers-form>
</b-tab>
<b-tab v-if="showAdminTab" :title-link-class="getTabClass($v.adminTab)">
<b-tab v-if="showAdminTab" :title-link-class="getTabClass(v$.$validationGroups.adminTab)">
<template #title>
<translate key="tab_admin">Administration</translate>
</template>
<admin-stations-admin-form :form="$v.form"
<admin-stations-admin-form :form="v$.form"
:is-edit-mode="isEditMode"
:storage-location-api-url="storageLocationApiUrl"
:show-advanced="showAdvanced">
@ -88,9 +88,6 @@
</template>
<script>
import {validationMixin} from "vuelidate";
import {decimal, numeric, required, url} from 'vuelidate/dist/validators.min.js';
import {AUDIO_PROCESSING_NONE, BACKEND_LIQUIDSOAP, FRONTEND_ICECAST} from "~/components/Entity/RadioAdapters";
import AdminStationsProfileForm from "./Form/ProfileForm";
import AdminStationsFrontendForm from "./Form/FrontendForm";
import AdminStationsBackendForm from "./Form/BackendForm";
@ -98,6 +95,10 @@ import AdminStationsAdminForm from "./Form/AdminForm";
import AdminStationsHlsForm from "./Form/HlsForm.vue";
import AdminStationsRequestsForm from "./Form/RequestsForm.vue";
import AdminStationsStreamersForm from "./Form/StreamersForm.vue";
import useVuelidate from "@vuelidate/core";
import {decimal, numeric, required, url} from '@vuelidate/validators';
import {AUDIO_PROCESSING_NONE, BACKEND_LIQUIDSOAP, FRONTEND_ICECAST} from "~/components/Entity/RadioAdapters";
import _ from "lodash";
import mergeExisting from "~/functions/mergeExisting";
@ -136,9 +137,17 @@ export default {
AdminStationsStreamersForm,
AdminStationsRequestsForm,
AdminStationsHlsForm,
AdminStationsAdminForm, AdminStationsBackendForm, AdminStationsFrontendForm, AdminStationsProfileForm
AdminStationsAdminForm,
AdminStationsBackendForm,
AdminStationsFrontendForm,
AdminStationsProfileForm
},
emits: ['error', 'submitted', 'loadingUpdate', 'validUpdate'],
setup() {
return {
v$: useVuelidate()
};
},
props: {
createUrl: String,
editUrl: String,
@ -149,7 +158,6 @@ export default {
}
},
mixins: [
validationMixin,
StationFormProps
],
validations() {
@ -191,28 +199,30 @@ export default {
enable_streamers: {},
disconnect_deactivate_streamer: {},
},
profileTab: [
'form.name', 'form.description', 'form.genre', 'form.url', 'form.timezone', 'form.enable_public_page',
'form.enable_on_demand', 'form.enable_on_demand_download', 'form.default_album_art_url'
],
frontendTab: [
'form.frontend_type', 'form.frontend_config'
],
backendTab: [
'form.backend_type', 'form.backend_config',
],
hlsTab: [
'form.enable_hls',
],
requestsTab: [
'form.enable_requests',
'form.request_delay',
'form.request_threshold'
],
streamersTab: [
'form.enable_streamers',
'form.disconnect_deactivate_streamer'
]
$validationGroups: {
profileTab: [
'form.name', 'form.description', 'form.genre', 'form.url', 'form.timezone', 'form.enable_public_page',
'form.enable_on_demand', 'form.enable_on_demand_download', 'form.default_album_art_url'
],
frontendTab: [
'form.frontend_type', 'form.frontend_config'
],
backendTab: [
'form.backend_type', 'form.backend_config',
],
hlsTab: [
'form.enable_hls',
],
requestsTab: [
'form.enable_requests',
'form.request_delay',
'form.request_threshold'
],
streamersTab: [
'form.enable_streamers',
'form.disconnect_deactivate_streamer'
]
}
};
function mergeCustom(objValue, srcValue) {
@ -250,9 +260,11 @@ export default {
duplicate_prevention_time_range: {},
},
},
profileTab: [
'form.short_name', 'form.api_history_items'
],
$validationGroups: {
profileTab: [
'form.short_name', 'form.api_history_items'
],
}
};
_.mergeWith(formValidations, advancedValidations, mergeCustom);
@ -266,10 +278,12 @@ export default {
podcasts_storage_location: {},
is_enabled: {},
},
adminTab: [
'form.media_storage_location', 'form.recordings_storage_location',
'form.podcasts_storage_location', 'form.is_enabled'
]
$validationGroups: {
adminTab: [
'form.media_storage_location', 'form.recordings_storage_location',
'form.podcasts_storage_location', 'form.is_enabled'
]
}
};
_.mergeWith(formValidations, adminValidations, mergeCustom);
@ -279,9 +293,11 @@ export default {
form: {
radio_base_dir: {},
},
adminTab: [
'form.radio_base_dir'
]
$validationGroups: {
adminTab: [
'form.radio_base_dir'
]
}
}
_.mergeWith(formValidations, advancedAdminValidations, mergeCustom);
@ -294,7 +310,7 @@ export default {
return {
loading: true,
error: null,
form: {},
form: this.getEmptyForm(),
station: {},
};
},
@ -308,7 +324,7 @@ export default {
},
computed: {
isValid() {
return !this.$v.form.$invalid;
return !this.v$?.form?.$invalid ?? true;
},
tabContentClass() {
return (this.isModal)
@ -323,10 +339,7 @@ export default {
}
return null;
},
clear() {
this.loading = false;
this.error = null;
getEmptyForm() {
let form = {
name: '',
description: '',
@ -413,13 +426,18 @@ export default {
}
}
return form;
},
clear() {
this.loading = false;
this.error = null;
this.station = {
stereo_tool_configuration_file_path: null,
links: {
stereo_tool_configuration: null
}
};
this.form = form;
this.form = this.getEmptyForm();
},
reset() {
this.clear();
@ -445,8 +463,8 @@ export default {
return this.form;
},
submit() {
this.$v.form.$touch();
if (this.$v.form.$anyError) {
this.v$.$touch();
if (this.v$.$errors.length > 0) {
return;
}
@ -462,7 +480,7 @@ export default {
: this.createUrl,
data: this.getSubmittableFormData()
})
).then((resp) => {
).then(() => {
this.$notifySuccess();
this.$emit('submitted');
}).catch((error) => {

View File

@ -1,22 +1,25 @@
<template>
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="$v.form.$invalid"
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="v$.form.$invalid"
@submit="doSubmit" @hidden="clearContents">
<storage-location-form :form="$v.form"></storage-location-form>
<storage-location-form :form="v$.form"></storage-location-form>
</modal-form>
</template>
<script>
import {validationMixin} from 'vuelidate';
import {required} from 'vuelidate/dist/validators.min.js';
import {required} from '@vuelidate/validators';
import BaseEditModal from '~/components/Common/BaseEditModal';
import StorageLocationForm from './Form';
import useVuelidate from "@vuelidate/core";
export default {
name: 'AdminStorageLocationsEditModal',
components: {StorageLocationForm},
mixins: [validationMixin, BaseEditModal],
setup() {
return {v$: useVuelidate()}
},
mixins: [BaseEditModal],
props: {
type: String
},

View File

@ -1,16 +1,16 @@
<template>
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error"
:disable-save-button="$v.form.$invalid"
:disable-save-button="v$.form.$invalid"
@submit="doSubmit" @hidden="clearContents">
<admin-users-form :form="$v.form" :roles="roles" :is-edit-mode="isEditMode"></admin-users-form>
<admin-users-form :form="v$.form" :roles="roles" :is-edit-mode="isEditMode"></admin-users-form>
</modal-form>
</template>
<script>
import {validationMixin} from 'vuelidate';
import {email, required} from 'vuelidate/dist/validators.min.js';
import useVuelidate from "@vuelidate/core";
import {email, required} from '@vuelidate/validators';
import BaseEditModal from '~/components/Common/BaseEditModal';
import AdminUsersForm from './Form.vue';
import _ from 'lodash';
@ -19,7 +19,10 @@ import validatePassword from "~/functions/validatePassword";
export default {
name: 'AdminUsersEditModal',
components: {AdminUsersForm},
mixins: [validationMixin, BaseEditModal],
setup() {
return {v$: useVuelidate()}
},
mixins: [BaseEditModal],
props: {
roles: Object
},

View File

@ -3,20 +3,20 @@
</template>
<script>
import {validationMixin} from 'vuelidate';
import useVuelidate from "@vuelidate/core";
import ModalForm from "~/components/Common/ModalForm";
import mergeExisting from "~/functions/mergeExisting";
export default {
name: 'BaseEditModal',
components: {ModalForm},
setup() {
return {v$: useVuelidate()}
},
emits: ['relist'],
props: {
createUrl: String
},
mixins: [
validationMixin
],
data() {
return {
loading: true,
@ -62,7 +62,7 @@ export default {
).then((resp) => {
this.populateForm(resp.data);
this.loading = false;
}).catch((error) => {
}).catch(() => {
this.close();
});
},
@ -83,7 +83,7 @@ export default {
data: this.getSubmittableFormData()
};
},
onSubmitSuccess(response) {
onSubmitSuccess() {
this.$notifySuccess();
this.$emit('relist');
this.close();
@ -91,9 +91,9 @@ export default {
onSubmitError(error) {
this.error = error.response.data.message;
},
doSubmit() {
this.$v.form.$touch();
if (this.$v.form.$anyError) {
async doSubmit() {
this.v$.$touch();
if (this.v$.$errors.length > 0) {
return;
}
@ -111,7 +111,7 @@ export default {
this.$refs.modal.hide();
},
clearContents() {
this.$v.form.$reset();
this.v$.$reset();
this.loading = false;
this.error = null;

View File

@ -64,16 +64,13 @@ export default {
},
computed: {
errorMessages() {
if (!this.field.$error) {
return [];
}
let errors = [];
_.forEach(this.messages, (message, key) => {
const isValid = !!_.get(this.field, key, true);
if (!isValid) {
const params = _.get(this.field, ['$params', key], {});
errors.push(message(params));
_.forEach(this.field.$errors, (error) => {
const message = _.get(this.messages, error.$validator, null);
if (null !== message) {
errors.push(message(error.$params));
} else {
errors.push(error.$message);
}
});

View File

@ -18,7 +18,7 @@
<form id="recover-form" class="form vue-form" action="" method="post">
<input type="hidden" name="csrf" :value="csrf"/>
<b-wrapped-form-group id="password" name="password" label-class="mb-2" :field="$v.form.password"
<b-wrapped-form-group id="password" name="password" label-class="mb-2" :field="v$.form.password"
input-type="password">
<template #label="{lang}">
<icon icon="vpn_key" class="mr-1"></icon>
@ -26,7 +26,7 @@
</template>
</b-wrapped-form-group>
<b-button type="submit" size="lg" block variant="primary" :disabled="$v.form.$invalid"
<b-button type="submit" size="lg" block variant="primary" :disabled="v$.form.$invalid"
class="mt-2">
<translate key="btn_submit">Recover Account</translate>
</b-button>
@ -37,18 +37,18 @@
</template>
<script>
import {validationMixin} from "vuelidate";
import {required} from 'vuelidate/dist/validators.min.js';
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
import Icon from "~/components/Common/Icon";
import validatePassword from '~/functions/validatePassword.js';
import useVuelidate from "@vuelidate/core";
import {required} from '@vuelidate/validators';
export default {
name: 'SetupRegister',
components: {Icon, BWrappedFormGroup},
mixins: [
validationMixin
],
setup() {
return {v$: useVuelidate()}
},
props: {
csrf: String,
error: String,

View File

@ -31,7 +31,7 @@
<form id="login-form" class="form vue-form" action="" method="post">
<input type="hidden" name="csrf" :value="csrf"/>
<b-wrapped-form-group id="username" name="username" label-class="mb-2" :field="$v.form.username"
<b-wrapped-form-group id="username" name="username" label-class="mb-2" :field="v$.form.username"
input-type="email">
<template #label="{lang}">
<icon icon="email" class="mr-1"></icon>
@ -39,7 +39,7 @@
</template>
</b-wrapped-form-group>
<b-wrapped-form-group id="password" name="password" label-class="mb-2" :field="$v.form.password"
<b-wrapped-form-group id="password" name="password" label-class="mb-2" :field="v$.form.password"
input-type="password">
<template #label="{lang}">
<icon icon="vpn_key" class="mr-1"></icon>
@ -47,7 +47,7 @@
</template>
</b-wrapped-form-group>
<b-button type="submit" size="lg" block variant="primary" :disabled="$v.form.$invalid"
<b-button type="submit" size="lg" block variant="primary" :disabled="v$.form.$invalid"
class="mt-2">
<translate key="btn_create_acct">Create Account</translate>
</b-button>
@ -58,8 +58,8 @@
</template>
<script>
import {validationMixin} from "vuelidate";
import {email, required} from 'vuelidate/dist/validators.min.js';
import useVuelidate from "@vuelidate/core";
import {email, required} from '@vuelidate/validators';
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
import Icon from "~/components/Common/Icon";
import validatePassword from '~/functions/validatePassword.js';
@ -67,9 +67,9 @@ import validatePassword from '~/functions/validatePassword.js';
export default {
name: 'SetupRegister',
components: {Icon, BWrappedFormGroup},
mixins: [
validationMixin
],
setup() {
return {v$: useVuelidate()}
},
props: {
csrf: String,
error: String,

View File

@ -1,13 +1,13 @@
<template>
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="$v.form.$invalid"
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="v$.form.$invalid"
@submit="doSubmit" @hidden="clearContents">
<b-tabs content-class="mt-3" pills>
<form-basic-info :form="$v.form"></form-basic-info>
<form-basic-info :form="v$.form"></form-basic-info>
</b-tabs>
</modal-form>
</template>
<script>
import {required} from 'vuelidate/dist/validators.min.js';
import {required} from '@vuelidate/validators';
import BaseEditModal from '~/components/Common/BaseEditModal';
import FormBasicInfo from './Form/BasicInfo';
import mergeExisting from "~/functions/mergeExisting";

View File

@ -22,7 +22,7 @@
<b-overlay variant="card" :show="loading">
<div class="card-body">
<b-form-fieldset v-for="(row, index) in config" :key="index" class="mb-0">
<b-wrapped-form-group v-if="row.is_field" :field="$v.form[row.field_name]"
<b-wrapped-form-group v-if="row.is_field" :field="v$.form[row.field_name]"
:id="'form_edit_'+row.field_name" input-type="textarea"
:input-attrs="{class: 'text-preformatted mb-3', spellcheck: 'false', 'max-rows': 20, rows: 5}">
</b-wrapped-form-group>
@ -31,7 +31,7 @@
</b-form-markup>
</b-form-fieldset>
<b-button size="lg" type="submit" :variant="($v.form.$invalid) ? 'danger' : 'primary'">
<b-button size="lg" type="submit" :variant="(v$.form.$invalid) ? 'danger' : 'primary'">
<translate key="lang_btn_save_changes">Save Changes</translate>
</b-button>
</div>
@ -41,10 +41,10 @@
</template>
<script>
import useVuelidate from "@vuelidate/core";
import BFormFieldset from "~/components/Form/BFormFieldset";
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
import BFormMarkup from "~/components/Form/BFormMarkup";
import {validationMixin} from "vuelidate";
import _ from "lodash";
import mergeExisting from "~/functions/mergeExisting";
import InfoCard from "~/components/Common/InfoCard";
@ -53,6 +53,9 @@ import StationMayNeedRestart from '~/components/Stations/Common/MayNeedRestart.v
export default {
name: 'StationsLiquidsoapConfig',
components: {InfoCard, BFormFieldset, BWrappedFormGroup, BFormMarkup},
setup() {
return {v$: useVuelidate()}
},
props: {
settingsUrl: String,
config: Array,
@ -60,7 +63,6 @@ export default {
},
mixins: [
StationMayNeedRestart,
validationMixin
],
validations() {
let validations = {form: {}};
@ -88,7 +90,7 @@ export default {
},
relist() {
this.resetForm();
this.$v.form.$reset();
this.v$.$reset();
this.loading = true;
this.axios.get(this.settingsUrl).then((resp) => {
@ -97,8 +99,8 @@ export default {
});
},
submit() {
this.$v.form.$touch();
if (this.$v.form.$anyError) {
this.v$.$touch();
if (this.v$.$errors.length > 0) {
return;
}
@ -108,7 +110,7 @@ export default {
url: this.settingsUrl,
data: this.form
})
).then((resp) => {
).then(() => {
this.$notifySuccess();
this.mayNeedRestart();

View File

@ -1,5 +1,5 @@
<template>
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="$v.form.$invalid"
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="v$.form.$invalid"
@submit="doEdit" @hidden="clearContents">
<b-tabs content-class="mt-3" pills>
@ -8,14 +8,14 @@
<translate key="tab_basic_info">Basic Information</translate>
</template>
<media-form-basic-info :form="$v.form"></media-form-basic-info>
<media-form-basic-info :form="v$.form"></media-form-basic-info>
</b-tab>
<b-tab>
<template #title>
<translate key="tab_playlists">Playlists</translate>
</template>
<media-form-playlists :form="$v.form" :playlists="playlists"></media-form-playlists>
<media-form-playlists :form="v$.form" :playlists="playlists"></media-form-playlists>
</b-tab>
<b-tab lazy>
<template #title>
@ -30,7 +30,7 @@
<translate key="tab_custom_fields">Custom Fields</translate>
</template>
<media-form-custom-fields :form="$v.form" :custom-fields="customFields"></media-form-custom-fields>
<media-form-custom-fields :form="v$.form" :custom-fields="customFields"></media-form-custom-fields>
</b-tab>
<b-tab lazy>
@ -47,14 +47,13 @@
<translate key="tab_advanced">Advanced</translate>
</template>
<media-form-advanced-settings :form="$v.form" :song-length="songLength"></media-form-advanced-settings>
<media-form-advanced-settings :form="v$.form" :song-length="songLength"></media-form-advanced-settings>
</b-tab>
</b-tabs>
</modal-form>
</template>
<script>
import {validationMixin} from 'vuelidate';
import {required} from 'vuelidate/dist/validators.min.js';
import {required} from '@vuelidate/validators';
import _ from 'lodash';
import MediaFormBasicInfo from './Form/BasicInfo';
import MediaFormAlbumArt from './Form/AlbumArt';
@ -63,6 +62,7 @@ import MediaFormAdvancedSettings from './Form/AdvancedSettings';
import MediaFormPlaylists from './Form/Playlists';
import MediaFormWaveformEditor from './Form/WaveformEditor';
import ModalForm from "~/components/Common/ModalForm";
import useVuelidate from "@vuelidate/core";
export default {
name: 'EditModal',
@ -75,7 +75,9 @@ export default {
MediaFormAlbumArt,
MediaFormBasicInfo
},
mixins: [validationMixin],
setup() {
return {v$: useVuelidate()}
},
props: {
customFields: Array,
playlists: Array
@ -200,7 +202,7 @@ export default {
});
this.loading = false;
}).catch((error) => {
}).catch(() => {
this.close();
});
},
@ -209,17 +211,17 @@ export default {
},
clearContents() {
this.resetForm();
this.$v.form.$reset();
this.v$.$reset();
},
doEdit() {
this.$v.form.$touch();
if (this.$v.form.$anyError) {
this.v$.$touch();
if (this.v$.$errors.length > 0) {
return;
}
this.error = null;
this.axios.put(this.recordUrl, this.form).then((resp) => {
this.axios.put(this.recordUrl, this.form).then(() => {
this.$notifySuccess();
this.$emit('relist');
this.close();

View File

@ -1,7 +1,7 @@
<template>
<b-modal id="create_directory" centered ref="modal" :title="langNewDirectory">
<b-form @submit.prevent="doMkdir">
<b-wrapped-form-group id="new_directory_name" :field="$v.newDirectory" autofocus>
<b-wrapped-form-group id="new_directory_name" :field="v$.newDirectory" autofocus>
<template #label="{lang}">
<translate :key="lang">Directory Name</translate>
</template>
@ -11,7 +11,7 @@
<b-button variant="default" @click="close" key="lang_btn_close" v-translate>
Close
</b-button>
<b-button :variant="($v.$invalid) ? 'danger' : 'primary'" @click="doMkdir" key="lang_btn_create"
<b-button :variant="(v$.$invalid) ? 'danger' : 'primary'" @click="doMkdir" key="lang_btn_create"
v-translate>
Create Directory
</b-button>
@ -19,14 +19,16 @@
</b-modal>
</template>
<script>
import {validationMixin} from 'vuelidate';
import {required} from 'vuelidate/dist/validators.min.js';
import useVuelidate from "@vuelidate/core";
import {required} from '@vuelidate/validators';
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
export default {
name: 'NewDirectoryModal',
components: {BWrappedFormGroup},
mixins: [validationMixin],
setup() {
return {v$: useVuelidate()}
},
props: {
currentDirectory: String,
mkdirUrl: String
@ -47,14 +49,14 @@ export default {
}
},
methods: {
close () {
close() {
this.newDirectory = null;
this.$v.$reset();
this.v$.$reset();
this.$refs.modal.hide();
},
doMkdir () {
this.$v.$touch();
if (this.$v.$anyError) {
doMkdir() {
this.v$.$touch();
if (this.v$.$errors.length > 0) {
return;
}

View File

@ -1,7 +1,7 @@
<template>
<b-modal id="rename_file" centered ref="modal" :title="langRenameFile">
<b-form @submit.prevent="doRename">
<b-wrapped-form-group id="new_directory_name" :field="$v.form.newPath" autofocus>
<b-wrapped-form-group id="new_directory_name" :field="v$.form.newPath" autofocus>
<template #label="{lang}">
<translate :key="lang">New File Name</translate>
</template>
@ -11,21 +11,23 @@
<b-button variant="default" @click="close">
<translate key="lang_btn_close">Close</translate>
</b-button>
<b-button :variant="($v.form.$invalid) ? 'danger' : 'primary'" @click="doRename">
<b-button :variant="(v$.form.$invalid) ? 'danger' : 'primary'" @click="doRename">
<translate key="lang_btn_rename">Rename</translate>
</b-button>
</template>
</b-modal>
</template>
<script>
import {validationMixin} from 'vuelidate';
import {required} from 'vuelidate/dist/validators.min.js';
import useVuelidate from "@vuelidate/core";
import {required} from '@vuelidate/validators';
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
export default {
name: 'RenameModal',
components: {BWrappedFormGroup},
mixins: [validationMixin],
setup() {
return {v$: useVuelidate()}
},
props: {
renameUrl: String
},
@ -50,19 +52,19 @@ export default {
}
},
methods: {
open (filePath) {
open(filePath) {
this.form.file = filePath;
this.form.newPath = filePath;
this.$refs.modal.show();
},
close () {
this.$v.form.$reset();
close() {
this.v$.$reset();
this.$refs.modal.hide();
},
doRename () {
this.$v.form.$touch();
if (this.$v.form.$anyError) {
doRename() {
this.v$.$touch();
if (this.v$.$errors.length > 0) {
return;
}

View File

@ -1,23 +1,23 @@
<template>
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="$v.form.$invalid"
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="v$.form.$invalid"
@submit="doSubmit" @hidden="clearContents">
<b-tabs content-class="mt-3" pills>
<mount-form-basic-info :form="$v.form"
<mount-form-basic-info :form="v$.form"
:station-frontend-type="stationFrontendType"></mount-form-basic-info>
<mount-form-auto-dj :form="$v.form"
<mount-form-auto-dj :form="v$.form"
:station-frontend-type="stationFrontendType"></mount-form-auto-dj>
<mount-form-intro v-model="$v.form.intro_file.$model" :record-has-intro="record.intro_path !== null"
<mount-form-intro v-model="v$.form.intro_file.$model" :record-has-intro="record.intro_path !== null"
:new-intro-url="newIntroUrl"
:edit-intro-url="record.links.intro"></mount-form-intro>
<mount-form-advanced v-if="showAdvanced" :form="$v.form"
<mount-form-advanced v-if="showAdvanced" :form="v$.form"
:station-frontend-type="stationFrontendType"></mount-form-advanced>
</b-tabs>
</modal-form>
</template>
<script>
import {required} from 'vuelidate/dist/validators.min.js';
import {required} from '@vuelidate/validators';
import BaseEditModal from '~/components/Common/BaseEditModal';
import {FRONTEND_ICECAST, FRONTEND_SHOUTCAST} from '~/components/Entity/RadioAdapters';

View File

@ -1,15 +1,15 @@
<template>
<modal-form ref="modal" id="clone_modal" :title="langTitle" :error="error"
:disable-save-button="$v.form.$invalid" @submit="doSubmit" @hidden="clearContents">
:disable-save-button="v$.form.$invalid" @submit="doSubmit" @hidden="clearContents">
<b-form-row>
<b-wrapped-form-group class="col-md-12" id="form_edit_name" :field="$v.form.name">
<b-wrapped-form-group class="col-md-12" id="form_edit_name" :field="v$.form.name">
<template #label="{lang}">
<translate :key="lang">New Playlist Name</translate>
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-12" id="form_edit_clone" :field="$v.form.clone">
<b-wrapped-form-group class="col-md-12" id="form_edit_clone" :field="v$.form.clone">
<template #label="{lang}">
<translate :key="lang">Customize Copy</translate>
</template>
@ -30,19 +30,19 @@
</template>
<script>
import {required} from 'vuelidate/dist/validators.min.js';
import useVuelidate from "@vuelidate/core";
import {required} from '@vuelidate/validators';
import InvisibleSubmitButton from '~/components/Common/InvisibleSubmitButton';
import {validationMixin} from 'vuelidate';
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
import ModalForm from "~/components/Common/ModalForm";
export default {
name: 'CloneModal',
components: {ModalForm, BWrappedFormGroup, InvisibleSubmitButton},
setup() {
return {v$: useVuelidate()}
},
emits: ['relist', 'needs-restart'],
mixins: [
validationMixin
],
data() {
return {
error: null,
@ -51,7 +51,7 @@ export default {
};
},
computed: {
langTitle () {
langTitle() {
return this.$gettext('Duplicate Playlist');
}
},
@ -75,13 +75,13 @@ export default {
this.cloneUrl = cloneUrl;
let langNewName = this.$gettext('%{name} - Copy');
this.form.name = this.$gettextInterpolate(langNewName, { name: name });
this.form.name = this.$gettextInterpolate(langNewName, {name: name});
this.$refs.modal.show();
},
doSubmit () {
this.$v.form.$touch();
if (this.$v.form.$anyError) {
async doSubmit() {
this.v$.$touch();
if (this.v$.$errors.length > 0) {
return;
}
@ -93,7 +93,7 @@ export default {
url: this.cloneUrl,
data: this.form
})
).then((resp) => {
).then(() => {
this.$notifySuccess();
this.$emit('needs-restart');
this.$emit('relist');
@ -105,7 +105,7 @@ export default {
this.cloneUrl = null;
this.resetForm();
this.$v.form.$reset();
this.v$.$reset();
}
}
};

View File

@ -1,19 +1,19 @@
<template>
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="$v.form.$invalid"
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="v$.form.$invalid"
@submit="doSubmit" @hidden="clearContents">
<b-tabs content-class="mt-3" pills>
<form-basic-info :form="$v.form"></form-basic-info>
<form-schedule :form="$v.form" :schedule-items="form.schedule_items"
<form-basic-info :form="v$.form"></form-basic-info>
<form-schedule :form="v$.form" :schedule-items="form.schedule_items"
:station-time-zone="stationTimeZone"></form-schedule>
<form-advanced :form="$v.form" v-if="enableAdvancedFeatures"></form-advanced>
<form-advanced :form="v$.form" v-if="enableAdvancedFeatures"></form-advanced>
</b-tabs>
</modal-form>
</template>
<script>
import {required} from 'vuelidate/dist/validators.min.js';
import {required} from '@vuelidate/validators';
import FormBasicInfo from './Form/BasicInfo';
import FormSchedule from './Form/Schedule';
import FormAdvanced from './Form/Advanced';
@ -54,16 +54,7 @@ export default {
'include_in_requests': {},
'avoid_duplicates': {},
'backend_options': {},
'schedule_items': {
$each: {
'start_time': {required},
'end_time': {required},
'start_date': {},
'end_date': {},
'days': {},
'loop_once': {}
}
}
'schedule_items': {}
}
},
methods: {
@ -89,7 +80,7 @@ export default {
'schedule_items': []
};
},
onSubmitSuccess(response) {
onSubmitSuccess() {
this.$notifySuccess();
this.$emit('needs-restart');

View File

@ -9,110 +9,10 @@
</p>
</b-form-group>
<b-card v-for="(row, index) in form.schedule_items.$each.$iter" :key="index" class="mb-3" no-body>
<div class="card-header bg-primary-dark d-flex align-items-center">
<div class="flex-fill">
<h2 class="card-title">
<translate :key="'lang_schedule_entry_'+index" :translate-params="{ num: parseInt(index)+1 }">Scheduled Time #%{num}</translate>
</h2>
</div>
<div class="flex-shrink-0">
<b-button size="sm" variant="outline-light" class="py-2 pr-0" @click.prevent="remove(index)">
<icon icon="remove"></icon>
<translate key="lang_btn_remove">Remove</translate>
</b-button>
</div>
</div>
<b-card-body>
<b-form-group>
<b-form-row>
<b-wrapped-form-group class="col-md-4" :id="'edit_form_start_time_'+index"
:field="row.start_time">
<template #label="{lang}">
<translate :key="lang">Start Time</translate>
</template>
<template #description="{lang}">
<translate :key="lang">To play once per day, set the start and end times to the same value.</translate>
</template>
<template #default="props">
<playlist-time :id="props.id" v-model="props.field.$model"
:state="props.state"></playlist-time>
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-4" :id="'edit_form_end_time_'+index" :field="row.end_time">
<template #label="{lang}">
<translate :key="lang">End Time</translate>
</template>
<template #description="{lang}">
<translate :key="lang">If the end time is before the start time, the playlist will play overnight.</translate>
</template>
<template #default="props">
<playlist-time :id="props.id" v-model="props.field.$model"
:state="props.state"></playlist-time>
</template>
</b-wrapped-form-group>
<b-col md="4" class="form-group">
<label>
<translate key="lang_station_tz">Station Time Zone</translate>
</label>
<div>
<translate key="lang_station_tz_desc" :translate-params="{ tz: stationTimeZone }">This station's time zone is currently %{tz}.</translate>
</div>
</b-col>
<b-wrapped-form-group class="col-md-4" :id="'edit_form_start_date_'+index"
:field="row.start_date">
<template #label="{lang}">
<translate :key="lang">Start Date</translate>
</template>
<template #description="{lang}">
<translate :key="lang">To set this schedule to run only within a certain date range, specify a start and end date.</translate>
</template>
<template #default="props">
<b-form-input :id="props.id" type="date"
v-model="props.field.$model" :state="props.state"></b-form-input>
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-4" :id="'edit_form_end_date_'+index" :field="row.end_date">
<template #label="{lang}">
<translate :key="lang">End Date</translate>
</template>
<template #default="props">
<b-form-input :id="props.id" type="date" v-model="props.field.$model"
:state="props.state"></b-form-input>
</template>
</b-wrapped-form-group>
<b-wrapped-form-checkbox class="col-md-4" :id="'edit_form_loop_once_'+index"
:field="row.loop_once">
<template #label="{lang}">
<translate :key="lang">Loop Once</translate>
</template>
<template #description="{lang}">
<translate :key="lang">Only loop through playlist once.</translate>
</template>
</b-wrapped-form-checkbox>
<b-wrapped-form-group class="col-md-4" :id="'edit_form_days_'+index" :field="row.days">
<template #label="{lang}">
<translate :key="lang">Scheduled Play Days of Week</translate>
</template>
<template #description="{lang}">
<translate :key="lang">Leave blank to play on every day of the week.</translate>
</template>
<template #default="props">
<b-checkbox-group stacked :id="'edit_form_days_'+index" v-model="row.days.$model"
:options="dayOptions"></b-checkbox-group>
</template>
</b-wrapped-form-group>
</b-form-row>
</b-form-group>
</b-card-body>
</b-card>
<playlists-form-schedule-row v-for="(row, index) in scheduleItems" :key="index"
:station-time-zone="stationTimeZone"
:index="index" :row.sync="row" @remove="remove(index)">
</playlists-form-schedule-row>
<b-button-group>
<b-button size="sm" variant="outline-primary" @click.prevent="add">
@ -124,34 +24,21 @@
</template>
<script>
import PlaylistTime from '~/components/Common/TimeCode';
import Icon from '~/components/Common/Icon';
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
import BWrappedFormCheckbox from "~/components/Form/BWrappedFormCheckbox";
import PlaylistsFormScheduleRow from "~/components/Stations/Playlists/Form/ScheduleRow.vue";
export default {
name: 'PlaylistEditSchedule',
components: {BWrappedFormCheckbox, BWrappedFormGroup, Icon, PlaylistTime},
components: {PlaylistsFormScheduleRow, BWrappedFormCheckbox, BWrappedFormGroup, Icon},
props: {
form: Object,
stationTimeZone: String,
scheduleItems: Array
},
data() {
return {
dayOptions: [
{value: 1, text: this.$gettext('Monday')},
{value: 2, text: this.$gettext('Tuesday')},
{value: 3, text: this.$gettext('Wednesday')},
{value: 4, text: this.$gettext('Thursday')},
{value: 5, text: this.$gettext('Friday')},
{value: 6, text: this.$gettext('Saturday')},
{value: 7, text: this.$gettext('Sunday')}
]
};
},
computed: {
langTabTitle () {
langTabTitle() {
return this.$gettext('Schedule');
}
},

View File

@ -0,0 +1,153 @@
<template>
<b-card class="mb-3" no-body>
<div class="card-header bg-primary-dark d-flex align-items-center">
<div class="flex-fill">
<h2 class="card-title">
<translate :key="'lang_schedule_entry_'+index" :translate-params="{ num: parseInt(index)+1 }">Scheduled Time #%{num}</translate>
</h2>
</div>
<div class="flex-shrink-0">
<b-button size="sm" variant="outline-light" class="py-2 pr-0" @click.prevent="$emit('remove')">
<icon icon="remove"></icon>
<translate key="lang_btn_remove">Remove</translate>
</b-button>
</div>
</div>
<b-card-body>
<b-form-group>
<b-form-row>
<b-wrapped-form-group class="col-md-4" :id="'edit_form_start_time_'+index"
:field="v$.row.start_time">
<template #label="{lang}">
<translate :key="lang">Start Time</translate>
</template>
<template #description="{lang}">
<translate
:key="lang">To play once per day, set the start and end times to the same value.</translate>
</template>
<template #default="props">
<playlist-time :id="props.id" v-model="props.field.$model"
:state="props.state"></playlist-time>
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-4" :id="'edit_form_end_time_'+index" :field="v$.row.end_time">
<template #label="{lang}">
<translate :key="lang">End Time</translate>
</template>
<template #description="{lang}">
<translate :key="lang">If the end time is before the start time, the playlist will play overnight.</translate>
</template>
<template #default="props">
<playlist-time :id="props.id" v-model="props.field.$model"
:state="props.state"></playlist-time>
</template>
</b-wrapped-form-group>
<b-col md="4" class="form-group">
<label>
<translate key="lang_station_tz">Station Time Zone</translate>
</label>
<div>
<translate key="lang_station_tz_desc" :translate-params="{ tz: stationTimeZone }">This station's time zone is currently %{tz}.</translate>
</div>
</b-col>
<b-wrapped-form-group class="col-md-4" :id="'edit_form_start_date_'+index"
:field="v$.row.start_date">
<template #label="{lang}">
<translate :key="lang">Start Date</translate>
</template>
<template #description="{lang}">
<translate :key="lang">To set this schedule to run only within a certain date range, specify a start and end date.</translate>
</template>
<template #default="props">
<b-form-input :id="props.id" type="date"
v-model="props.field.$model" :state="props.state"></b-form-input>
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-4" :id="'edit_form_end_date_'+index" :field="v$.row.end_date">
<template #label="{lang}">
<translate :key="lang">End Date</translate>
</template>
<template #default="props">
<b-form-input :id="props.id" type="date" v-model="props.field.$model"
:state="props.state"></b-form-input>
</template>
</b-wrapped-form-group>
<b-wrapped-form-checkbox class="col-md-4" :id="'edit_form_loop_once_'+index"
:field="v$.row.loop_once">
<template #label="{lang}">
<translate :key="lang">Loop Once</translate>
</template>
<template #description="{lang}">
<translate :key="lang">Only loop through playlist once.</translate>
</template>
</b-wrapped-form-checkbox>
<b-wrapped-form-group class="col-md-4" :id="'edit_form_days_'+index" :field="v$.row.days">
<template #label="{lang}">
<translate :key="lang">Scheduled Play Days of Week</translate>
</template>
<template #description="{lang}">
<translate :key="lang">Leave blank to play on every day of the week.</translate>
</template>
<template #default="props">
<b-checkbox-group stacked :id="'edit_form_days_'+index" v-model="v$.row.days.$model"
:options="dayOptions"></b-checkbox-group>
</template>
</b-wrapped-form-group>
</b-form-row>
</b-form-group>
</b-card-body>
</b-card>
</template>
<script>
import PlaylistTime from '~/components/Common/TimeCode';
import Icon from "~/components/Common/Icon.vue";
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup.vue";
import {required} from "@vuelidate/validators";
import useVuelidate from "@vuelidate/core";
import BWrappedFormCheckbox from "~/components/Form/BWrappedFormCheckbox.vue";
export default {
name: 'PlaylistsFormScheduleRow',
components: {BWrappedFormCheckbox, BWrappedFormGroup, Icon, PlaylistTime},
props: {
index: Number,
row: Object,
stationTimeZone: String,
},
setup() {
return {v$: useVuelidate()}
},
emits: ['remove'],
validations: {
row: {
'start_time': {required},
'end_time': {required},
'start_date': {},
'end_date': {},
'days': {},
'loop_once': {}
}
},
data() {
return {
dayOptions: [
{value: 1, text: this.$gettext('Monday')},
{value: 2, text: this.$gettext('Tuesday')},
{value: 3, text: this.$gettext('Wednesday')},
{value: 4, text: this.$gettext('Thursday')},
{value: 5, text: this.$gettext('Friday')},
{value: 6, text: this.$gettext('Saturday')},
{value: 7, text: this.$gettext('Sunday')}
]
};
},
}
</script>

View File

@ -1,15 +1,15 @@
<template>
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="$v.form.$invalid"
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="v$.form.$invalid"
@submit="doSubmit" @hidden="clearContents">
<b-tabs content-class="mt-3" pills>
<episode-form-basic-info :form="$v.form"></episode-form-basic-info>
<episode-form-basic-info :form="v$.form"></episode-form-basic-info>
<episode-form-media v-model="$v.form.media_file.$model" :record-has-media="record.has_media"
<episode-form-media v-model="v$.form.media_file.$model" :record-has-media="record.has_media"
:new-media-url="newMediaUrl" :edit-media-url="record.links.media"
:download-url="record.links.download"></episode-form-media>
<podcast-common-artwork v-model="$v.form.artwork_file.$model" :artwork-src="record.art"
<podcast-common-artwork v-model="v$.form.artwork_file.$model" :artwork-src="record.art"
:new-art-url="newArtUrl" :edit-art-url="record.links.art"></podcast-common-artwork>
</b-tabs>
@ -17,7 +17,7 @@
</template>
<script>
import {required} from 'vuelidate/dist/validators.min.js';
import {required} from '@vuelidate/validators';
import BaseEditModal from '~/components/Common/BaseEditModal';
import EpisodeFormBasicInfo from './EpisodeForm/BasicInfo';
import PodcastCommonArtwork from './Common/Artwork';

View File

@ -1,13 +1,13 @@
<template>
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="$v.form.$invalid"
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="v$.form.$invalid"
@submit="doSubmit" @hidden="clearContents">
<b-tabs content-class="mt-3" pills>
<podcast-form-basic-info :form="$v.form"
<podcast-form-basic-info :form="v$.form"
:categories-options="categoriesOptions" :language-options="languageOptions">
</podcast-form-basic-info>
<podcast-common-artwork v-model="$v.form.artwork_file.$model" :artwork-src="record.art"
<podcast-common-artwork v-model="v$.form.artwork_file.$model" :artwork-src="record.art"
:new-art-url="newArtUrl" :edit-art-url="record.links.art"></podcast-common-artwork>
</b-tabs>
@ -15,7 +15,7 @@
</template>
<script>
import {required} from 'vuelidate/dist/validators.min.js';
import {required} from '@vuelidate/validators';
import BaseEditModal from '~/components/Common/BaseEditModal';
import PodcastFormBasicInfo from './PodcastForm/BasicInfo';
import PodcastCommonArtwork from './Common/Artwork';

View File

@ -1,17 +1,17 @@
<template>
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="$v.form.$invalid"
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="v$.form.$invalid"
@submit="doSubmit" @hidden="clearContents">
<b-tabs content-class="mt-3" pills>
<remote-form-basic-info :form="$v.form"></remote-form-basic-info>
<remote-form-basic-info :form="v$.form"></remote-form-basic-info>
<remote-form-auto-dj :form="$v.form"></remote-form-auto-dj>
<remote-form-auto-dj :form="v$.form"></remote-form-auto-dj>
</b-tabs>
</modal-form>
</template>
<script>
import {required} from 'vuelidate/dist/validators.min.js';
import {required} from '@vuelidate/validators';
import BaseEditModal from '~/components/Common/BaseEditModal';
import RemoteFormBasicInfo from "./Form/BasicInfo";
import RemoteFormAutoDj from "./Form/AutoDj";

View File

@ -41,21 +41,21 @@
</b-form-fieldset>
<b-form-fieldset>
<b-wrapped-form-group name="start_date" id="form_start_date" :field="$v.form.start_date"
<b-wrapped-form-group name="start_date" id="form_start_date" :field="v$.form.start_date"
input-type="date">
<template #label="{lang}">
<translate :key="lang">Start Date</translate>
</template>
</b-wrapped-form-group>
<b-wrapped-form-group name="end_date" id="form_end_date" :field="$v.form.end_date"
<b-wrapped-form-group name="end_date" id="form_end_date" :field="v$.form.end_date"
input-type="date">
<template #label="{lang}">
<translate :key="lang">End Date</translate>
</template>
</b-wrapped-form-group>
<b-wrapped-form-checkbox name="fetch_isrc" id="form_edit_fetch_isrc" :field="$v.form.fetch_isrc">
<b-wrapped-form-checkbox name="fetch_isrc" id="form_edit_fetch_isrc" :field="v$.form.fetch_isrc">
<template #label="{lang}">
<translate :key="lang">Attempt to Automatically Retrieve ISRC When Missing</translate>
</template>
@ -65,7 +65,7 @@
</b-wrapped-form-checkbox>
</b-form-fieldset>
<b-button type="submit" size="lg" :variant="($v.form.$invalid) ? 'danger' : 'primary'" class="mt-2">
<b-button type="submit" size="lg" :variant="(v$.form.$invalid) ? 'danger' : 'primary'" class="mt-2">
<translate key="btn_submit">Generate Report</translate>
</b-button>
</div>
@ -74,10 +74,10 @@
</template>
<script>
import useVuelidate from "@vuelidate/core";
import {required} from '@vuelidate/validators';
import Icon from "~/components/Common/Icon";
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
import {validationMixin} from "vuelidate";
import {required} from 'vuelidate/dist/validators.min.js';
import BFormFieldset from "~/components/Form/BFormFieldset";
import BWrappedFormCheckbox from "~/components/Form/BWrappedFormCheckbox";
@ -89,9 +89,9 @@ export default {
endDate: String
},
components: {Icon, BWrappedFormGroup, BFormFieldset, BWrappedFormCheckbox},
mixins: [
validationMixin
],
setup() {
return {v$: useVuelidate()}
},
validations() {
return {
form: {

View File

@ -1,13 +1,13 @@
<template>
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="$v.form.$invalid"
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="v$.form.$invalid"
@submit="doSubmit" @hidden="clearContents">
<sftp-users-form :form="$v.form" :is-edit-mode="isEditMode"></sftp-users-form>
<sftp-users-form :form="v$.form" :is-edit-mode="isEditMode"></sftp-users-form>
</modal-form>
</template>
<script>
import {required} from 'vuelidate/dist/validators.min.js';
import {required} from '@vuelidate/validators';
import BaseEditModal from '~/components/Common/BaseEditModal';
import SftpUsersForm from "./Form";

View File

@ -1,19 +1,19 @@
<template>
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="$v.form.$invalid"
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="v$.form.$invalid"
@submit="doSubmit" @hidden="clearContents">
<b-tabs content-class="mt-3" pills>
<form-basic-info :form="$v.form"></form-basic-info>
<form-schedule :form="$v.form" :schedule-items="form.schedule_items"
<form-basic-info :form="v$.form"></form-basic-info>
<form-schedule :form="v$.form" :schedule-items="form.schedule_items"
:station-time-zone="stationTimeZone"></form-schedule>
<form-artwork v-model="$v.form.artwork_file.$model" :artwork-src="record.links.art"
<form-artwork v-model="v$.form.artwork_file.$model" :artwork-src="record.links.art"
:new-art-url="newArtUrl" :edit-art-url="record.links.art"></form-artwork>
</b-tabs>
</modal-form>
</template>
<script>
import {required} from 'vuelidate/dist/validators.min.js';
import {required} from '@vuelidate/validators';
import FormBasicInfo from './Form/BasicInfo';
import FormSchedule from './Form/Schedule';
import FormArtwork from './Form/Artwork';
@ -38,15 +38,7 @@ export default {
'is_active': {},
'enforce_schedule': {},
'artwork_file': {},
'schedule_items': {
$each: {
'start_time': {required},
'end_time': {required},
'start_date': {},
'end_date': {},
'days': {}
}
}
'schedule_items': {}
}
};

View File

@ -5,94 +5,15 @@
<translate key="lang_schedule_not_scheduled">Not Scheduled</translate>
</label>
<p>
<translate key="lang_schedule_not_scheduled_desc">This streamer is not scheduled to play at any times.</translate>
<translate
key="lang_schedule_not_scheduled_desc">This streamer is not scheduled to play at any times.</translate>
</p>
</b-form-group>
<b-card v-for="(row, index) in form.schedule_items.$each.$iter" :key="index" class="mb-3" no-body>
<div class="card-header bg-primary-dark d-flex align-items-center">
<div class="flex-fill">
<h2 class="card-title">
<translate :key="'lang_scheduled_time_'+index" :translate-params="{ num: parseInt(index)+1 }">Scheduled Time #%{num}</translate>
</h2>
</div>
<div class="flex-shrink-0">
<b-button size="sm" variant="outline-light" class="py-2 pr-0" @click.prevent="remove(index)">
<icon icon="remove"></icon>
<translate key="lang_btn_remove">Remove</translate>
</b-button>
</div>
</div>
<b-card-body>
<b-form-group>
<b-form-row>
<b-wrapped-form-group class="col-md-4" :id="'edit_form_start_time_'+index"
:field="row.start_time">
<template #label="{lang}">
<translate :key="lang">Start Time</translate>
</template>
<template #default="props">
<playlist-time :id="props.id" v-model="props.field.$model"
:state="props.state"></playlist-time>
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-4" :id="'edit_form_end_time_'+index" :field="row.end_time">
<template #label="{lang}">
<translate :key="lang">End Time</translate>
</template>
<template #description="{lang}">
<translate :key="lang">If the end time is before the start time, the schedule entry will continue overnight.</translate>
</template>
<template #default="props">
<playlist-time :id="props.id" v-model="props.field.$model"
:state="props.state"></playlist-time>
</template>
</b-wrapped-form-group>
<b-col md="4" class="form-group">
<label>
<translate key="lang_station_tz">Station Time Zone</translate>
</label>
<div>
<translate key="lang_station_tz_desc" :translate-params="{ tz: stationTimeZone }">This station's time zone is currently %{tz}.</translate>
</div>
</b-col>
<b-wrapped-form-group class="col-md-4" :id="'edit_form_start_date_'+index"
:field="row.start_date" input-type="date">
<template #label="{lang}">
<translate :key="lang">Start Date</translate>
</template>
<template #description="{lang}">
<translate :key="lang">To set this schedule to run only within a certain date range, specify a start and end date.</translate>
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-4" :id="'edit_form_end_date_'+index" :field="row.end_date"
input-type="date">
<template #label="{lang}">
<translate :key="lang">End Date</translate>
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-4" :id="'edit_form_days_'+index" :field="row.days">
<template #label="{lang}">
<translate :key="lang">Scheduled Play Days of Week</translate>
</template>
<template #description="{lang}">
<translate :key="lang">Leave blank to play on every day of the week.</translate>
</template>
<template #default="props">
<b-checkbox-group stacked :id="props.id" v-model="props.field.$model"
:options="dayOptions"></b-checkbox-group>
</template>
</b-wrapped-form-group>
</b-form-row>
</b-form-group>
</b-card-body>
</b-card>
<streamers-form-schedule-row v-for="(row, index) in scheduleItems" :key="index"
:station-time-zone="stationTimeZone"
:index="index" :row.sync="row" @remove="remove(index)">
</streamers-form-schedule-row>
<b-button-group>
<b-button size="sm" variant="outline-primary" @click.prevent="add">
@ -107,10 +28,11 @@
import PlaylistTime from '~/components/Common/TimeCode';
import Icon from '~/components/Common/Icon';
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
import StreamersFormScheduleRow from "~/components/Stations/Streamers/Form/ScheduleRow.vue";
export default {
name: 'StreamerFormSchedule',
components: {BWrappedFormGroup, Icon, PlaylistTime},
components: {StreamersFormScheduleRow, BWrappedFormGroup, Icon, PlaylistTime},
props: {
form: Object,
stationTimeZone: String,

View File

@ -0,0 +1,131 @@
<template>
<b-card class="mb-3" no-body>
<div class="card-header bg-primary-dark d-flex align-items-center">
<div class="flex-fill">
<h2 class="card-title">
<translate :key="'lang_scheduled_time_'+index" :translate-params="{ num: parseInt(index)+1 }">Scheduled Time #%{num}</translate>
</h2>
</div>
<div class="flex-shrink-0">
<b-button size="sm" variant="outline-light" class="py-2 pr-0" @click.prevent="$emit('remove')">
<icon icon="remove"></icon>
<translate key="lang_btn_remove">Remove</translate>
</b-button>
</div>
</div>
<b-card-body>
<b-form-group>
<b-form-row>
<b-wrapped-form-group class="col-md-4" :id="'edit_form_start_time_'+index"
:field="v$.row.start_time">
<template #label="{lang}">
<translate :key="lang">Start Time</translate>
</template>
<template #default="props">
<playlist-time :id="props.id" v-model="props.field.$model"
:state="props.state"></playlist-time>
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-4" :id="'edit_form_end_time_'+index" :field="v$.row.end_time">
<template #label="{lang}">
<translate :key="lang">End Time</translate>
</template>
<template #description="{lang}">
<translate :key="lang">If the end time is before the start time, the schedule entry will continue overnight.</translate>
</template>
<template #default="props">
<playlist-time :id="props.id" v-model="props.field.$model"
:state="props.state"></playlist-time>
</template>
</b-wrapped-form-group>
<b-col md="4" class="form-group">
<label>
<translate key="lang_station_tz">Station Time Zone</translate>
</label>
<div>
<translate key="lang_station_tz_desc" :translate-params="{ tz: stationTimeZone }">This station's time zone is currently %{tz}.</translate>
</div>
</b-col>
<b-wrapped-form-group class="col-md-4" :id="'edit_form_start_date_'+index"
:field="v$.row.start_date" input-type="date">
<template #label="{lang}">
<translate :key="lang">Start Date</translate>
</template>
<template #description="{lang}">
<translate :key="lang">To set this schedule to run only within a certain date range, specify a start and end date.</translate>
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-4" :id="'edit_form_end_date_'+index" :field="v$.row.end_date"
input-type="date">
<template #label="{lang}">
<translate :key="lang">End Date</translate>
</template>
</b-wrapped-form-group>
<b-wrapped-form-group class="col-md-4" :id="'edit_form_days_'+index" :field="v$.row.days">
<template #label="{lang}">
<translate :key="lang">Scheduled Play Days of Week</translate>
</template>
<template #description="{lang}">
<translate :key="lang">Leave blank to play on every day of the week.</translate>
</template>
<template #default="props">
<b-checkbox-group stacked :id="props.id" v-model="props.field.$model"
:options="dayOptions"></b-checkbox-group>
</template>
</b-wrapped-form-group>
</b-form-row>
</b-form-group>
</b-card-body>
</b-card>
</template>
<script>
import PlaylistTime from '~/components/Common/TimeCode';
import Icon from "~/components/Common/Icon.vue";
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup.vue";
import {required} from "@vuelidate/validators";
import useVuelidate from "@vuelidate/core";
import BWrappedFormCheckbox from "~/components/Form/BWrappedFormCheckbox.vue";
export default {
name: 'StreamersFormScheduleRow',
components: {BWrappedFormCheckbox, BWrappedFormGroup, Icon, PlaylistTime},
props: {
index: Number,
row: Object,
stationTimeZone: String,
},
setup() {
return {v$: useVuelidate()}
},
emits: ['remove'],
validations: {
row: {
'start_time': {required},
'end_time': {required},
'start_date': {},
'end_date': {},
'days': {}
}
},
data() {
return {
dayOptions: [
{value: 1, text: this.$gettext('Monday')},
{value: 2, text: this.$gettext('Tuesday')},
{value: 3, text: this.$gettext('Wednesday')},
{value: 4, text: this.$gettext('Thursday')},
{value: 5, text: this.$gettext('Friday')},
{value: 6, text: this.$gettext('Saturday')},
{value: 7, text: this.$gettext('Sunday')}
]
};
},
}
</script>

View File

@ -1,5 +1,5 @@
<template>
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="$v.form.$invalid"
<modal-form ref="modal" :loading="loading" :title="langTitle" :error="error" :disable-save-button="v$.form.$invalid"
@submit="doSubmit" @hidden="clearContents">
<type-select v-if="!type" :webhook-types="webhookTypes" @select="setType"></type-select>
@ -9,17 +9,17 @@
<translate key="tab_basic_info">Basic Info</translate>
</template>
<basic-info :trigger-options="triggerOptions" :form="$v.form"></basic-info>
<basic-info :trigger-options="triggerOptions" :form="v$.form"></basic-info>
</b-tab>
<b-tab :title="typeTitle">
<component :is="formComponent" :now-playing-url="nowPlayingUrl" :form="$v.form"></component>
<component :is="formComponent" :now-playing-url="nowPlayingUrl" :form="v$.form"></component>
</b-tab>
</b-tabs>
</modal-form>
</template>
<script>
import {required} from 'vuelidate/dist/validators.min.js';
import {required} from '@vuelidate/validators';
import BaseEditModal from '~/components/Common/BaseEditModal';
import TypeSelect from "./Form/TypeSelect";
import BasicInfo from "./Form/BasicInfo";

View File

@ -1,4 +1,4 @@
import {helpers} from 'vuelidate/dist/validators.min.js';
import {helpers} from '@vuelidate/validators';
import zxcvbn from "zxcvbn";
export default function validatePassword(value) {