Implement common methods across CRUD components.
This commit is contained in:
parent
bace4da082
commit
6a85fab94c
|
@ -30,7 +30,7 @@
|
|||
|
||||
<data-table
|
||||
id="api_keys"
|
||||
ref="$dataTable"
|
||||
ref="$datatable"
|
||||
:fields="fields"
|
||||
:api-url="apiUrl"
|
||||
>
|
||||
|
@ -52,12 +52,11 @@
|
|||
<script setup>
|
||||
import DataTable from "~/components/Common/DataTable.vue";
|
||||
import {ref} from "vue";
|
||||
import {useSweetAlert} from "~/vendor/sweetalert";
|
||||
import {useNotify} from "~/vendor/bootstrapVue";
|
||||
import {useAxios} from "~/vendor/axios";
|
||||
import {useTranslate} from "~/vendor/gettext";
|
||||
import InfoCard from "~/components/Common/InfoCard.vue";
|
||||
import Icon from "~/components/Common/Icon.vue";
|
||||
import confirmAndDelete from "~/functions/confirmAndDelete";
|
||||
import useHasDatatable from "~/functions/useHasDatatable";
|
||||
|
||||
defineProps({
|
||||
apiUrl: {
|
||||
|
@ -92,28 +91,12 @@ const fields = ref([
|
|||
}
|
||||
]);
|
||||
|
||||
const $dataTable = ref();
|
||||
const $datatable = ref();
|
||||
const {relist} = useHasDatatable($datatable);
|
||||
|
||||
const relist = () => {
|
||||
$dataTable.value.relist();
|
||||
};
|
||||
|
||||
const {confirmDelete} = useSweetAlert();
|
||||
const {wrapWithLoading, notifySuccess} = useNotify();
|
||||
const {axios} = useAxios();
|
||||
|
||||
const doDelete = (url) => {
|
||||
confirmDelete({
|
||||
title: $gettext('Delete API Key?'),
|
||||
}).then((result) => {
|
||||
if (result.value) {
|
||||
wrapWithLoading(
|
||||
axios.delete(url)
|
||||
).then((resp) => {
|
||||
notifySuccess(resp.data.message);
|
||||
relist();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
const doDelete = (url) => confirmAndDelete(
|
||||
url,
|
||||
$gettext('Delete API Key?'),
|
||||
relist
|
||||
);
|
||||
</script>
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<data-table
|
||||
ref="$dataTable"
|
||||
ref="$datatable"
|
||||
responsive
|
||||
paginated
|
||||
:fields="fields"
|
||||
|
@ -118,6 +118,7 @@ import {useAzuraCast} from "~/vendor/azuracast";
|
|||
import DataTable from "~/components/Common/DataTable.vue";
|
||||
import DateRangeDropdown from "~/components/Common/DateRangeDropdown.vue";
|
||||
import Icon from "~/components/Common/Icon.vue";
|
||||
import useHasDatatable from "~/functions/useHasDatatable";
|
||||
|
||||
const props = defineProps({
|
||||
baseApiUrl: {
|
||||
|
@ -164,11 +165,8 @@ const apiUrl = computed(() => {
|
|||
return apiUrl.toString();
|
||||
});
|
||||
|
||||
const $dataTable = ref(); // DataTable Template Ref
|
||||
|
||||
const relist = () => {
|
||||
$dataTable.value.relist();
|
||||
};
|
||||
const $datatable = ref(); // DataTable Template Ref
|
||||
const {relist} = useHasDatatable($datatable);
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
|
|
@ -108,7 +108,7 @@
|
|||
|
||||
<data-table
|
||||
id="api_keys"
|
||||
ref="$dataTable"
|
||||
ref="$datatable"
|
||||
:fields="fields"
|
||||
:api-url="listUrl"
|
||||
>
|
||||
|
@ -169,7 +169,7 @@ import {onMounted, ref} from "vue";
|
|||
import {useTranslate} from "~/vendor/gettext";
|
||||
import {useNotify} from "~/vendor/bootstrapVue";
|
||||
import {useAxios} from "~/vendor/axios";
|
||||
import {useSweetAlert} from "~/vendor/sweetalert";
|
||||
import confirmAndDelete from "~/functions/confirmAndDelete";
|
||||
|
||||
const props = defineProps({
|
||||
listUrl: {
|
||||
|
@ -238,9 +238,9 @@ const fields = [
|
|||
}
|
||||
];
|
||||
|
||||
const $dataTable = ref(); // DataTable
|
||||
const $datatable = ref(); // DataTable
|
||||
|
||||
const {wrapWithLoading, notifySuccess} = useNotify();
|
||||
const {wrapWithLoading} = useNotify();
|
||||
const {axios} = useAxios();
|
||||
|
||||
const relist = () => {
|
||||
|
@ -256,7 +256,7 @@ const relist = () => {
|
|||
settingsLoading.value = false;
|
||||
});
|
||||
|
||||
$dataTable.value.relist();
|
||||
$datatable.value.relist();
|
||||
};
|
||||
|
||||
onMounted(relist);
|
||||
|
@ -280,20 +280,9 @@ const doRunBackup = () => {
|
|||
$runBackupModal.value.open();
|
||||
};
|
||||
|
||||
const {confirmDelete} = useSweetAlert();
|
||||
|
||||
const doDelete = (url) => {
|
||||
confirmDelete({
|
||||
title: $gettext('Delete Backup?')
|
||||
}).then((result) => {
|
||||
if (result.value) {
|
||||
wrapWithLoading(
|
||||
axios.delete(url)
|
||||
).then((resp) => {
|
||||
notifySuccess(resp.data.message);
|
||||
relist();
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
const doDelete = (url) => confirmAndDelete(
|
||||
url,
|
||||
$gettext('Delete Backup?'),
|
||||
relist,
|
||||
);
|
||||
</script>
|
||||
|
|
|
@ -71,9 +71,9 @@ import InfoCard from '~/components/Common/InfoCard.vue';
|
|||
import {get} from 'lodash';
|
||||
import {useTranslate} from "~/vendor/gettext";
|
||||
import {ref} from "vue";
|
||||
import {useSweetAlert} from "~/vendor/sweetalert";
|
||||
import {useNotify} from "~/vendor/bootstrapVue";
|
||||
import {useAxios} from "~/vendor/axios";
|
||||
import useHasDatatable from "~/functions/useHasDatatable";
|
||||
import useHasEditModal from "~/functions/useHasEditModal";
|
||||
import confirmAndDelete from "~/functions/confirmAndDelete";
|
||||
|
||||
const props = defineProps({
|
||||
listUrl: {
|
||||
|
@ -112,37 +112,14 @@ const fields = [
|
|||
];
|
||||
|
||||
const $dataTable = ref(); // DataTable
|
||||
|
||||
const relist = () => {
|
||||
$dataTable.value.refresh();
|
||||
};
|
||||
const {relist} = useHasDatatable($dataTable);
|
||||
|
||||
const $editModal = ref(); // EditModal
|
||||
const {doCreate, doEdit} = useHasEditModal($editModal);
|
||||
|
||||
const doCreate = () => {
|
||||
$editModal.value.create();
|
||||
}
|
||||
|
||||
const doEdit = (url) => {
|
||||
$editModal.value.edit(url);
|
||||
}
|
||||
|
||||
const {confirmDelete} = useSweetAlert();
|
||||
const {wrapWithLoading, notifySuccess} = useNotify();
|
||||
const {axios} = useAxios();
|
||||
|
||||
const doDelete = (url) => {
|
||||
confirmDelete({
|
||||
title: $gettext('Delete Custom Field?')
|
||||
}).then((result) => {
|
||||
if (result.value) {
|
||||
wrapWithLoading(
|
||||
axios.delete(url)
|
||||
).then((resp) => {
|
||||
notifySuccess(resp.data.message);
|
||||
relist();
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
const doDelete = (url) => confirmAndDelete(
|
||||
url,
|
||||
$gettext('Delete Custom Field?'),
|
||||
relist
|
||||
);
|
||||
</script>
|
||||
|
|
|
@ -88,9 +88,9 @@ import InfoCard from '~/components/Common/InfoCard';
|
|||
import {filter, get, map} from 'lodash';
|
||||
import {useTranslate} from "~/vendor/gettext";
|
||||
import {ref} from "vue";
|
||||
import {useNotify} from "~/vendor/bootstrapVue";
|
||||
import {useAxios} from "~/vendor/axios";
|
||||
import {useSweetAlert} from "~/vendor/sweetalert";
|
||||
import useHasDatatable from "~/functions/useHasDatatable";
|
||||
import useHasEditModal from "~/functions/useHasEditModal";
|
||||
import confirmAndDelete from "~/functions/confirmAndDelete";
|
||||
|
||||
const props = defineProps({
|
||||
listUrl: {
|
||||
|
@ -136,37 +136,14 @@ const getStationName = (stationId) => {
|
|||
};
|
||||
|
||||
const $datatable = ref(); // Template Ref
|
||||
|
||||
const relist = () => {
|
||||
$datatable.value.refresh();
|
||||
};
|
||||
const {relist} = useHasDatatable($datatable);
|
||||
|
||||
const $editModal = ref(); // Template Ref
|
||||
const {doCreate, doEdit} = useHasEditModal($editModal);
|
||||
|
||||
const doCreate = () => {
|
||||
$editModal.value.create();
|
||||
};
|
||||
|
||||
const doEdit = (url) => {
|
||||
$editModal.value.edit(url);
|
||||
};
|
||||
|
||||
const {wrapWithLoading, notifySuccess} = useNotify();
|
||||
const {confirmDelete} = useSweetAlert();
|
||||
const {axios} = useAxios();
|
||||
|
||||
const doDelete = (url) => {
|
||||
confirmDelete({
|
||||
title: $gettext('Delete Role?'),
|
||||
}).then((result) => {
|
||||
if (result.value) {
|
||||
wrapWithLoading(
|
||||
axios.delete(url)
|
||||
).then((resp) => {
|
||||
notifySuccess(resp.data.message);
|
||||
relist();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
const doDelete = (url) => confirmAndDelete(
|
||||
url,
|
||||
$gettext('Delete Role?'),
|
||||
relist
|
||||
);
|
||||
</script>
|
||||
|
|
|
@ -88,9 +88,9 @@ import stationFormProps from "./Stations/stationFormProps";
|
|||
import {pickProps} from "~/functions/pickProps";
|
||||
import {useTranslate} from "~/vendor/gettext";
|
||||
import {ref} from "vue";
|
||||
import {useNotify} from "~/vendor/bootstrapVue";
|
||||
import {useAxios} from "~/vendor/axios";
|
||||
import {useSweetAlert} from "~/vendor/sweetalert";
|
||||
import useHasDatatable from "~/functions/useHasDatatable";
|
||||
import useHasEditModal from "~/functions/useHasEditModal";
|
||||
import confirmAndDelete from "~/functions/confirmAndDelete";
|
||||
|
||||
const props = defineProps({
|
||||
...stationFormProps,
|
||||
|
@ -142,20 +142,10 @@ const fields = [
|
|||
];
|
||||
|
||||
const $datatable = ref(); // Template Ref
|
||||
|
||||
const relist = () => {
|
||||
$datatable.value.refresh();
|
||||
};
|
||||
const {relist} = useHasDatatable($datatable);
|
||||
|
||||
const $editModal = ref(); // Template Ref
|
||||
|
||||
const doCreate = () => {
|
||||
$editModal.value.create();
|
||||
};
|
||||
|
||||
const doEdit = (url) => {
|
||||
$editModal.value.edit(url);
|
||||
};
|
||||
const {doCreate, doEdit} = useHasEditModal($editModal);
|
||||
|
||||
const $cloneModal = ref(); // Template Ref
|
||||
|
||||
|
@ -163,22 +153,9 @@ const doClone = (stationName, url) => {
|
|||
$cloneModal.value.create(stationName, url);
|
||||
};
|
||||
|
||||
const {wrapWithLoading, notifySuccess} = useNotify();
|
||||
const {confirmDelete} = useSweetAlert();
|
||||
const {axios} = useAxios();
|
||||
|
||||
const doDelete = (url) => {
|
||||
confirmDelete({
|
||||
title: $gettext('Delete Station?'),
|
||||
}).then((result) => {
|
||||
if (result.value) {
|
||||
wrapWithLoading(
|
||||
axios.delete(url)
|
||||
).then((resp) => {
|
||||
notifySuccess(resp.data.message);
|
||||
relist();
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
const doDelete = (url) => confirmAndDelete(
|
||||
url,
|
||||
$gettext('Delete Station?'),
|
||||
relist
|
||||
);
|
||||
</script>
|
||||
|
|
|
@ -100,9 +100,9 @@ import EditModal from './StorageLocations/EditModal';
|
|||
import Icon from '~/components/Common/Icon';
|
||||
import {computed, ref} from "vue";
|
||||
import {useTranslate} from "~/vendor/gettext";
|
||||
import {useNotify} from "~/vendor/bootstrapVue";
|
||||
import {useAxios} from "~/vendor/axios";
|
||||
import {useSweetAlert} from "~/vendor/sweetalert";
|
||||
import useHasDatatable from "~/functions/useHasDatatable";
|
||||
import useHasEditModal from "~/functions/useHasEditModal";
|
||||
import confirmAndDelete from "~/functions/confirmAndDelete";
|
||||
|
||||
const props = defineProps({
|
||||
listUrl: {
|
||||
|
@ -146,20 +146,10 @@ const tabs = [
|
|||
];
|
||||
|
||||
const $datatable = ref(); // Template Ref
|
||||
|
||||
const relist = () => {
|
||||
$datatable.value?.refresh();
|
||||
};
|
||||
const {relist} = useHasDatatable($datatable);
|
||||
|
||||
const $editModal = ref(); // Template Ref
|
||||
|
||||
const doCreate = () => {
|
||||
$editModal.value?.create();
|
||||
};
|
||||
|
||||
const doEdit = (url) => {
|
||||
$editModal.value?.edit(url);
|
||||
};
|
||||
const {doCreate, doEdit} = useHasEditModal($editModal);
|
||||
|
||||
const setType = (type) => {
|
||||
activeType.value = type;
|
||||
|
@ -198,22 +188,9 @@ const getProgressVariant = (percent) => {
|
|||
}
|
||||
};
|
||||
|
||||
const {notifySuccess, wrapWithLoading} = useNotify();
|
||||
const {confirmDelete} = useSweetAlert();
|
||||
const {axios} = useAxios();
|
||||
|
||||
const doDelete = (url) => {
|
||||
confirmDelete({
|
||||
title: $gettext('Delete Storage Location?'),
|
||||
}).then((result) => {
|
||||
if (result.value) {
|
||||
wrapWithLoading(
|
||||
axios.delete(url)
|
||||
).then((resp) => {
|
||||
notifySuccess(resp.data.message);
|
||||
relist();
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
const doDelete = (url) => confirmAndDelete(
|
||||
url,
|
||||
$gettext('Delete Storage Location?'),
|
||||
relist
|
||||
);
|
||||
</script>
|
||||
|
|
|
@ -92,9 +92,9 @@ import EditModal from './Users/EditModal';
|
|||
import Icon from '~/components/Common/Icon';
|
||||
import {useTranslate} from "~/vendor/gettext";
|
||||
import {ref} from "vue";
|
||||
import {useNotify} from "~/vendor/bootstrapVue";
|
||||
import {useSweetAlert} from "~/vendor/sweetalert";
|
||||
import {useAxios} from "~/vendor/axios";
|
||||
import useHasDatatable from "~/functions/useHasDatatable";
|
||||
import useHasEditModal from "~/functions/useHasEditModal";
|
||||
import confirmAndDelete from "~/functions/confirmAndDelete";
|
||||
|
||||
const props = defineProps({
|
||||
listUrl: {
|
||||
|
@ -116,38 +116,14 @@ const fields = [
|
|||
];
|
||||
|
||||
const $datatable = ref(); // Template Ref
|
||||
|
||||
const relist = () => {
|
||||
$datatable.value.refresh();
|
||||
};
|
||||
const {relist} = useHasDatatable($datatable);
|
||||
|
||||
const $editModal = ref(); // Template Ref
|
||||
const {doCreate, doEdit} = useHasEditModal($editModal);
|
||||
|
||||
const doCreate = () => {
|
||||
$editModal.value.create();
|
||||
};
|
||||
|
||||
const doEdit = (url) => {
|
||||
$editModal.value.edit(url);
|
||||
};
|
||||
|
||||
const {wrapWithLoading, notifySuccess} = useNotify();
|
||||
const {confirmDelete} = useSweetAlert();
|
||||
const {axios} = useAxios();
|
||||
|
||||
const doDelete = (url) => {
|
||||
confirmDelete({
|
||||
title: $gettext('Delete User?'),
|
||||
}).then((result) => {
|
||||
if (result.value) {
|
||||
wrapWithLoading(
|
||||
axios.delete(url)
|
||||
).then((resp) => {
|
||||
notifySuccess(resp.data.message);
|
||||
relist();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const doDelete = (url) => confirmAndDelete(
|
||||
url,
|
||||
$gettext('Delete User?'),
|
||||
relist
|
||||
);
|
||||
</script>
|
||||
|
|
|
@ -1,79 +1,76 @@
|
|||
<template>
|
||||
<modal-form
|
||||
ref="modal"
|
||||
ref="$modal"
|
||||
:loading="loading"
|
||||
:title="langTitle"
|
||||
:error="error"
|
||||
:disable-save-button="v$.form.$invalid"
|
||||
:disable-save-button="v$.$invalid"
|
||||
@submit="doSubmit"
|
||||
@hidden="clearContents"
|
||||
>
|
||||
<admin-users-form
|
||||
:form="v$.form"
|
||||
:form="v$"
|
||||
:roles="roles"
|
||||
:is-edit-mode="isEditMode"
|
||||
/>
|
||||
</modal-form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import useVuelidate from "@vuelidate/core";
|
||||
<script setup>
|
||||
import {email, required} from '@vuelidate/validators';
|
||||
import BaseEditModal from '~/components/Common/BaseEditModal';
|
||||
import AdminUsersForm from './Form.vue';
|
||||
import {map} from 'lodash';
|
||||
import validatePassword from "~/functions/validatePassword";
|
||||
import {computed, ref} from "vue";
|
||||
import {baseEditModalProps, useBaseEditModal} from "~/functions/useBaseEditModal";
|
||||
import {useTranslate} from "~/vendor/gettext";
|
||||
import ModalForm from "~/components/Common/ModalForm.vue";
|
||||
|
||||
/* TODO Options API */
|
||||
const props = defineProps({
|
||||
...baseEditModalProps,
|
||||
roles: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
export default {
|
||||
name: 'AdminUsersEditModal',
|
||||
components: {AdminUsersForm},
|
||||
mixins: [BaseEditModal],
|
||||
props: {
|
||||
roles: {
|
||||
type: Object,
|
||||
required: true
|
||||
const emit = defineEmits(['relist']);
|
||||
|
||||
const $modal = ref(); // Template Ref
|
||||
|
||||
const {
|
||||
loading,
|
||||
error,
|
||||
isEditMode,
|
||||
v$,
|
||||
clearContents,
|
||||
create,
|
||||
edit,
|
||||
doSubmit,
|
||||
close
|
||||
} = useBaseEditModal(
|
||||
props,
|
||||
emit,
|
||||
$modal,
|
||||
(formRef, formIsEditMode) => computed(() => {
|
||||
return {
|
||||
name: {},
|
||||
new_password: (formIsEditMode.value)
|
||||
? {validatePassword}
|
||||
: {required, validatePassword},
|
||||
email: {required, email},
|
||||
roles: {}
|
||||
}
|
||||
}),
|
||||
{
|
||||
name: '',
|
||||
email: '',
|
||||
new_password: '',
|
||||
roles: [],
|
||||
},
|
||||
setup() {
|
||||
return {v$: useVuelidate()}
|
||||
},
|
||||
computed: {
|
||||
langTitle() {
|
||||
return this.isEditMode
|
||||
? this.$gettext('Edit User')
|
||||
: this.$gettext('Add User');
|
||||
}
|
||||
},
|
||||
validations() {
|
||||
let validations = {
|
||||
form: {
|
||||
name: {},
|
||||
email: {required, email},
|
||||
roles: {}
|
||||
}
|
||||
};
|
||||
|
||||
if (this.isEditMode) {
|
||||
validations.form.new_password = {validatePassword};
|
||||
} else {
|
||||
validations.form.new_password = {required, validatePassword};
|
||||
}
|
||||
|
||||
return validations;
|
||||
},
|
||||
methods: {
|
||||
resetForm() {
|
||||
this.form = {
|
||||
name: '',
|
||||
email: '',
|
||||
new_password: '',
|
||||
roles: [],
|
||||
};
|
||||
},
|
||||
populateForm(data) {
|
||||
this.form = {
|
||||
{
|
||||
populateForm: (data, formRef) => {
|
||||
formRef.value = {
|
||||
name: data.name,
|
||||
email: data.email,
|
||||
new_password: '',
|
||||
|
@ -81,5 +78,19 @@ export default {
|
|||
};
|
||||
},
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
const {$gettext} = useTranslate();
|
||||
|
||||
const langTitle = computed(() => {
|
||||
return isEditMode.value
|
||||
? $gettext('Edit User')
|
||||
: $gettext('Add User');
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
create,
|
||||
edit,
|
||||
close
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
<textarea
|
||||
id="log-view-contents"
|
||||
ref="textarea"
|
||||
ref="$textarea"
|
||||
class="form-control log-viewer"
|
||||
spellcheck="false"
|
||||
readonly
|
||||
|
@ -23,84 +23,85 @@
|
|||
</b-overlay>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import {nextTick, onMounted, ref} from "vue";
|
||||
import {useAxios} from "~/vendor/axios";
|
||||
import {useTimeoutFn} from "@vueuse/core";
|
||||
|
||||
/* TODO Options API */
|
||||
const props = defineProps({
|
||||
logUrl: {
|
||||
type: String,
|
||||
required: true,
|
||||
}
|
||||
});
|
||||
|
||||
export default {
|
||||
name: 'StreamingLogView',
|
||||
props: {
|
||||
logUrl: {
|
||||
type: String,
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
logs: '',
|
||||
currentLogPosition: null,
|
||||
timeoutUpdateLog: null,
|
||||
scrollToBottom: true,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.loading = true;
|
||||
const loading = ref(false);
|
||||
const logs = ref('');
|
||||
const currentLogPosition = ref(null);
|
||||
const scrollToBottom = ref(true);
|
||||
|
||||
this.axios({
|
||||
method: 'GET',
|
||||
url: this.logUrl
|
||||
}).then((resp) => {
|
||||
if (resp.data.contents !== '') {
|
||||
this.logs = resp.data.contents + "\n";
|
||||
this.scrollTextarea();
|
||||
} else {
|
||||
this.logs = '';
|
||||
}
|
||||
const {axios} = useAxios();
|
||||
|
||||
this.currentLogPosition = resp.data.position;
|
||||
const $textarea = ref(); // Template Ref
|
||||
|
||||
if (!resp.data.eof) {
|
||||
this.timeoutUpdateLog = setTimeout(this.updateLogs, 2500);
|
||||
}
|
||||
}).finally(() => {
|
||||
this.loading = false;
|
||||
const scrollTextarea = () => {
|
||||
if (scrollToBottom.value) {
|
||||
nextTick(() => {
|
||||
$textarea.value.scrollTop = $textarea.value.scrollHeight;
|
||||
});
|
||||
},
|
||||
beforeUnmount() {
|
||||
clearTimeout(this.timeoutUpdateLog);
|
||||
},
|
||||
methods: {
|
||||
updateLogs() {
|
||||
this.axios({
|
||||
method: 'GET',
|
||||
url: this.logUrl,
|
||||
params: {
|
||||
position: this.currentLogPosition
|
||||
}
|
||||
}).then((resp) => {
|
||||
if (resp.data.contents !== '') {
|
||||
this.logs = this.logs + resp.data.contents + "\n";
|
||||
this.scrollTextarea();
|
||||
}
|
||||
this.currentLogPosition = resp.data.position;
|
||||
|
||||
if (!resp.data.eof) {
|
||||
this.timeoutUpdateLog = setTimeout(this.updateLogs, 2500);
|
||||
}
|
||||
});
|
||||
},
|
||||
getContents() {
|
||||
return this.logs;
|
||||
},
|
||||
scrollTextarea() {
|
||||
if (this.scrollToBottom) {
|
||||
this.$nextTick(() => {
|
||||
const textarea = this.$refs.textarea;
|
||||
textarea.scrollTop = textarea.scrollHeight;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const updateLogs = () => {
|
||||
axios({
|
||||
method: 'GET',
|
||||
url: props.logUrl,
|
||||
params: {
|
||||
position: currentLogPosition.value
|
||||
}
|
||||
}).then((resp) => {
|
||||
if (resp.data.contents !== '') {
|
||||
logs.value = logs.value + resp.data.contents + "\n";
|
||||
scrollTextarea();
|
||||
}
|
||||
|
||||
currentLogPosition.value = resp.data.position;
|
||||
|
||||
if (!resp.data.eof) {
|
||||
useTimeoutFn(updateLogs, 2500);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loading.value = true;
|
||||
|
||||
axios({
|
||||
method: 'GET',
|
||||
url: props.logUrl
|
||||
}).then((resp) => {
|
||||
if (resp.data.contents !== '') {
|
||||
logs.value = resp.data.contents + "\n";
|
||||
scrollTextarea();
|
||||
} else {
|
||||
logs.value = '';
|
||||
}
|
||||
|
||||
currentLogPosition.value = resp.data.position;
|
||||
|
||||
if (!resp.data.eof) {
|
||||
useTimeoutFn(updateLogs, 2500);
|
||||
}
|
||||
}).finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
});
|
||||
|
||||
const getContents = () => {
|
||||
return logs.value;
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
getContents
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -84,7 +84,7 @@
|
|||
|
||||
<b-modal
|
||||
id="import_modal"
|
||||
ref="modal"
|
||||
ref="$modal"
|
||||
:title="$gettext('Import Results')"
|
||||
>
|
||||
<div>
|
||||
|
@ -154,47 +154,49 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import {ref} from "vue";
|
||||
import {useNotify} from "~/vendor/bootstrapVue";
|
||||
import {useAxios} from "~/vendor/axios";
|
||||
|
||||
/* TODO Options API */
|
||||
|
||||
export default {
|
||||
name: 'StationsBulkMedia',
|
||||
props: {
|
||||
apiUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
importFile: null,
|
||||
importResults: {},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
doSubmit() {
|
||||
let formData = new FormData();
|
||||
formData.append('import_file', this.importFile);
|
||||
|
||||
this.$wrapWithLoading(
|
||||
this.axios.post(this.apiUrl, formData)
|
||||
).then((resp) => {
|
||||
this.importFile = null;
|
||||
|
||||
if (resp.data.success) {
|
||||
this.importResults = resp.data;
|
||||
this.$notifySuccess(resp.data.message);
|
||||
this.$refs.modal.show();
|
||||
} else {
|
||||
this.$notifyError(resp.data.message);
|
||||
this.close();
|
||||
}
|
||||
});
|
||||
},
|
||||
closeModal() {
|
||||
this.$refs.modal.hide();
|
||||
}
|
||||
const props = defineProps({
|
||||
apiUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const importFile = ref(null);
|
||||
const importResults = ref(null);
|
||||
|
||||
const {wrapWithLoading, notifySuccess, notifyError} = useNotify();
|
||||
const {axios} = useAxios();
|
||||
|
||||
const $modal = ref(); // Template Ref
|
||||
|
||||
const close = () => {
|
||||
$modal.value.hide();
|
||||
};
|
||||
|
||||
const doSubmit = () => {
|
||||
let formData = new FormData();
|
||||
formData.append('import_file', importFile.value);
|
||||
|
||||
wrapWithLoading(
|
||||
axios.post(props.apiUrl, formData)
|
||||
).then((resp) => {
|
||||
importFile.value = null;
|
||||
|
||||
if (resp.data.success) {
|
||||
importResults.value = resp.data;
|
||||
notifySuccess(resp.data.message);
|
||||
|
||||
$modal.value.show();
|
||||
} else {
|
||||
notifyError(resp.data.message);
|
||||
|
||||
close();
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -79,9 +79,9 @@ import InfoCard from '~/components/Common/InfoCard';
|
|||
import {useTranslate} from "~/vendor/gettext";
|
||||
import {ref} from "vue";
|
||||
import {mayNeedRestartProps, useMayNeedRestart} from "~/functions/useMayNeedRestart";
|
||||
import {useSweetAlert} from "~/vendor/sweetalert";
|
||||
import {useNotify} from "~/vendor/bootstrapVue";
|
||||
import {useAxios} from "~/vendor/axios";
|
||||
import useHasDatatable from "~/functions/useHasDatatable";
|
||||
import useHasEditModal from "~/functions/useHasEditModal";
|
||||
import confirmAndDelete from "~/functions/confirmAndDelete";
|
||||
|
||||
const props = defineProps({
|
||||
...mayNeedRestartProps,
|
||||
|
@ -109,39 +109,19 @@ const upper = (data) => {
|
|||
};
|
||||
|
||||
const $dataTable = ref(); // DataTable
|
||||
|
||||
const relist = () => {
|
||||
$dataTable.value?.refresh();
|
||||
};
|
||||
const {relist} = useHasDatatable($dataTable);
|
||||
|
||||
const $editModal = ref(); // EditModal
|
||||
|
||||
const doCreate = () => {
|
||||
$editModal.value?.create();
|
||||
};
|
||||
|
||||
const doEdit = (url) => {
|
||||
$editModal.value?.edit(url);
|
||||
};
|
||||
const {doCreate, doEdit} = useHasEditModal($editModal);
|
||||
|
||||
const {mayNeedRestart, needsRestart} = useMayNeedRestart(props.restartStatusUrl);
|
||||
const {confirmDelete} = useSweetAlert();
|
||||
const {wrapWithLoading, notifySuccess} = useNotify();
|
||||
const {axios} = useAxios();
|
||||
|
||||
const doDelete = (url) => {
|
||||
confirmDelete({
|
||||
title: $gettext('Delete HLS Stream?'),
|
||||
}).then((result) => {
|
||||
if (result.value) {
|
||||
wrapWithLoading(
|
||||
axios.delete(url)
|
||||
).then((resp) => {
|
||||
notifySuccess(resp.data.message);
|
||||
needsRestart();
|
||||
relist();
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
const doDelete = (url) => confirmAndDelete(
|
||||
url,
|
||||
$gettext('Delete HLS Stream?'),
|
||||
() => {
|
||||
needsRestart();
|
||||
relist();
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
|
|
@ -12,65 +12,76 @@
|
|||
content-class="mt-3"
|
||||
pills
|
||||
>
|
||||
<form-basic-info :form="v$" />
|
||||
<form-basic-info :form="v$"/>
|
||||
</b-tabs>
|
||||
</modal-form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import {required} from '@vuelidate/validators';
|
||||
import BaseEditModal from '~/components/Common/BaseEditModal';
|
||||
import FormBasicInfo from './Form/BasicInfo';
|
||||
import mergeExisting from "~/functions/mergeExisting";
|
||||
import {useVuelidateOnForm} from "~/functions/useVuelidateOnForm";
|
||||
import {baseEditModalProps, useBaseEditModal} from "~/functions/useBaseEditModal";
|
||||
import {computed, ref} from "vue";
|
||||
import {useNotify} from "~/vendor/bootstrapVue";
|
||||
import {useTranslate} from "~/vendor/gettext";
|
||||
import ModalForm from "~/components/Common/ModalForm.vue";
|
||||
|
||||
/* TODO Options API */
|
||||
const props = defineProps({
|
||||
...baseEditModalProps,
|
||||
});
|
||||
|
||||
export default {
|
||||
name: 'EditModal',
|
||||
components: {FormBasicInfo},
|
||||
mixins: [BaseEditModal],
|
||||
emits: ['relist', 'needs-restart'],
|
||||
setup() {
|
||||
const {form, resetForm, v$} = useVuelidateOnForm(
|
||||
{
|
||||
name: {required},
|
||||
format: {required},
|
||||
bitrate: {required}
|
||||
},
|
||||
{
|
||||
name: null,
|
||||
format: 'aac',
|
||||
bitrate: 128
|
||||
}
|
||||
);
|
||||
const emit = defineEmits(['relist', 'needs-restart']);
|
||||
|
||||
return {
|
||||
form,
|
||||
resetForm,
|
||||
v$
|
||||
}
|
||||
const $modal = ref(); // Template Ref
|
||||
|
||||
const {notifySuccess} = useNotify();
|
||||
|
||||
const {
|
||||
loading,
|
||||
error,
|
||||
isEditMode,
|
||||
form,
|
||||
v$,
|
||||
clearContents,
|
||||
create,
|
||||
edit,
|
||||
doSubmit,
|
||||
close
|
||||
} = useBaseEditModal(
|
||||
props,
|
||||
emit,
|
||||
$modal,
|
||||
{
|
||||
name: {required},
|
||||
format: {required},
|
||||
bitrate: {required}
|
||||
},
|
||||
computed: {
|
||||
langTitle() {
|
||||
return this.isEditMode
|
||||
? this.$gettext('Edit HLS Stream')
|
||||
: this.$gettext('Add HLS Stream');
|
||||
}
|
||||
{
|
||||
name: null,
|
||||
format: 'aac',
|
||||
bitrate: 128
|
||||
},
|
||||
methods: {
|
||||
populateForm(d) {
|
||||
this.record = d;
|
||||
this.form = mergeExisting(this.form, d);
|
||||
},
|
||||
onSubmitSuccess() {
|
||||
this.$notifySuccess();
|
||||
|
||||
this.$emit('needs-restart');
|
||||
this.$emit('relist');
|
||||
|
||||
this.close();
|
||||
{
|
||||
onSubmitSuccess: () => {
|
||||
notifySuccess();
|
||||
emit('relist');
|
||||
emit('needs-restart');
|
||||
close();
|
||||
},
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
const {$gettext} = useTranslate();
|
||||
|
||||
const langTitle = computed(() => {
|
||||
return isEditMode.value
|
||||
? $gettext('Edit HLS Stream')
|
||||
: $gettext('Add HLS Stream');
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
create,
|
||||
edit,
|
||||
close
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -69,10 +69,6 @@ const props = defineProps({
|
|||
form: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
stationFrontendType: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<b-modal
|
||||
id="move_file"
|
||||
ref="modal"
|
||||
ref="$modal"
|
||||
size="xl"
|
||||
centered
|
||||
:title="langHeader"
|
||||
|
@ -12,7 +12,7 @@
|
|||
size="sm"
|
||||
variant="primary"
|
||||
:disabled="dirHistory.length === 0"
|
||||
@click="pageBack"
|
||||
@click.prevent="pageBack"
|
||||
>
|
||||
<icon icon="chevron_left" />
|
||||
{{ $gettext('Back') }}
|
||||
|
@ -31,7 +31,7 @@
|
|||
<b-col md="12">
|
||||
<data-table
|
||||
id="station_media"
|
||||
ref="datatable"
|
||||
ref="$datatable"
|
||||
:show-toolbar="false"
|
||||
:selectable="false"
|
||||
:fields="fields"
|
||||
|
@ -71,108 +71,113 @@
|
|||
</template>
|
||||
</b-modal>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
<script setup>
|
||||
import DataTable from '~/components/Common/DataTable.vue';
|
||||
import {forEach} from 'lodash';
|
||||
import Icon from '~/components/Common/Icon';
|
||||
import {computed, h, ref} from "vue";
|
||||
import {useTranslate} from "~/vendor/gettext";
|
||||
import {useNotify} from "~/vendor/bootstrapVue";
|
||||
import {useAxios} from "~/vendor/axios";
|
||||
|
||||
/* TODO Options API */
|
||||
|
||||
export default {
|
||||
name: 'MoveFilesModal',
|
||||
components: {Icon, DataTable},
|
||||
props: {
|
||||
selectedItems: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
currentDirectory: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
batchUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
listDirectoriesUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
const props = defineProps({
|
||||
selectedItems: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
emits: ['relist'],
|
||||
data() {
|
||||
return {
|
||||
destinationDirectory: '',
|
||||
dirHistory: [],
|
||||
fields: [
|
||||
{key: 'directory', label: this.$gettext('Directory'), sortable: false}
|
||||
]
|
||||
};
|
||||
currentDirectory: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
computed: {
|
||||
langHeader () {
|
||||
return this.$gettext(
|
||||
'Move %{ num } File(s) to',
|
||||
{num: this.selectedItems.all.length}
|
||||
);
|
||||
}
|
||||
batchUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
methods: {
|
||||
close () {
|
||||
this.dirHistory = [];
|
||||
this.destinationDirectory = '';
|
||||
|
||||
this.$refs.modal.hide();
|
||||
},
|
||||
doMove () {
|
||||
(this.selectedItems.all.length) && this.$wrapWithLoading(
|
||||
this.axios.put(this.batchUrl, {
|
||||
'do': 'move',
|
||||
'currentDirectory': this.currentDirectory,
|
||||
'directory': this.destinationDirectory,
|
||||
'files': this.selectedItems.files,
|
||||
'dirs': this.selectedItems.directories
|
||||
})
|
||||
).then(() => {
|
||||
let notifyMessage = this.$gettext('Files moved:');
|
||||
let itemNameNodes = [];
|
||||
forEach(this.selectedItems.all, (item) => {
|
||||
itemNameNodes.push(this.$createElement('div', {}, item.name));
|
||||
});
|
||||
|
||||
this.$notifySuccess(itemNameNodes, {
|
||||
title: notifyMessage
|
||||
});
|
||||
}).finally(() => {
|
||||
this.close();
|
||||
this.$emit('relist');
|
||||
});
|
||||
},
|
||||
enterDirectory (path) {
|
||||
this.dirHistory.push(path);
|
||||
this.destinationDirectory = path;
|
||||
|
||||
this.$refs.datatable.refresh();
|
||||
},
|
||||
pageBack: function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
this.dirHistory.pop();
|
||||
|
||||
let newDirectory = this.dirHistory.slice(-1)[0];
|
||||
if (typeof newDirectory === 'undefined' || null === newDirectory) {
|
||||
newDirectory = '';
|
||||
}
|
||||
this.destinationDirectory = newDirectory;
|
||||
|
||||
this.$refs.datatable.refresh();
|
||||
},
|
||||
requestConfig (config) {
|
||||
config.params.currentDirectory = this.destinationDirectory;
|
||||
config.params.csrf = this.csrf;
|
||||
|
||||
return config;
|
||||
}
|
||||
listDirectoriesUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(['relist']);
|
||||
|
||||
const destinationDirectory = ref('');
|
||||
const dirHistory = ref([]);
|
||||
|
||||
const {$gettext} = useTranslate();
|
||||
|
||||
const fields = [
|
||||
{key: 'directory', label: $gettext('Directory'), sortable: false}
|
||||
];
|
||||
|
||||
const langHeader = computed(() => {
|
||||
return $gettext(
|
||||
'Move %{ num } File(s) to',
|
||||
{num: props.selectedItems.all.length}
|
||||
);
|
||||
});
|
||||
|
||||
const $modal = ref(); // Template Ref
|
||||
|
||||
const close = () => {
|
||||
dirHistory.value = [];
|
||||
destinationDirectory.value = '';
|
||||
|
||||
$modal.value.hide();
|
||||
};
|
||||
|
||||
const {wrapWithLoading, notifySuccess} = useNotify();
|
||||
const {axios} = useAxios();
|
||||
|
||||
const doMove = () => {
|
||||
(props.selectedItems.all.length) && wrapWithLoading(
|
||||
axios.put(props.batchUrl, {
|
||||
'do': 'move',
|
||||
'currentDirectory': props.currentDirectory,
|
||||
'directory': destinationDirectory.value,
|
||||
'files': props.selectedItems.files,
|
||||
'dirs': props.selectedItems.directories
|
||||
})
|
||||
).then(() => {
|
||||
let notifyMessage = $gettext('Files moved:');
|
||||
let itemNameNodes = [];
|
||||
forEach(props.selectedItems.all, (item) => {
|
||||
itemNameNodes.push(h('div', {}, item.path_short));
|
||||
});
|
||||
|
||||
notifySuccess(itemNameNodes, {
|
||||
title: notifyMessage
|
||||
});
|
||||
}).finally(() => {
|
||||
close();
|
||||
emit('relist');
|
||||
});
|
||||
};
|
||||
|
||||
const $datatable = ref(); // Template Ref
|
||||
|
||||
const enterDirectory = (path) => {
|
||||
dirHistory.value.push(path);
|
||||
destinationDirectory.value = path;
|
||||
|
||||
$datatable.value.refresh();
|
||||
};
|
||||
|
||||
const pageBack = () => {
|
||||
dirHistory.value.pop();
|
||||
|
||||
let newDirectory = dirHistory.value.slice(-1)[0];
|
||||
if (typeof newDirectory === 'undefined' || null === newDirectory) {
|
||||
newDirectory = '';
|
||||
}
|
||||
|
||||
destinationDirectory.value = newDirectory;
|
||||
$datatable.value.refresh();
|
||||
};
|
||||
|
||||
const requestConfig = (config) => {
|
||||
config.params.currentDirectory = destinationDirectory.value;
|
||||
return config;
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -90,10 +90,10 @@ import InfoCard from '~/components/Common/InfoCard';
|
|||
import {mayNeedRestartProps, useMayNeedRestart} from "~/functions/useMayNeedRestart";
|
||||
import {useTranslate} from "~/vendor/gettext";
|
||||
import {ref} from "vue";
|
||||
import {useSweetAlert} from "~/vendor/sweetalert";
|
||||
import {useNotify} from "~/vendor/bootstrapVue";
|
||||
import {useAxios} from "~/vendor/axios";
|
||||
import showFormatAndBitrate from "~/functions/showFormatAndBitrate";
|
||||
import useHasDatatable from "~/functions/useHasDatatable";
|
||||
import useHasEditModal from "~/functions/useHasEditModal";
|
||||
import confirmAndDelete from "~/functions/confirmAndDelete";
|
||||
|
||||
const props = defineProps({
|
||||
...mayNeedRestartProps,
|
||||
|
@ -124,42 +124,20 @@ const fields = [
|
|||
];
|
||||
|
||||
const $dataTable = ref(); // DataTable
|
||||
|
||||
const relist = () => {
|
||||
$dataTable.value.refresh();
|
||||
};
|
||||
const {relist} = useHasDatatable($dataTable);
|
||||
|
||||
const $editModal = ref(); // EditModal
|
||||
|
||||
const doCreate = () => {
|
||||
$editModal.value.create();
|
||||
};
|
||||
|
||||
const doEdit = (url) => {
|
||||
$editModal.value.edit(url);
|
||||
};
|
||||
const {doCreate, doEdit} = useHasEditModal($editModal);
|
||||
|
||||
const {needsRestart, mayNeedRestart} = useMayNeedRestart(props.restartStatusUrl);
|
||||
|
||||
const {confirmDelete} = useSweetAlert();
|
||||
const {wrapWithLoading, notifySuccess} = useNotify();
|
||||
const {axios} = useAxios();
|
||||
|
||||
const doDelete = (url) => {
|
||||
confirmDelete({
|
||||
title: $gettext('Delete Mount Point?'),
|
||||
}).then((result) => {
|
||||
if (result.value) {
|
||||
wrapWithLoading(
|
||||
axios.delete(url)
|
||||
).then((resp) => {
|
||||
notifySuccess(resp.data.message);
|
||||
needsRestart();
|
||||
relist();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const doDelete = (url) => confirmAndDelete(
|
||||
url,
|
||||
$gettext('Delete Mount Point?'),
|
||||
() => {
|
||||
needsRestart();
|
||||
relist();
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
|
||||
<data-table
|
||||
id="station_playlists"
|
||||
ref="datatable"
|
||||
ref="$datatable"
|
||||
paginated
|
||||
:fields="fields"
|
||||
:responsive="false"
|
||||
|
@ -185,7 +185,7 @@
|
|||
no-body
|
||||
>
|
||||
<schedule
|
||||
ref="schedule"
|
||||
ref="$schedule"
|
||||
:schedule-url="scheduleUrl"
|
||||
:station-time-zone="stationTimeZone"
|
||||
@click="doCalendarClick"
|
||||
|
@ -195,28 +195,28 @@
|
|||
</b-card>
|
||||
|
||||
<edit-modal
|
||||
ref="editModal"
|
||||
ref="$editModal"
|
||||
:create-url="listUrl"
|
||||
:station-time-zone="stationTimeZone"
|
||||
:enable-advanced-features="enableAdvancedFeatures"
|
||||
@relist="relist"
|
||||
@needs-restart="mayNeedRestart"
|
||||
/>
|
||||
<reorder-modal ref="reorderModal" />
|
||||
<queue-modal ref="queueModal" />
|
||||
<reorder-modal ref="reorderModal" />
|
||||
<reorder-modal ref="$reorderModal" />
|
||||
<queue-modal ref="$queueModal" />
|
||||
<reorder-modal ref="$reorderModal" />
|
||||
<import-modal
|
||||
ref="importModal"
|
||||
ref="$importModal"
|
||||
@relist="relist"
|
||||
/>
|
||||
<clone-modal
|
||||
ref="cloneModal"
|
||||
ref="$cloneModal"
|
||||
@relist="relist"
|
||||
@needs-restart="mayNeedRestart"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import DataTable from '~/components/Common/DataTable';
|
||||
import Schedule from '~/components/Common/ScheduleView';
|
||||
import EditModal from './Playlists/EditModal';
|
||||
|
@ -225,181 +225,182 @@ import ImportModal from './Playlists/ImportModal';
|
|||
import QueueModal from './Playlists/QueueModal';
|
||||
import Icon from '~/components/Common/Icon';
|
||||
import CloneModal from './Playlists/CloneModal';
|
||||
import {DateTime} from 'luxon';
|
||||
import humanizeDuration from 'humanize-duration';
|
||||
import {useAzuraCast} from "~/vendor/azuracast";
|
||||
import {useTranslate} from "~/vendor/gettext";
|
||||
import {ref} from "vue";
|
||||
import useHasEditModal from "~/functions/useHasEditModal";
|
||||
import {mayNeedRestartProps, useMayNeedRestart} from "~/functions/useMayNeedRestart";
|
||||
import {useNotify} from "~/vendor/bootstrapVue";
|
||||
import {useAxios} from "~/vendor/axios";
|
||||
import confirmAndDelete from "~/functions/confirmAndDelete";
|
||||
|
||||
/* TODO Options API */
|
||||
|
||||
export default {
|
||||
name: 'StationPlaylists',
|
||||
components: {CloneModal, Icon, QueueModal, ImportModal, ReorderModal, EditModal, Schedule, DataTable},
|
||||
props: {
|
||||
listUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
scheduleUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
filesUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
restartStatusUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
stationTimeZone: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
useManualAutoDj: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
enableAdvancedFeatures: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
}
|
||||
const props = defineProps({
|
||||
...mayNeedRestartProps,
|
||||
listUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
fields: [
|
||||
{key: 'name', isRowHeader: true, label: this.$gettext('Playlist'), sortable: true},
|
||||
{key: 'scheduling', label: this.$gettext('Scheduling'), sortable: false},
|
||||
{key: 'num_songs', label: this.$gettext('# Songs'), sortable: false},
|
||||
{key: 'actions', label: this.$gettext('Actions'), sortable: false, class: 'shrink'}
|
||||
]
|
||||
};
|
||||
scheduleUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
methods: {
|
||||
langToggleButton (record) {
|
||||
return (record.is_enabled)
|
||||
? this.$gettext('Disable')
|
||||
: this.$gettext('Enable');
|
||||
},
|
||||
formatTime (time) {
|
||||
const {timeConfig} = useAzuraCast();
|
||||
filesUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
stationTimeZone: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
useManualAutoDj: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
enableAdvancedFeatures: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
return DateTime.fromSeconds(time).setZone(this.stationTimeZone).toLocaleString(
|
||||
{...DateTime.DATETIME_MED, ...timeConfig}
|
||||
const {$gettext} = useTranslate();
|
||||
|
||||
const fields = [
|
||||
{key: 'name', isRowHeader: true, label: $gettext('Playlist'), sortable: true},
|
||||
{key: 'scheduling', label: $gettext('Scheduling'), sortable: false},
|
||||
{key: 'num_songs', label: $gettext('# Songs'), sortable: false},
|
||||
{key: 'actions', label: $gettext('Actions'), sortable: false, class: 'shrink'}
|
||||
];
|
||||
|
||||
const langToggleButton = (record) => {
|
||||
return (record.is_enabled)
|
||||
? $gettext('Disable')
|
||||
: $gettext('Enable');
|
||||
};
|
||||
|
||||
const {localeShort} = useAzuraCast();
|
||||
|
||||
const formatLength = (length) => humanizeDuration(
|
||||
length * 1000,
|
||||
{
|
||||
round: true,
|
||||
language: localeShort,
|
||||
fallbacks: ['en']
|
||||
}
|
||||
);
|
||||
|
||||
const formatType = (record) => {
|
||||
if (!record.is_enabled) {
|
||||
return $gettext('Disabled');
|
||||
}
|
||||
|
||||
switch (record.type) {
|
||||
case 'default':
|
||||
return $gettext('General Rotation') + '<br>' + $gettext('Weight') + ': ' + record.weight;
|
||||
|
||||
case 'once_per_x_songs':
|
||||
return $gettext(
|
||||
'Once per %{songs} Songs',
|
||||
{songs: record.play_per_songs}
|
||||
);
|
||||
},
|
||||
formatLength (length) {
|
||||
const {localeShort} = useAzuraCast();
|
||||
|
||||
return humanizeDuration(length * 1000, {
|
||||
round: true,
|
||||
language: localeShort,
|
||||
fallbacks: ['en']
|
||||
});
|
||||
},
|
||||
formatType (record) {
|
||||
if (!record.is_enabled) {
|
||||
return this.$gettext('Disabled');
|
||||
}
|
||||
case 'once_per_x_minutes':
|
||||
return $gettext(
|
||||
'Once per %{minutes} Minutes',
|
||||
{minutes: record.play_per_minutes}
|
||||
);
|
||||
|
||||
switch (record.type) {
|
||||
case 'default':
|
||||
return this.$gettext('General Rotation') + '<br>' + this.$gettext('Weight') + ': ' + record.weight;
|
||||
case 'once_per_hour':
|
||||
return $gettext(
|
||||
'Once per Hour (at %{minute})',
|
||||
{minute: record.play_per_hour_minute}
|
||||
);
|
||||
|
||||
case 'once_per_x_songs':
|
||||
return this.$gettext(
|
||||
'Once per %{songs} Songs',
|
||||
{songs: record.play_per_songs}
|
||||
);
|
||||
|
||||
case 'once_per_x_minutes':
|
||||
return this.$gettext(
|
||||
'Once per %{minutes} Minutes',
|
||||
{minutes: record.play_per_minutes}
|
||||
);
|
||||
|
||||
case 'once_per_hour':
|
||||
return this.$gettext(
|
||||
'Once per Hour (at %{minute})',
|
||||
{minute: record.play_per_hour_minute}
|
||||
);
|
||||
|
||||
default:
|
||||
return this.$gettext('Custom');
|
||||
}
|
||||
},
|
||||
relist () {
|
||||
if (this.$refs.datatable) {
|
||||
this.$refs.datatable.refresh();
|
||||
}
|
||||
if (this.$refs.schedule) {
|
||||
this.$refs.schedule.refresh();
|
||||
}
|
||||
},
|
||||
doCreate () {
|
||||
this.$refs.editModal.create();
|
||||
},
|
||||
doCalendarClick (event) {
|
||||
this.doEdit(event.extendedProps.edit_url);
|
||||
},
|
||||
doEdit (url) {
|
||||
this.$refs.editModal.edit(url);
|
||||
},
|
||||
doReorder (url) {
|
||||
this.$refs.reorderModal.open(url);
|
||||
},
|
||||
doQueue (url) {
|
||||
this.$refs.queueModal.open(url);
|
||||
},
|
||||
doImport (url) {
|
||||
this.$refs.importModal.open(url);
|
||||
},
|
||||
doClone (name, url) {
|
||||
this.$refs.cloneModal.open(name, url);
|
||||
},
|
||||
doModify (url) {
|
||||
this.$wrapWithLoading(
|
||||
this.axios.put(url)
|
||||
).then((resp) => {
|
||||
this.needsRestart();
|
||||
|
||||
this.$notifySuccess(resp.data.message);
|
||||
this.relist();
|
||||
});
|
||||
},
|
||||
doDelete (url) {
|
||||
this.$confirmDelete({
|
||||
title: this.$gettext('Delete Playlist?'),
|
||||
}).then((result) => {
|
||||
if (result.value) {
|
||||
this.$wrapWithLoading(
|
||||
this.axios.delete(url)
|
||||
).then((resp) => {
|
||||
this.needsRestart();
|
||||
|
||||
this.$notifySuccess(resp.data.message);
|
||||
this.relist();
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
mayNeedRestart() {
|
||||
if (!this.useManualAutoDj) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.axios.get(this.restartStatusUrl).then((resp) => {
|
||||
if (resp.data.needs_restart) {
|
||||
this.needsRestart();
|
||||
}
|
||||
});
|
||||
},
|
||||
needsRestart() {
|
||||
if (!this.useManualAutoDj) {
|
||||
return;
|
||||
}
|
||||
|
||||
document.dispatchEvent(new CustomEvent("station-needs-restart"));
|
||||
}
|
||||
default:
|
||||
return $gettext('Custom');
|
||||
}
|
||||
};
|
||||
|
||||
const $datatable = ref(); // Template Ref
|
||||
const $schedule = ref(); // Template Ref
|
||||
|
||||
const relist = () => {
|
||||
$datatable.value?.refresh();
|
||||
$schedule.value?.refresh();
|
||||
};
|
||||
|
||||
const $editModal = ref(); // Template Ref
|
||||
const {doCreate, doEdit} = useHasEditModal($editModal);
|
||||
|
||||
const doCalendarClick = (event) => {
|
||||
doEdit(event.extendedProps.edit_url);
|
||||
};
|
||||
|
||||
const $reorderModal = ref(); // Template Ref
|
||||
|
||||
const doReorder = (url) => {
|
||||
$reorderModal.value?.open(url);
|
||||
};
|
||||
|
||||
const $queueModal = ref(); // Template Ref
|
||||
|
||||
const doQueue = (url) => {
|
||||
$queueModal.value?.open(url);
|
||||
};
|
||||
|
||||
const $importModal = ref(); // Template Ref
|
||||
|
||||
const doImport = (url) => {
|
||||
$importModal.value?.open(url);
|
||||
};
|
||||
|
||||
const $cloneModal = ref(); // Template Ref
|
||||
|
||||
const doClone = (name, url) => {
|
||||
$cloneModal.value?.open(name, url);
|
||||
};
|
||||
|
||||
const {
|
||||
mayNeedRestart: originalMayNeedRestart,
|
||||
needsRestart: originalNeedsRestart
|
||||
} = useMayNeedRestart(props.restartStatusUrl);
|
||||
|
||||
const mayNeedRestart = () => {
|
||||
if (!props.useManualAutoDj) {
|
||||
return;
|
||||
}
|
||||
|
||||
originalMayNeedRestart();
|
||||
};
|
||||
|
||||
const needsRestart = () => {
|
||||
if (!props.useManualAutoDj) {
|
||||
return;
|
||||
}
|
||||
|
||||
originalNeedsRestart();
|
||||
};
|
||||
|
||||
const {wrapWithLoading, notifySuccess} = useNotify();
|
||||
const {axios} = useAxios();
|
||||
|
||||
const doModify = (url) => {
|
||||
wrapWithLoading(
|
||||
axios.put(url)
|
||||
).then((resp) => {
|
||||
needsRestart();
|
||||
|
||||
notifySuccess(resp.data.message);
|
||||
relist();
|
||||
});
|
||||
};
|
||||
|
||||
const doDelete = (url) => confirmAndDelete(
|
||||
url,
|
||||
$gettext('Delete Playlist?'),
|
||||
() => {
|
||||
relist();
|
||||
needsRestart();
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
</div>
|
||||
<data-table
|
||||
id="station_queue"
|
||||
ref="datatable"
|
||||
ref="$datatable"
|
||||
:fields="fields"
|
||||
:api-url="listUrl"
|
||||
>
|
||||
|
@ -67,91 +67,87 @@
|
|||
</data-table>
|
||||
</b-card>
|
||||
|
||||
<queue-logs-modal ref="logs_modal" />
|
||||
<queue-logs-modal ref="$logsModal" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import DataTable from '../Common/DataTable';
|
||||
import QueueLogsModal from './Queue/LogsModal';
|
||||
import Icon from "~/components/Common/Icon";
|
||||
import {DateTime} from 'luxon';
|
||||
import {useAzuraCast} from "~/vendor/azuracast";
|
||||
import {useTranslate} from "~/vendor/gettext";
|
||||
import {ref} from "vue";
|
||||
import confirmAndDelete from "~/functions/confirmAndDelete";
|
||||
import useHasDatatable from "~/functions/useHasDatatable";
|
||||
import {useNotify} from "~/vendor/bootstrapVue";
|
||||
import {useAxios} from "~/vendor/axios";
|
||||
|
||||
/* TODO Options API */
|
||||
|
||||
export default {
|
||||
name: 'StationQueue',
|
||||
components: {QueueLogsModal, DataTable, Icon},
|
||||
props: {
|
||||
listUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
clearUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
stationTimeZone: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
const props = defineProps({
|
||||
listUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
fields: [
|
||||
{key: 'actions', label: this.$gettext('Actions'), sortable: false},
|
||||
{key: 'song_title', isRowHeader: true, label: this.$gettext('Song Title'), sortable: false},
|
||||
{key: 'played_at', label: this.$gettext('Expected to Play at'), sortable: false},
|
||||
{key: 'source', label: this.$gettext('Source'), sortable: false}
|
||||
]
|
||||
};
|
||||
clearUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
methods: {
|
||||
formatTime(time) {
|
||||
const {timeConfig} = useAzuraCast();
|
||||
|
||||
return this.getDateTime(time).toLocaleString(
|
||||
{...DateTime.TIME_WITH_SECONDS, ...timeConfig}
|
||||
);
|
||||
},
|
||||
formatRelativeTime(time) {
|
||||
return this.getDateTime(time).toRelative();
|
||||
},
|
||||
getDateTime(timestamp) {
|
||||
return DateTime.fromSeconds(timestamp).setZone(this.stationTimeZone);
|
||||
},
|
||||
doShowLogs(logs) {
|
||||
this.$refs.logs_modal.show(logs);
|
||||
},
|
||||
doDelete(url) {
|
||||
this.$confirmDelete({
|
||||
title: this.$gettext('Delete Queue Item?'),
|
||||
}).then((result) => {
|
||||
if (result.value) {
|
||||
this.$wrapWithLoading(
|
||||
this.axios.delete(url)
|
||||
).then((resp) => {
|
||||
this.$notifySuccess(resp.data.message);
|
||||
this.$refs.datatable.refresh();
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
doClear() {
|
||||
this.$confirmDelete({
|
||||
title: this.$gettext('Clear Upcoming Song Queue?'),
|
||||
confirmButtonText: this.$gettext('Clear'),
|
||||
}).then((result) => {
|
||||
if (result.value) {
|
||||
this.$wrapWithLoading(
|
||||
this.axios.post(this.clearUrl)
|
||||
).then((resp) => {
|
||||
this.$notifySuccess(resp.data.message);
|
||||
this.$refs.datatable.refresh();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
stationTimeZone: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const {$gettext} = useTranslate();
|
||||
|
||||
const fields = [
|
||||
{key: 'actions', label: $gettext('Actions'), sortable: false},
|
||||
{key: 'song_title', isRowHeader: true, label: $gettext('Song Title'), sortable: false},
|
||||
{key: 'played_at', label: $gettext('Expected to Play at'), sortable: false},
|
||||
{key: 'source', label: $gettext('Source'), sortable: false}
|
||||
];
|
||||
|
||||
const getDateTime = (timestamp) =>
|
||||
DateTime.fromSeconds(timestamp).setZone(props.stationTimeZone);
|
||||
|
||||
const {timeConfig} = useAzuraCast();
|
||||
|
||||
const formatTime = (time) => getDateTime(time).toLocaleString(
|
||||
{...DateTime.TIME_WITH_SECONDS, ...timeConfig}
|
||||
);
|
||||
|
||||
const formatRelativeTime = (time) => getDateTime(time).toRelative();
|
||||
|
||||
const $datatable = ref(); // Template Ref
|
||||
const {relist} = useHasDatatable($datatable);
|
||||
|
||||
const $logsModal = ref(); // Template Ref
|
||||
const doShowLogs = (logs) => {
|
||||
$logsModal.value?.show(logs);
|
||||
};
|
||||
|
||||
const doDelete = (url) => confirmAndDelete(
|
||||
url,
|
||||
$gettext('Delete Queue Item?'),
|
||||
relist
|
||||
);
|
||||
|
||||
const {wrapWithLoading, confirmDelete, notifySuccess} = useNotify();
|
||||
const {axios} = useAxios();
|
||||
|
||||
const doClear = () => {
|
||||
confirmDelete({
|
||||
title: $gettext('Clear Upcoming Song Queue?'),
|
||||
confirmButtonText: $gettext('Clear'),
|
||||
}).then((result) => {
|
||||
if (result.value) {
|
||||
wrapWithLoading(
|
||||
axios.post(props.clearUrl)
|
||||
).then((resp) => {
|
||||
notifySuccess(resp.data.message);
|
||||
relist();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -89,10 +89,10 @@ import '~/vendor/sweetalert';
|
|||
import {mayNeedRestartProps, useMayNeedRestart} from "~/functions/useMayNeedRestart";
|
||||
import {useTranslate} from "~/vendor/gettext";
|
||||
import {ref} from "vue";
|
||||
import {useSweetAlert} from "~/vendor/sweetalert";
|
||||
import {useNotify} from "~/vendor/bootstrapVue";
|
||||
import {useAxios} from "~/vendor/axios";
|
||||
import showFormatAndBitrate from "~/functions/showFormatAndBitrate";
|
||||
import useHasDatatable from "~/functions/useHasDatatable";
|
||||
import useHasEditModal from "~/functions/useHasEditModal";
|
||||
import confirmAndDelete from "~/functions/confirmAndDelete";
|
||||
|
||||
const props = defineProps({
|
||||
...mayNeedRestartProps,
|
||||
|
@ -111,40 +111,19 @@ const fields = [
|
|||
];
|
||||
|
||||
const $dataTable = ref(); // DataTable
|
||||
|
||||
const relist = () => {
|
||||
$dataTable.value?.refresh();
|
||||
};
|
||||
const {relist} = useHasDatatable($dataTable);
|
||||
|
||||
const $editModal = ref(); // EditModal
|
||||
|
||||
const doCreate = () => {
|
||||
$editModal.value?.create();
|
||||
};
|
||||
|
||||
const doEdit = (url) => {
|
||||
$editModal.value?.edit(url);
|
||||
};
|
||||
const {doCreate, doEdit} = useHasEditModal($editModal);
|
||||
|
||||
const {mayNeedRestart, needsRestart} = useMayNeedRestart(props.restartStatusUrl);
|
||||
|
||||
const {confirmDelete} = useSweetAlert();
|
||||
const {wrapWithLoading, notifySuccess} = useNotify();
|
||||
const {axios} = useAxios();
|
||||
|
||||
const doDelete = (url) => {
|
||||
confirmDelete({
|
||||
title: $gettext('Delete Remote Relay?'),
|
||||
}).then((result) => {
|
||||
if (result.value) {
|
||||
wrapWithLoading(
|
||||
axios.delete(url)
|
||||
).then((resp) => {
|
||||
notifySuccess(resp.data.message);
|
||||
needsRestart();
|
||||
relist();
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
const doDelete = (url) => confirmAndDelete(
|
||||
url,
|
||||
$gettext('Delete Remote Relay?'),
|
||||
() => {
|
||||
needsRestart();
|
||||
relist();
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
<data-table
|
||||
id="station_remotes"
|
||||
ref="datatable"
|
||||
ref="$datatable"
|
||||
:show-toolbar="false"
|
||||
:fields="fields"
|
||||
:api-url="listUrl"
|
||||
|
@ -75,65 +75,50 @@
|
|||
</div>
|
||||
|
||||
<sftp-users-edit-modal
|
||||
ref="editModal"
|
||||
ref="$editModal"
|
||||
:create-url="listUrl"
|
||||
@relist="relist"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import DataTable from "~/components/Common/DataTable";
|
||||
import SftpUsersEditModal from "./SftpUsers/EditModal";
|
||||
import Icon from "~/components/Common/Icon";
|
||||
import {useTranslate} from "~/vendor/gettext";
|
||||
import {ref} from "vue";
|
||||
import useHasDatatable from "~/functions/useHasDatatable";
|
||||
import useHasEditModal from "~/functions/useHasEditModal";
|
||||
import confirmAndDelete from "~/functions/confirmAndDelete";
|
||||
|
||||
/* TODO Options API */
|
||||
|
||||
export default {
|
||||
name: 'SftpUsers',
|
||||
components: {Icon, SftpUsersEditModal, DataTable},
|
||||
props: {
|
||||
listUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
connectionInfo: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
const props = defineProps({
|
||||
listUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
fields: [
|
||||
{key: 'username', isRowHeader: true, label: this.$gettext('Username'), sortable: false},
|
||||
{key: 'actions', label: this.$gettext('Actions'), sortable: false, class: 'shrink'}
|
||||
]
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
relist() {
|
||||
this.$refs.datatable.refresh();
|
||||
},
|
||||
doCreate() {
|
||||
this.$refs.editModal.create();
|
||||
},
|
||||
doEdit(url) {
|
||||
this.$refs.editModal.edit(url);
|
||||
},
|
||||
doDelete(url) {
|
||||
this.$confirmDelete({
|
||||
title: this.$gettext('Delete SFTP User?')
|
||||
}).then((result) => {
|
||||
if (result.value) {
|
||||
this.$wrapWithLoading(
|
||||
this.axios.delete(url)
|
||||
).then((resp) => {
|
||||
this.$notifySuccess(resp.data.message);
|
||||
this.relist();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
connectionInfo: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const {$gettext} = useTranslate();
|
||||
|
||||
const fields = [
|
||||
{key: 'username', isRowHeader: true, label: $gettext('Username'), sortable: false},
|
||||
{key: 'actions', label: $gettext('Actions'), sortable: false, class: 'shrink'}
|
||||
];
|
||||
|
||||
const $datatable = ref(); // Template Ref
|
||||
const {relist} = useHasDatatable($datatable);
|
||||
|
||||
const $editModal = ref(); // Template Ref
|
||||
const {doCreate, doEdit} = useHasEditModal($editModal);
|
||||
|
||||
const doDelete = (url) => confirmAndDelete(
|
||||
url,
|
||||
$gettext('Delete SFTP User?'),
|
||||
relist
|
||||
);
|
||||
</script>
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
|
||||
<data-table
|
||||
id="station_streamers"
|
||||
ref="datatable"
|
||||
ref="$datatable"
|
||||
:fields="fields"
|
||||
:api-url="listUrl"
|
||||
>
|
||||
|
@ -94,7 +94,7 @@
|
|||
no-body
|
||||
>
|
||||
<schedule
|
||||
ref="schedule"
|
||||
ref="$schedule"
|
||||
:schedule-url="scheduleUrl"
|
||||
:station-time-zone="stationTimeZone"
|
||||
@click="doCalendarClick"
|
||||
|
@ -108,17 +108,17 @@
|
|||
</div>
|
||||
|
||||
<edit-modal
|
||||
ref="editModal"
|
||||
ref="$editModal"
|
||||
:create-url="listUrl"
|
||||
:station-time-zone="stationTimeZone"
|
||||
:new-art-url="newArtUrl"
|
||||
@relist="relist"
|
||||
/>
|
||||
<broadcasts-modal ref="broadcastsModal" />
|
||||
<broadcasts-modal ref="$broadcastsModal" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import DataTable from '~/components/Common/DataTable';
|
||||
import EditModal from './Streamers/EditModal';
|
||||
import BroadcastsModal from './Streamers/BroadcastsModal';
|
||||
|
@ -126,75 +126,64 @@ import Schedule from '~/components/Common/ScheduleView';
|
|||
import Icon from '~/components/Common/Icon';
|
||||
import ConnectionInfo from "./Streamers/ConnectionInfo";
|
||||
import AlbumArt from "~/components/Common/AlbumArt";
|
||||
import {useTranslate} from "~/vendor/gettext";
|
||||
import {ref} from "vue";
|
||||
import useHasDatatable from "~/functions/useHasDatatable";
|
||||
import useHasEditModal from "~/functions/useHasEditModal";
|
||||
import confirmAndDelete from "~/functions/confirmAndDelete";
|
||||
|
||||
/* TODO Options API */
|
||||
|
||||
export default {
|
||||
name: 'StationStreamers',
|
||||
components: {AlbumArt, ConnectionInfo, Icon, EditModal, BroadcastsModal, DataTable, Schedule},
|
||||
props: {
|
||||
listUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
newArtUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
scheduleUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
stationTimeZone: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
connectionInfo: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
const props = defineProps({
|
||||
listUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
fields: [
|
||||
{key: 'art', label: this.$gettext('Art'), sortable: false, class: 'shrink pr-0'},
|
||||
{key: 'display_name', label: this.$gettext('Display Name'), sortable: true},
|
||||
{key: 'streamer_username', isRowHeader: true, label: this.$gettext('Username'), sortable: true},
|
||||
{key: 'comments', label: this.$gettext('Notes'), sortable: false},
|
||||
{key: 'actions', label: this.$gettext('Actions'), sortable: false, class: 'shrink'}
|
||||
]
|
||||
};
|
||||
newArtUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
methods: {
|
||||
relist() {
|
||||
this.$refs.datatable.refresh();
|
||||
},
|
||||
doCreate() {
|
||||
this.$refs.editModal.create();
|
||||
},
|
||||
doCalendarClick(event) {
|
||||
this.doEdit(event.extendedProps.edit_url);
|
||||
},
|
||||
doEdit(url) {
|
||||
this.$refs.editModal.edit(url);
|
||||
},
|
||||
doShowBroadcasts(url) {
|
||||
this.$refs.broadcastsModal.open(url);
|
||||
},
|
||||
doDelete(url) {
|
||||
this.$confirmDelete({
|
||||
title: this.$gettext('Delete Streamer?'),
|
||||
}).then((result) => {
|
||||
if (result.value) {
|
||||
this.$wrapWithLoading(
|
||||
this.axios.delete(url)
|
||||
).then((resp) => {
|
||||
this.$notifySuccess(resp.data.message);
|
||||
this.relist();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
scheduleUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
stationTimeZone: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
connectionInfo: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const {$gettext} = useTranslate();
|
||||
|
||||
const fields = [
|
||||
{key: 'art', label: $gettext('Art'), sortable: false, class: 'shrink pr-0'},
|
||||
{key: 'display_name', label: $gettext('Display Name'), sortable: true},
|
||||
{key: 'streamer_username', isRowHeader: true, label: $gettext('Username'), sortable: true},
|
||||
{key: 'comments', label: $gettext('Notes'), sortable: false},
|
||||
{key: 'actions', label: $gettext('Actions'), sortable: false, class: 'shrink'}
|
||||
];
|
||||
|
||||
const $datatable = ref(); // Template Ref
|
||||
const {relist} = useHasDatatable($datatable);
|
||||
|
||||
const $editModal = ref(); // Template Ref
|
||||
const {doCreate, doEdit} = useHasEditModal($editModal);
|
||||
|
||||
const doCalendarClick = (event) => {
|
||||
doEdit(event.extendedProps.edit_url);
|
||||
};
|
||||
|
||||
const $broadcastsModal = ref(); // Template Ref
|
||||
|
||||
const doShowBroadcasts = (url) => {
|
||||
$broadcastsModal.value.open(url);
|
||||
};
|
||||
|
||||
const doDelete = (url) => confirmAndDelete(
|
||||
url,
|
||||
$gettext('Delete Streamer?'),
|
||||
relist
|
||||
);
|
||||
</script>
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
<data-table
|
||||
id="station_webhooks"
|
||||
ref="datatable"
|
||||
ref="$datatable"
|
||||
:fields="fields"
|
||||
:api-url="listUrl"
|
||||
>
|
||||
|
@ -84,9 +84,9 @@
|
|||
</data-table>
|
||||
</b-card>
|
||||
|
||||
<streaming-log-modal ref="logModal" />
|
||||
<streaming-log-modal ref="$logModal" />
|
||||
<edit-modal
|
||||
ref="editModal"
|
||||
ref="$editModal"
|
||||
:create-url="listUrl"
|
||||
:webhook-types="webhookTypes"
|
||||
:trigger-titles="langTriggerTitles"
|
||||
|
@ -96,125 +96,119 @@
|
|||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
<script setup>
|
||||
import DataTable from '~/components/Common/DataTable';
|
||||
import EditModal from './Webhooks/EditModal';
|
||||
import Icon from '~/components/Common/Icon';
|
||||
import InfoCard from "~/components/Common/InfoCard";
|
||||
import {get, map} from 'lodash';
|
||||
import StreamingLogModal from "~/components/Common/StreamingLogModal";
|
||||
import {useTranslate} from "~/vendor/gettext";
|
||||
import {ref} from "vue";
|
||||
import useHasDatatable from "~/functions/useHasDatatable";
|
||||
import useHasEditModal from "~/functions/useHasEditModal";
|
||||
import {useNotify} from "~/vendor/bootstrapVue";
|
||||
import {useAxios} from "~/vendor/axios";
|
||||
import confirmAndDelete from "~/functions/confirmAndDelete";
|
||||
|
||||
/* TODO Options API */
|
||||
|
||||
export default {
|
||||
name: 'StationWebhooks',
|
||||
components: {StreamingLogModal, InfoCard, Icon, EditModal, DataTable},
|
||||
props: {
|
||||
listUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
nowPlayingUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
webhookTypes: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
const props = defineProps({
|
||||
listUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
fields: [
|
||||
{key: 'name', isRowHeader: true, label: this.$gettext('Name/Type'), sortable: true},
|
||||
{key: 'triggers', label: this.$gettext('Triggers'), sortable: false},
|
||||
{key: 'actions', label: this.$gettext('Actions'), sortable: false, class: 'shrink'}
|
||||
]
|
||||
};
|
||||
nowPlayingUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
computed: {
|
||||
langTriggerTitles() {
|
||||
return {
|
||||
song_changed: this.$gettext('Song Change'),
|
||||
song_changed_live: this.$gettext('Song Change (Live Only)'),
|
||||
listener_gained: this.$gettext('Listener Gained'),
|
||||
listener_lost: this.$gettext('Listener Lost'),
|
||||
live_connect: this.$gettext('Live Streamer/DJ Connected'),
|
||||
live_disconnect: this.$gettext('Live Streamer/DJ Disconnected'),
|
||||
station_offline: this.$gettext('Station Goes Offline'),
|
||||
station_online: this.$gettext('Station Goes Online'),
|
||||
}
|
||||
},
|
||||
langTriggerDescriptions() {
|
||||
return {
|
||||
song_changed: this.$gettext('Any time the currently playing song changes'),
|
||||
song_changed_live: this.$gettext('When the song changes and a live streamer/DJ is connected'),
|
||||
listener_gained: this.$gettext('Any time the listener count increases'),
|
||||
listener_lost: this.$gettext('Any time the listener count decreases'),
|
||||
live_connect: this.$gettext('Any time a live streamer/DJ connects to the stream'),
|
||||
live_disconnect: this.$gettext('Any time a live streamer/DJ disconnects from the stream'),
|
||||
station_offline: this.$gettext('When the station broadcast goes offline'),
|
||||
station_online: this.$gettext('When the station broadcast comes online'),
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
langToggleButton(record) {
|
||||
return (record.is_enabled)
|
||||
? this.$gettext('Disable')
|
||||
: this.$gettext('Enable');
|
||||
},
|
||||
getToggleVariant(record) {
|
||||
return (record.is_enabled)
|
||||
? 'warning'
|
||||
: 'success';
|
||||
},
|
||||
getWebhookName(key) {
|
||||
return get(this.webhookTypes, [key, 'name'], '');
|
||||
},
|
||||
getTriggerNames(triggers) {
|
||||
return map(triggers, (trigger) => {
|
||||
return get(this.langTriggerTitles, trigger, '');
|
||||
});
|
||||
},
|
||||
relist() {
|
||||
this.$refs.datatable.refresh();
|
||||
},
|
||||
doCreate() {
|
||||
this.$refs.editModal.create();
|
||||
},
|
||||
doEdit(url) {
|
||||
this.$refs.editModal.edit(url);
|
||||
},
|
||||
doToggle(url) {
|
||||
this.$wrapWithLoading(
|
||||
this.axios.put(url)
|
||||
).then((resp) => {
|
||||
this.$notifySuccess(resp.data.message);
|
||||
this.relist();
|
||||
});
|
||||
},
|
||||
doTest(url) {
|
||||
this.$wrapWithLoading(
|
||||
this.axios.put(url)
|
||||
).then((resp) => {
|
||||
this.$refs.logModal.show(resp.data.links.log);
|
||||
});
|
||||
},
|
||||
doDelete(url) {
|
||||
this.$confirmDelete({
|
||||
title: this.$gettext('Delete Web Hook?'),
|
||||
}).then((result) => {
|
||||
if (result.value) {
|
||||
this.$wrapWithLoading(
|
||||
this.axios.delete(url)
|
||||
).then((resp) => {
|
||||
this.$notifySuccess(resp.data.message);
|
||||
this.relist();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
webhookTypes: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
|
||||
const {$gettext} = useTranslate();
|
||||
|
||||
const fields = [
|
||||
{key: 'name', isRowHeader: true, label: $gettext('Name/Type'), sortable: true},
|
||||
{key: 'triggers', label: $gettext('Triggers'), sortable: false},
|
||||
{key: 'actions', label: $gettext('Actions'), sortable: false, class: 'shrink'}
|
||||
];
|
||||
|
||||
const langTriggerTitles = {
|
||||
song_changed: $gettext('Song Change'),
|
||||
song_changed_live: $gettext('Song Change (Live Only)'),
|
||||
listener_gained: $gettext('Listener Gained'),
|
||||
listener_lost: $gettext('Listener Lost'),
|
||||
live_connect: $gettext('Live Streamer/DJ Connected'),
|
||||
live_disconnect: $gettext('Live Streamer/DJ Disconnected'),
|
||||
station_offline: $gettext('Station Goes Offline'),
|
||||
station_online: $gettext('Station Goes Online'),
|
||||
};
|
||||
|
||||
const langTriggerDescriptions = {
|
||||
song_changed: $gettext('Any time the currently playing song changes'),
|
||||
song_changed_live: $gettext('When the song changes and a live streamer/DJ is connected'),
|
||||
listener_gained: $gettext('Any time the listener count increases'),
|
||||
listener_lost: $gettext('Any time the listener count decreases'),
|
||||
live_connect: $gettext('Any time a live streamer/DJ connects to the stream'),
|
||||
live_disconnect: $gettext('Any time a live streamer/DJ disconnects from the stream'),
|
||||
station_offline: $gettext('When the station broadcast goes offline'),
|
||||
station_online: $gettext('When the station broadcast comes online'),
|
||||
};
|
||||
|
||||
const langToggleButton = (record) => {
|
||||
return (record.is_enabled)
|
||||
? $gettext('Disable')
|
||||
: $gettext('Enable');
|
||||
};
|
||||
|
||||
const getToggleVariant = (record) => {
|
||||
return (record.is_enabled)
|
||||
? 'warning'
|
||||
: 'success';
|
||||
};
|
||||
|
||||
const getWebhookName = (key) => {
|
||||
return get(props.webhookTypes, [key, 'name'], '');
|
||||
};
|
||||
|
||||
const getTriggerNames = (triggers) => {
|
||||
return map(triggers, (trigger) => {
|
||||
return get(langTriggerTitles, trigger, '');
|
||||
});
|
||||
};
|
||||
|
||||
const $datatable = ref(); // Template Ref
|
||||
const {relist} = useHasDatatable($datatable);
|
||||
|
||||
const $editModal = ref(); // Template Ref
|
||||
const {doCreate, doEdit} = useHasEditModal($editModal);
|
||||
|
||||
const {wrapWithLoading, notifySuccess} = useNotify();
|
||||
const {axios} = useAxios();
|
||||
|
||||
const doToggle = (url) => {
|
||||
wrapWithLoading(
|
||||
axios.put(url)
|
||||
).then((resp) => {
|
||||
notifySuccess(resp.data.message);
|
||||
relist();
|
||||
});
|
||||
};
|
||||
|
||||
const $logModal = ref(); // Template Ref
|
||||
|
||||
const doTest = (url) => {
|
||||
wrapWithLoading(
|
||||
axios.put(url)
|
||||
).then((resp) => {
|
||||
$logModal.value.show(resp.data.links.log);
|
||||
});
|
||||
};
|
||||
|
||||
const doDelete = (url) => confirmAndDelete(
|
||||
url,
|
||||
$gettext('Delete Web Hook?'),
|
||||
relist
|
||||
);
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
import {useSweetAlert} from "~/vendor/sweetalert";
|
||||
import {useNotify} from "~/vendor/bootstrapVue";
|
||||
import {useAxios} from "~/vendor/axios";
|
||||
|
||||
export default function confirmAndDelete(
|
||||
deleteUrl,
|
||||
confirmMessage,
|
||||
onSuccess = null
|
||||
) {
|
||||
const {confirmDelete} = useSweetAlert();
|
||||
const {wrapWithLoading, notifySuccess} = useNotify();
|
||||
const {axios} = useAxios();
|
||||
|
||||
confirmDelete({
|
||||
title: confirmMessage
|
||||
}).then((result) => {
|
||||
if (result.value) {
|
||||
wrapWithLoading(
|
||||
axios.delete(deleteUrl)
|
||||
).then((resp) => {
|
||||
notifySuccess(resp.data.message);
|
||||
|
||||
if (typeof onSuccess === 'function') {
|
||||
onSuccess(resp.data);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
export default function useHasDatatable($datatableRef) {
|
||||
const relist = () => {
|
||||
return $datatableRef.value?.relist();
|
||||
}
|
||||
|
||||
return {
|
||||
relist
|
||||
};
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
export default function useHasEditModal($modalRef) {
|
||||
const doCreate = () => {
|
||||
$modalRef.value?.create();
|
||||
};
|
||||
|
||||
const doEdit = (editUrl) => {
|
||||
$modalRef.value?.edit(editUrl);
|
||||
};
|
||||
|
||||
return {
|
||||
doCreate,
|
||||
doEdit
|
||||
}
|
||||
|
||||
}
|
|
@ -1,7 +1,10 @@
|
|||
import {useAxios} from "~/vendor/axios";
|
||||
|
||||
export const mayNeedRestartProps = {
|
||||
restartStatusUrl: String
|
||||
restartStatusUrl: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
};
|
||||
|
||||
export function useNeedsRestart() {
|
||||
|
|
Loading…
Reference in New Issue