mirror of
https://github.com/AzuraCast/AzuraCast.git
synced 2024-06-13 20:56:36 +00:00
Implement common methods across CRUD components.
This commit is contained in:
parent
bace4da082
commit
6a85fab94c
|
@ -30,7 +30,7 @@
|
||||||
|
|
||||||
<data-table
|
<data-table
|
||||||
id="api_keys"
|
id="api_keys"
|
||||||
ref="$dataTable"
|
ref="$datatable"
|
||||||
:fields="fields"
|
:fields="fields"
|
||||||
:api-url="apiUrl"
|
:api-url="apiUrl"
|
||||||
>
|
>
|
||||||
|
@ -52,12 +52,11 @@
|
||||||
<script setup>
|
<script setup>
|
||||||
import DataTable from "~/components/Common/DataTable.vue";
|
import DataTable from "~/components/Common/DataTable.vue";
|
||||||
import {ref} from "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 {useTranslate} from "~/vendor/gettext";
|
||||||
import InfoCard from "~/components/Common/InfoCard.vue";
|
import InfoCard from "~/components/Common/InfoCard.vue";
|
||||||
import Icon from "~/components/Common/Icon.vue";
|
import Icon from "~/components/Common/Icon.vue";
|
||||||
|
import confirmAndDelete from "~/functions/confirmAndDelete";
|
||||||
|
import useHasDatatable from "~/functions/useHasDatatable";
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
apiUrl: {
|
apiUrl: {
|
||||||
|
@ -92,28 +91,12 @@ const fields = ref([
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const $dataTable = ref();
|
const $datatable = ref();
|
||||||
|
const {relist} = useHasDatatable($datatable);
|
||||||
|
|
||||||
const relist = () => {
|
const doDelete = (url) => confirmAndDelete(
|
||||||
$dataTable.value.relist();
|
url,
|
||||||
};
|
$gettext('Delete API Key?'),
|
||||||
|
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();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<data-table
|
<data-table
|
||||||
ref="$dataTable"
|
ref="$datatable"
|
||||||
responsive
|
responsive
|
||||||
paginated
|
paginated
|
||||||
:fields="fields"
|
:fields="fields"
|
||||||
|
@ -118,6 +118,7 @@ import {useAzuraCast} from "~/vendor/azuracast";
|
||||||
import DataTable from "~/components/Common/DataTable.vue";
|
import DataTable from "~/components/Common/DataTable.vue";
|
||||||
import DateRangeDropdown from "~/components/Common/DateRangeDropdown.vue";
|
import DateRangeDropdown from "~/components/Common/DateRangeDropdown.vue";
|
||||||
import Icon from "~/components/Common/Icon.vue";
|
import Icon from "~/components/Common/Icon.vue";
|
||||||
|
import useHasDatatable from "~/functions/useHasDatatable";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
baseApiUrl: {
|
baseApiUrl: {
|
||||||
|
@ -164,11 +165,8 @@ const apiUrl = computed(() => {
|
||||||
return apiUrl.toString();
|
return apiUrl.toString();
|
||||||
});
|
});
|
||||||
|
|
||||||
const $dataTable = ref(); // DataTable Template Ref
|
const $datatable = ref(); // DataTable Template Ref
|
||||||
|
const {relist} = useHasDatatable($datatable);
|
||||||
const relist = () => {
|
|
||||||
$dataTable.value.relist();
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
|
|
@ -108,7 +108,7 @@
|
||||||
|
|
||||||
<data-table
|
<data-table
|
||||||
id="api_keys"
|
id="api_keys"
|
||||||
ref="$dataTable"
|
ref="$datatable"
|
||||||
:fields="fields"
|
:fields="fields"
|
||||||
:api-url="listUrl"
|
:api-url="listUrl"
|
||||||
>
|
>
|
||||||
|
@ -169,7 +169,7 @@ import {onMounted, ref} from "vue";
|
||||||
import {useTranslate} from "~/vendor/gettext";
|
import {useTranslate} from "~/vendor/gettext";
|
||||||
import {useNotify} from "~/vendor/bootstrapVue";
|
import {useNotify} from "~/vendor/bootstrapVue";
|
||||||
import {useAxios} from "~/vendor/axios";
|
import {useAxios} from "~/vendor/axios";
|
||||||
import {useSweetAlert} from "~/vendor/sweetalert";
|
import confirmAndDelete from "~/functions/confirmAndDelete";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
listUrl: {
|
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 {axios} = useAxios();
|
||||||
|
|
||||||
const relist = () => {
|
const relist = () => {
|
||||||
|
@ -256,7 +256,7 @@ const relist = () => {
|
||||||
settingsLoading.value = false;
|
settingsLoading.value = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
$dataTable.value.relist();
|
$datatable.value.relist();
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(relist);
|
onMounted(relist);
|
||||||
|
@ -280,20 +280,9 @@ const doRunBackup = () => {
|
||||||
$runBackupModal.value.open();
|
$runBackupModal.value.open();
|
||||||
};
|
};
|
||||||
|
|
||||||
const {confirmDelete} = useSweetAlert();
|
const doDelete = (url) => confirmAndDelete(
|
||||||
|
url,
|
||||||
const doDelete = (url) => {
|
$gettext('Delete Backup?'),
|
||||||
confirmDelete({
|
relist,
|
||||||
title: $gettext('Delete Backup?')
|
);
|
||||||
}).then((result) => {
|
|
||||||
if (result.value) {
|
|
||||||
wrapWithLoading(
|
|
||||||
axios.delete(url)
|
|
||||||
).then((resp) => {
|
|
||||||
notifySuccess(resp.data.message);
|
|
||||||
relist();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -71,9 +71,9 @@ import InfoCard from '~/components/Common/InfoCard.vue';
|
||||||
import {get} from 'lodash';
|
import {get} from 'lodash';
|
||||||
import {useTranslate} from "~/vendor/gettext";
|
import {useTranslate} from "~/vendor/gettext";
|
||||||
import {ref} from "vue";
|
import {ref} from "vue";
|
||||||
import {useSweetAlert} from "~/vendor/sweetalert";
|
import useHasDatatable from "~/functions/useHasDatatable";
|
||||||
import {useNotify} from "~/vendor/bootstrapVue";
|
import useHasEditModal from "~/functions/useHasEditModal";
|
||||||
import {useAxios} from "~/vendor/axios";
|
import confirmAndDelete from "~/functions/confirmAndDelete";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
listUrl: {
|
listUrl: {
|
||||||
|
@ -112,37 +112,14 @@ const fields = [
|
||||||
];
|
];
|
||||||
|
|
||||||
const $dataTable = ref(); // DataTable
|
const $dataTable = ref(); // DataTable
|
||||||
|
const {relist} = useHasDatatable($dataTable);
|
||||||
const relist = () => {
|
|
||||||
$dataTable.value.refresh();
|
|
||||||
};
|
|
||||||
|
|
||||||
const $editModal = ref(); // EditModal
|
const $editModal = ref(); // EditModal
|
||||||
|
const {doCreate, doEdit} = useHasEditModal($editModal);
|
||||||
|
|
||||||
const doCreate = () => {
|
const doDelete = (url) => confirmAndDelete(
|
||||||
$editModal.value.create();
|
url,
|
||||||
}
|
$gettext('Delete Custom Field?'),
|
||||||
|
relist
|
||||||
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();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -88,9 +88,9 @@ import InfoCard from '~/components/Common/InfoCard';
|
||||||
import {filter, get, map} from 'lodash';
|
import {filter, get, map} from 'lodash';
|
||||||
import {useTranslate} from "~/vendor/gettext";
|
import {useTranslate} from "~/vendor/gettext";
|
||||||
import {ref} from "vue";
|
import {ref} from "vue";
|
||||||
import {useNotify} from "~/vendor/bootstrapVue";
|
import useHasDatatable from "~/functions/useHasDatatable";
|
||||||
import {useAxios} from "~/vendor/axios";
|
import useHasEditModal from "~/functions/useHasEditModal";
|
||||||
import {useSweetAlert} from "~/vendor/sweetalert";
|
import confirmAndDelete from "~/functions/confirmAndDelete";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
listUrl: {
|
listUrl: {
|
||||||
|
@ -136,37 +136,14 @@ const getStationName = (stationId) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const $datatable = ref(); // Template Ref
|
const $datatable = ref(); // Template Ref
|
||||||
|
const {relist} = useHasDatatable($datatable);
|
||||||
const relist = () => {
|
|
||||||
$datatable.value.refresh();
|
|
||||||
};
|
|
||||||
|
|
||||||
const $editModal = ref(); // Template Ref
|
const $editModal = ref(); // Template Ref
|
||||||
|
const {doCreate, doEdit} = useHasEditModal($editModal);
|
||||||
|
|
||||||
const doCreate = () => {
|
const doDelete = (url) => confirmAndDelete(
|
||||||
$editModal.value.create();
|
url,
|
||||||
};
|
$gettext('Delete Role?'),
|
||||||
|
relist
|
||||||
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();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -88,9 +88,9 @@ import stationFormProps from "./Stations/stationFormProps";
|
||||||
import {pickProps} from "~/functions/pickProps";
|
import {pickProps} from "~/functions/pickProps";
|
||||||
import {useTranslate} from "~/vendor/gettext";
|
import {useTranslate} from "~/vendor/gettext";
|
||||||
import {ref} from "vue";
|
import {ref} from "vue";
|
||||||
import {useNotify} from "~/vendor/bootstrapVue";
|
import useHasDatatable from "~/functions/useHasDatatable";
|
||||||
import {useAxios} from "~/vendor/axios";
|
import useHasEditModal from "~/functions/useHasEditModal";
|
||||||
import {useSweetAlert} from "~/vendor/sweetalert";
|
import confirmAndDelete from "~/functions/confirmAndDelete";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
...stationFormProps,
|
...stationFormProps,
|
||||||
|
@ -142,20 +142,10 @@ const fields = [
|
||||||
];
|
];
|
||||||
|
|
||||||
const $datatable = ref(); // Template Ref
|
const $datatable = ref(); // Template Ref
|
||||||
|
const {relist} = useHasDatatable($datatable);
|
||||||
const relist = () => {
|
|
||||||
$datatable.value.refresh();
|
|
||||||
};
|
|
||||||
|
|
||||||
const $editModal = ref(); // Template Ref
|
const $editModal = ref(); // Template Ref
|
||||||
|
const {doCreate, doEdit} = useHasEditModal($editModal);
|
||||||
const doCreate = () => {
|
|
||||||
$editModal.value.create();
|
|
||||||
};
|
|
||||||
|
|
||||||
const doEdit = (url) => {
|
|
||||||
$editModal.value.edit(url);
|
|
||||||
};
|
|
||||||
|
|
||||||
const $cloneModal = ref(); // Template Ref
|
const $cloneModal = ref(); // Template Ref
|
||||||
|
|
||||||
|
@ -163,22 +153,9 @@ const doClone = (stationName, url) => {
|
||||||
$cloneModal.value.create(stationName, url);
|
$cloneModal.value.create(stationName, url);
|
||||||
};
|
};
|
||||||
|
|
||||||
const {wrapWithLoading, notifySuccess} = useNotify();
|
const doDelete = (url) => confirmAndDelete(
|
||||||
const {confirmDelete} = useSweetAlert();
|
url,
|
||||||
const {axios} = useAxios();
|
$gettext('Delete Station?'),
|
||||||
|
relist
|
||||||
const doDelete = (url) => {
|
);
|
||||||
confirmDelete({
|
|
||||||
title: $gettext('Delete Station?'),
|
|
||||||
}).then((result) => {
|
|
||||||
if (result.value) {
|
|
||||||
wrapWithLoading(
|
|
||||||
axios.delete(url)
|
|
||||||
).then((resp) => {
|
|
||||||
notifySuccess(resp.data.message);
|
|
||||||
relist();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -100,9 +100,9 @@ import EditModal from './StorageLocations/EditModal';
|
||||||
import Icon from '~/components/Common/Icon';
|
import Icon from '~/components/Common/Icon';
|
||||||
import {computed, ref} from "vue";
|
import {computed, ref} from "vue";
|
||||||
import {useTranslate} from "~/vendor/gettext";
|
import {useTranslate} from "~/vendor/gettext";
|
||||||
import {useNotify} from "~/vendor/bootstrapVue";
|
import useHasDatatable from "~/functions/useHasDatatable";
|
||||||
import {useAxios} from "~/vendor/axios";
|
import useHasEditModal from "~/functions/useHasEditModal";
|
||||||
import {useSweetAlert} from "~/vendor/sweetalert";
|
import confirmAndDelete from "~/functions/confirmAndDelete";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
listUrl: {
|
listUrl: {
|
||||||
|
@ -146,20 +146,10 @@ const tabs = [
|
||||||
];
|
];
|
||||||
|
|
||||||
const $datatable = ref(); // Template Ref
|
const $datatable = ref(); // Template Ref
|
||||||
|
const {relist} = useHasDatatable($datatable);
|
||||||
const relist = () => {
|
|
||||||
$datatable.value?.refresh();
|
|
||||||
};
|
|
||||||
|
|
||||||
const $editModal = ref(); // Template Ref
|
const $editModal = ref(); // Template Ref
|
||||||
|
const {doCreate, doEdit} = useHasEditModal($editModal);
|
||||||
const doCreate = () => {
|
|
||||||
$editModal.value?.create();
|
|
||||||
};
|
|
||||||
|
|
||||||
const doEdit = (url) => {
|
|
||||||
$editModal.value?.edit(url);
|
|
||||||
};
|
|
||||||
|
|
||||||
const setType = (type) => {
|
const setType = (type) => {
|
||||||
activeType.value = type;
|
activeType.value = type;
|
||||||
|
@ -198,22 +188,9 @@ const getProgressVariant = (percent) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const {notifySuccess, wrapWithLoading} = useNotify();
|
const doDelete = (url) => confirmAndDelete(
|
||||||
const {confirmDelete} = useSweetAlert();
|
url,
|
||||||
const {axios} = useAxios();
|
$gettext('Delete Storage Location?'),
|
||||||
|
relist
|
||||||
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();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -92,9 +92,9 @@ import EditModal from './Users/EditModal';
|
||||||
import Icon from '~/components/Common/Icon';
|
import Icon from '~/components/Common/Icon';
|
||||||
import {useTranslate} from "~/vendor/gettext";
|
import {useTranslate} from "~/vendor/gettext";
|
||||||
import {ref} from "vue";
|
import {ref} from "vue";
|
||||||
import {useNotify} from "~/vendor/bootstrapVue";
|
import useHasDatatable from "~/functions/useHasDatatable";
|
||||||
import {useSweetAlert} from "~/vendor/sweetalert";
|
import useHasEditModal from "~/functions/useHasEditModal";
|
||||||
import {useAxios} from "~/vendor/axios";
|
import confirmAndDelete from "~/functions/confirmAndDelete";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
listUrl: {
|
listUrl: {
|
||||||
|
@ -116,38 +116,14 @@ const fields = [
|
||||||
];
|
];
|
||||||
|
|
||||||
const $datatable = ref(); // Template Ref
|
const $datatable = ref(); // Template Ref
|
||||||
|
const {relist} = useHasDatatable($datatable);
|
||||||
const relist = () => {
|
|
||||||
$datatable.value.refresh();
|
|
||||||
};
|
|
||||||
|
|
||||||
const $editModal = ref(); // Template Ref
|
const $editModal = ref(); // Template Ref
|
||||||
|
const {doCreate, doEdit} = useHasEditModal($editModal);
|
||||||
|
|
||||||
const doCreate = () => {
|
const doDelete = (url) => confirmAndDelete(
|
||||||
$editModal.value.create();
|
url,
|
||||||
};
|
$gettext('Delete User?'),
|
||||||
|
relist
|
||||||
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();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,79 +1,76 @@
|
||||||
<template>
|
<template>
|
||||||
<modal-form
|
<modal-form
|
||||||
ref="modal"
|
ref="$modal"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:title="langTitle"
|
:title="langTitle"
|
||||||
:error="error"
|
:error="error"
|
||||||
:disable-save-button="v$.form.$invalid"
|
:disable-save-button="v$.$invalid"
|
||||||
@submit="doSubmit"
|
@submit="doSubmit"
|
||||||
@hidden="clearContents"
|
@hidden="clearContents"
|
||||||
>
|
>
|
||||||
<admin-users-form
|
<admin-users-form
|
||||||
:form="v$.form"
|
:form="v$"
|
||||||
:roles="roles"
|
:roles="roles"
|
||||||
:is-edit-mode="isEditMode"
|
:is-edit-mode="isEditMode"
|
||||||
/>
|
/>
|
||||||
</modal-form>
|
</modal-form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import useVuelidate from "@vuelidate/core";
|
|
||||||
import {email, required} from '@vuelidate/validators';
|
import {email, required} from '@vuelidate/validators';
|
||||||
import BaseEditModal from '~/components/Common/BaseEditModal';
|
|
||||||
import AdminUsersForm from './Form.vue';
|
import AdminUsersForm from './Form.vue';
|
||||||
import {map} from 'lodash';
|
import {map} from 'lodash';
|
||||||
import validatePassword from "~/functions/validatePassword";
|
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 {
|
const emit = defineEmits(['relist']);
|
||||||
name: 'AdminUsersEditModal',
|
|
||||||
components: {AdminUsersForm},
|
const $modal = ref(); // Template Ref
|
||||||
mixins: [BaseEditModal],
|
|
||||||
props: {
|
const {
|
||||||
roles: {
|
loading,
|
||||||
type: Object,
|
error,
|
||||||
required: true
|
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()}
|
populateForm: (data, formRef) => {
|
||||||
},
|
formRef.value = {
|
||||||
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 = {
|
|
||||||
name: data.name,
|
name: data.name,
|
||||||
email: data.email,
|
email: data.email,
|
||||||
new_password: '',
|
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>
|
</script>
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
<textarea
|
<textarea
|
||||||
id="log-view-contents"
|
id="log-view-contents"
|
||||||
ref="textarea"
|
ref="$textarea"
|
||||||
class="form-control log-viewer"
|
class="form-control log-viewer"
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
readonly
|
readonly
|
||||||
|
@ -23,84 +23,85 @@
|
||||||
</b-overlay>
|
</b-overlay>
|
||||||
</template>
|
</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 {
|
const loading = ref(false);
|
||||||
name: 'StreamingLogView',
|
const logs = ref('');
|
||||||
props: {
|
const currentLogPosition = ref(null);
|
||||||
logUrl: {
|
const scrollToBottom = ref(true);
|
||||||
type: String,
|
|
||||||
required: true,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
loading: false,
|
|
||||||
logs: '',
|
|
||||||
currentLogPosition: null,
|
|
||||||
timeoutUpdateLog: null,
|
|
||||||
scrollToBottom: true,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.loading = true;
|
|
||||||
|
|
||||||
this.axios({
|
const {axios} = useAxios();
|
||||||
method: 'GET',
|
|
||||||
url: this.logUrl
|
|
||||||
}).then((resp) => {
|
|
||||||
if (resp.data.contents !== '') {
|
|
||||||
this.logs = resp.data.contents + "\n";
|
|
||||||
this.scrollTextarea();
|
|
||||||
} else {
|
|
||||||
this.logs = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
this.currentLogPosition = resp.data.position;
|
const $textarea = ref(); // Template Ref
|
||||||
|
|
||||||
if (!resp.data.eof) {
|
const scrollTextarea = () => {
|
||||||
this.timeoutUpdateLog = setTimeout(this.updateLogs, 2500);
|
if (scrollToBottom.value) {
|
||||||
}
|
nextTick(() => {
|
||||||
}).finally(() => {
|
$textarea.value.scrollTop = $textarea.value.scrollHeight;
|
||||||
this.loading = false;
|
|
||||||
});
|
});
|
||||||
},
|
|
||||||
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>
|
</script>
|
||||||
|
|
|
@ -84,7 +84,7 @@
|
||||||
|
|
||||||
<b-modal
|
<b-modal
|
||||||
id="import_modal"
|
id="import_modal"
|
||||||
ref="modal"
|
ref="$modal"
|
||||||
:title="$gettext('Import Results')"
|
:title="$gettext('Import Results')"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
|
@ -154,47 +154,49 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
|
import {ref} from "vue";
|
||||||
|
import {useNotify} from "~/vendor/bootstrapVue";
|
||||||
|
import {useAxios} from "~/vendor/axios";
|
||||||
|
|
||||||
/* TODO Options API */
|
const props = defineProps({
|
||||||
|
apiUrl: {
|
||||||
export default {
|
type: String,
|
||||||
name: 'StationsBulkMedia',
|
required: true
|
||||||
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 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>
|
</script>
|
||||||
|
|
|
@ -79,9 +79,9 @@ import InfoCard from '~/components/Common/InfoCard';
|
||||||
import {useTranslate} from "~/vendor/gettext";
|
import {useTranslate} from "~/vendor/gettext";
|
||||||
import {ref} from "vue";
|
import {ref} from "vue";
|
||||||
import {mayNeedRestartProps, useMayNeedRestart} from "~/functions/useMayNeedRestart";
|
import {mayNeedRestartProps, useMayNeedRestart} from "~/functions/useMayNeedRestart";
|
||||||
import {useSweetAlert} from "~/vendor/sweetalert";
|
import useHasDatatable from "~/functions/useHasDatatable";
|
||||||
import {useNotify} from "~/vendor/bootstrapVue";
|
import useHasEditModal from "~/functions/useHasEditModal";
|
||||||
import {useAxios} from "~/vendor/axios";
|
import confirmAndDelete from "~/functions/confirmAndDelete";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
...mayNeedRestartProps,
|
...mayNeedRestartProps,
|
||||||
|
@ -109,39 +109,19 @@ const upper = (data) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const $dataTable = ref(); // DataTable
|
const $dataTable = ref(); // DataTable
|
||||||
|
const {relist} = useHasDatatable($dataTable);
|
||||||
const relist = () => {
|
|
||||||
$dataTable.value?.refresh();
|
|
||||||
};
|
|
||||||
|
|
||||||
const $editModal = ref(); // EditModal
|
const $editModal = ref(); // EditModal
|
||||||
|
const {doCreate, doEdit} = useHasEditModal($editModal);
|
||||||
const doCreate = () => {
|
|
||||||
$editModal.value?.create();
|
|
||||||
};
|
|
||||||
|
|
||||||
const doEdit = (url) => {
|
|
||||||
$editModal.value?.edit(url);
|
|
||||||
};
|
|
||||||
|
|
||||||
const {mayNeedRestart, needsRestart} = useMayNeedRestart(props.restartStatusUrl);
|
const {mayNeedRestart, needsRestart} = useMayNeedRestart(props.restartStatusUrl);
|
||||||
const {confirmDelete} = useSweetAlert();
|
|
||||||
const {wrapWithLoading, notifySuccess} = useNotify();
|
|
||||||
const {axios} = useAxios();
|
|
||||||
|
|
||||||
const doDelete = (url) => {
|
const doDelete = (url) => confirmAndDelete(
|
||||||
confirmDelete({
|
url,
|
||||||
title: $gettext('Delete HLS Stream?'),
|
$gettext('Delete HLS Stream?'),
|
||||||
}).then((result) => {
|
() => {
|
||||||
if (result.value) {
|
needsRestart();
|
||||||
wrapWithLoading(
|
relist();
|
||||||
axios.delete(url)
|
}
|
||||||
).then((resp) => {
|
);
|
||||||
notifySuccess(resp.data.message);
|
|
||||||
needsRestart();
|
|
||||||
relist();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -12,65 +12,76 @@
|
||||||
content-class="mt-3"
|
content-class="mt-3"
|
||||||
pills
|
pills
|
||||||
>
|
>
|
||||||
<form-basic-info :form="v$" />
|
<form-basic-info :form="v$"/>
|
||||||
</b-tabs>
|
</b-tabs>
|
||||||
</modal-form>
|
</modal-form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import {required} from '@vuelidate/validators';
|
import {required} from '@vuelidate/validators';
|
||||||
import BaseEditModal from '~/components/Common/BaseEditModal';
|
|
||||||
import FormBasicInfo from './Form/BasicInfo';
|
import FormBasicInfo from './Form/BasicInfo';
|
||||||
import mergeExisting from "~/functions/mergeExisting";
|
import {baseEditModalProps, useBaseEditModal} from "~/functions/useBaseEditModal";
|
||||||
import {useVuelidateOnForm} from "~/functions/useVuelidateOnForm";
|
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 {
|
const emit = defineEmits(['relist', 'needs-restart']);
|
||||||
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
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
const $modal = ref(); // Template Ref
|
||||||
form,
|
|
||||||
resetForm,
|
const {notifySuccess} = useNotify();
|
||||||
v$
|
|
||||||
}
|
const {
|
||||||
|
loading,
|
||||||
|
error,
|
||||||
|
isEditMode,
|
||||||
|
form,
|
||||||
|
v$,
|
||||||
|
clearContents,
|
||||||
|
create,
|
||||||
|
edit,
|
||||||
|
doSubmit,
|
||||||
|
close
|
||||||
|
} = useBaseEditModal(
|
||||||
|
props,
|
||||||
|
emit,
|
||||||
|
$modal,
|
||||||
|
{
|
||||||
|
name: {required},
|
||||||
|
format: {required},
|
||||||
|
bitrate: {required}
|
||||||
},
|
},
|
||||||
computed: {
|
{
|
||||||
langTitle() {
|
name: null,
|
||||||
return this.isEditMode
|
format: 'aac',
|
||||||
? this.$gettext('Edit HLS Stream')
|
bitrate: 128
|
||||||
: this.$gettext('Add HLS Stream');
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
methods: {
|
{
|
||||||
populateForm(d) {
|
onSubmitSuccess: () => {
|
||||||
this.record = d;
|
notifySuccess();
|
||||||
this.form = mergeExisting(this.form, d);
|
emit('relist');
|
||||||
},
|
emit('needs-restart');
|
||||||
onSubmitSuccess() {
|
close();
|
||||||
this.$notifySuccess();
|
|
||||||
|
|
||||||
this.$emit('needs-restart');
|
|
||||||
this.$emit('relist');
|
|
||||||
|
|
||||||
this.close();
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
};
|
);
|
||||||
|
|
||||||
|
const {$gettext} = useTranslate();
|
||||||
|
|
||||||
|
const langTitle = computed(() => {
|
||||||
|
return isEditMode.value
|
||||||
|
? $gettext('Edit HLS Stream')
|
||||||
|
: $gettext('Add HLS Stream');
|
||||||
|
});
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
create,
|
||||||
|
edit,
|
||||||
|
close
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -69,10 +69,6 @@ const props = defineProps({
|
||||||
form: {
|
form: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
},
|
|
||||||
stationFrontendType: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<b-modal
|
<b-modal
|
||||||
id="move_file"
|
id="move_file"
|
||||||
ref="modal"
|
ref="$modal"
|
||||||
size="xl"
|
size="xl"
|
||||||
centered
|
centered
|
||||||
:title="langHeader"
|
:title="langHeader"
|
||||||
|
@ -12,7 +12,7 @@
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="primary"
|
variant="primary"
|
||||||
:disabled="dirHistory.length === 0"
|
:disabled="dirHistory.length === 0"
|
||||||
@click="pageBack"
|
@click.prevent="pageBack"
|
||||||
>
|
>
|
||||||
<icon icon="chevron_left" />
|
<icon icon="chevron_left" />
|
||||||
{{ $gettext('Back') }}
|
{{ $gettext('Back') }}
|
||||||
|
@ -31,7 +31,7 @@
|
||||||
<b-col md="12">
|
<b-col md="12">
|
||||||
<data-table
|
<data-table
|
||||||
id="station_media"
|
id="station_media"
|
||||||
ref="datatable"
|
ref="$datatable"
|
||||||
:show-toolbar="false"
|
:show-toolbar="false"
|
||||||
:selectable="false"
|
:selectable="false"
|
||||||
:fields="fields"
|
:fields="fields"
|
||||||
|
@ -71,108 +71,113 @@
|
||||||
</template>
|
</template>
|
||||||
</b-modal>
|
</b-modal>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
|
||||||
|
<script setup>
|
||||||
import DataTable from '~/components/Common/DataTable.vue';
|
import DataTable from '~/components/Common/DataTable.vue';
|
||||||
import {forEach} from 'lodash';
|
import {forEach} from 'lodash';
|
||||||
import Icon from '~/components/Common/Icon';
|
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 */
|
const props = defineProps({
|
||||||
|
selectedItems: {
|
||||||
export default {
|
type: Object,
|
||||||
name: 'MoveFilesModal',
|
required: true
|
||||||
components: {Icon, DataTable},
|
|
||||||
props: {
|
|
||||||
selectedItems: {
|
|
||||||
type: Object,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
currentDirectory: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
batchUrl: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
listDirectoriesUrl: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
emits: ['relist'],
|
currentDirectory: {
|
||||||
data() {
|
type: String,
|
||||||
return {
|
required: true
|
||||||
destinationDirectory: '',
|
|
||||||
dirHistory: [],
|
|
||||||
fields: [
|
|
||||||
{key: 'directory', label: this.$gettext('Directory'), sortable: false}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
computed: {
|
batchUrl: {
|
||||||
langHeader () {
|
type: String,
|
||||||
return this.$gettext(
|
required: true
|
||||||
'Move %{ num } File(s) to',
|
|
||||||
{num: this.selectedItems.all.length}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
methods: {
|
listDirectoriesUrl: {
|
||||||
close () {
|
type: String,
|
||||||
this.dirHistory = [];
|
required: true
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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>
|
</script>
|
||||||
|
|
|
@ -90,10 +90,10 @@ import InfoCard from '~/components/Common/InfoCard';
|
||||||
import {mayNeedRestartProps, useMayNeedRestart} from "~/functions/useMayNeedRestart";
|
import {mayNeedRestartProps, useMayNeedRestart} from "~/functions/useMayNeedRestart";
|
||||||
import {useTranslate} from "~/vendor/gettext";
|
import {useTranslate} from "~/vendor/gettext";
|
||||||
import {ref} from "vue";
|
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 showFormatAndBitrate from "~/functions/showFormatAndBitrate";
|
||||||
|
import useHasDatatable from "~/functions/useHasDatatable";
|
||||||
|
import useHasEditModal from "~/functions/useHasEditModal";
|
||||||
|
import confirmAndDelete from "~/functions/confirmAndDelete";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
...mayNeedRestartProps,
|
...mayNeedRestartProps,
|
||||||
|
@ -124,42 +124,20 @@ const fields = [
|
||||||
];
|
];
|
||||||
|
|
||||||
const $dataTable = ref(); // DataTable
|
const $dataTable = ref(); // DataTable
|
||||||
|
const {relist} = useHasDatatable($dataTable);
|
||||||
const relist = () => {
|
|
||||||
$dataTable.value.refresh();
|
|
||||||
};
|
|
||||||
|
|
||||||
const $editModal = ref(); // EditModal
|
const $editModal = ref(); // EditModal
|
||||||
|
const {doCreate, doEdit} = useHasEditModal($editModal);
|
||||||
const doCreate = () => {
|
|
||||||
$editModal.value.create();
|
|
||||||
};
|
|
||||||
|
|
||||||
const doEdit = (url) => {
|
|
||||||
$editModal.value.edit(url);
|
|
||||||
};
|
|
||||||
|
|
||||||
const {needsRestart, mayNeedRestart} = useMayNeedRestart(props.restartStatusUrl);
|
const {needsRestart, mayNeedRestart} = useMayNeedRestart(props.restartStatusUrl);
|
||||||
|
|
||||||
const {confirmDelete} = useSweetAlert();
|
const doDelete = (url) => confirmAndDelete(
|
||||||
const {wrapWithLoading, notifySuccess} = useNotify();
|
url,
|
||||||
const {axios} = useAxios();
|
$gettext('Delete Mount Point?'),
|
||||||
|
() => {
|
||||||
const doDelete = (url) => {
|
needsRestart();
|
||||||
confirmDelete({
|
relist();
|
||||||
title: $gettext('Delete Mount Point?'),
|
}
|
||||||
}).then((result) => {
|
);
|
||||||
if (result.value) {
|
|
||||||
wrapWithLoading(
|
|
||||||
axios.delete(url)
|
|
||||||
).then((resp) => {
|
|
||||||
notifySuccess(resp.data.message);
|
|
||||||
needsRestart();
|
|
||||||
relist();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
|
|
||||||
<data-table
|
<data-table
|
||||||
id="station_playlists"
|
id="station_playlists"
|
||||||
ref="datatable"
|
ref="$datatable"
|
||||||
paginated
|
paginated
|
||||||
:fields="fields"
|
:fields="fields"
|
||||||
:responsive="false"
|
:responsive="false"
|
||||||
|
@ -185,7 +185,7 @@
|
||||||
no-body
|
no-body
|
||||||
>
|
>
|
||||||
<schedule
|
<schedule
|
||||||
ref="schedule"
|
ref="$schedule"
|
||||||
:schedule-url="scheduleUrl"
|
:schedule-url="scheduleUrl"
|
||||||
:station-time-zone="stationTimeZone"
|
:station-time-zone="stationTimeZone"
|
||||||
@click="doCalendarClick"
|
@click="doCalendarClick"
|
||||||
|
@ -195,28 +195,28 @@
|
||||||
</b-card>
|
</b-card>
|
||||||
|
|
||||||
<edit-modal
|
<edit-modal
|
||||||
ref="editModal"
|
ref="$editModal"
|
||||||
:create-url="listUrl"
|
:create-url="listUrl"
|
||||||
:station-time-zone="stationTimeZone"
|
:station-time-zone="stationTimeZone"
|
||||||
:enable-advanced-features="enableAdvancedFeatures"
|
:enable-advanced-features="enableAdvancedFeatures"
|
||||||
@relist="relist"
|
@relist="relist"
|
||||||
@needs-restart="mayNeedRestart"
|
@needs-restart="mayNeedRestart"
|
||||||
/>
|
/>
|
||||||
<reorder-modal ref="reorderModal" />
|
<reorder-modal ref="$reorderModal" />
|
||||||
<queue-modal ref="queueModal" />
|
<queue-modal ref="$queueModal" />
|
||||||
<reorder-modal ref="reorderModal" />
|
<reorder-modal ref="$reorderModal" />
|
||||||
<import-modal
|
<import-modal
|
||||||
ref="importModal"
|
ref="$importModal"
|
||||||
@relist="relist"
|
@relist="relist"
|
||||||
/>
|
/>
|
||||||
<clone-modal
|
<clone-modal
|
||||||
ref="cloneModal"
|
ref="$cloneModal"
|
||||||
@relist="relist"
|
@relist="relist"
|
||||||
@needs-restart="mayNeedRestart"
|
@needs-restart="mayNeedRestart"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import DataTable from '~/components/Common/DataTable';
|
import DataTable from '~/components/Common/DataTable';
|
||||||
import Schedule from '~/components/Common/ScheduleView';
|
import Schedule from '~/components/Common/ScheduleView';
|
||||||
import EditModal from './Playlists/EditModal';
|
import EditModal from './Playlists/EditModal';
|
||||||
|
@ -225,181 +225,182 @@ import ImportModal from './Playlists/ImportModal';
|
||||||
import QueueModal from './Playlists/QueueModal';
|
import QueueModal from './Playlists/QueueModal';
|
||||||
import Icon from '~/components/Common/Icon';
|
import Icon from '~/components/Common/Icon';
|
||||||
import CloneModal from './Playlists/CloneModal';
|
import CloneModal from './Playlists/CloneModal';
|
||||||
import {DateTime} from 'luxon';
|
|
||||||
import humanizeDuration from 'humanize-duration';
|
import humanizeDuration from 'humanize-duration';
|
||||||
import {useAzuraCast} from "~/vendor/azuracast";
|
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 */
|
const props = defineProps({
|
||||||
|
...mayNeedRestartProps,
|
||||||
export default {
|
listUrl: {
|
||||||
name: 'StationPlaylists',
|
type: String,
|
||||||
components: {CloneModal, Icon, QueueModal, ImportModal, ReorderModal, EditModal, Schedule, DataTable},
|
required: true
|
||||||
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
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
data () {
|
scheduleUrl: {
|
||||||
return {
|
type: String,
|
||||||
fields: [
|
required: true
|
||||||
{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'}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
methods: {
|
filesUrl: {
|
||||||
langToggleButton (record) {
|
type: String,
|
||||||
return (record.is_enabled)
|
required: true
|
||||||
? this.$gettext('Disable')
|
},
|
||||||
: this.$gettext('Enable');
|
stationTimeZone: {
|
||||||
},
|
type: String,
|
||||||
formatTime (time) {
|
required: true
|
||||||
const {timeConfig} = useAzuraCast();
|
},
|
||||||
|
useManualAutoDj: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
enableAdvancedFeatures: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return DateTime.fromSeconds(time).setZone(this.stationTimeZone).toLocaleString(
|
const {$gettext} = useTranslate();
|
||||||
{...DateTime.DATETIME_MED, ...timeConfig}
|
|
||||||
|
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, {
|
case 'once_per_x_minutes':
|
||||||
round: true,
|
return $gettext(
|
||||||
language: localeShort,
|
'Once per %{minutes} Minutes',
|
||||||
fallbacks: ['en']
|
{minutes: record.play_per_minutes}
|
||||||
});
|
);
|
||||||
},
|
|
||||||
formatType (record) {
|
|
||||||
if (!record.is_enabled) {
|
|
||||||
return this.$gettext('Disabled');
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (record.type) {
|
case 'once_per_hour':
|
||||||
case 'default':
|
return $gettext(
|
||||||
return this.$gettext('General Rotation') + '<br>' + this.$gettext('Weight') + ': ' + record.weight;
|
'Once per Hour (at %{minute})',
|
||||||
|
{minute: record.play_per_hour_minute}
|
||||||
|
);
|
||||||
|
|
||||||
case 'once_per_x_songs':
|
default:
|
||||||
return this.$gettext(
|
return $gettext('Custom');
|
||||||
'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"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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>
|
</script>
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
</div>
|
</div>
|
||||||
<data-table
|
<data-table
|
||||||
id="station_queue"
|
id="station_queue"
|
||||||
ref="datatable"
|
ref="$datatable"
|
||||||
:fields="fields"
|
:fields="fields"
|
||||||
:api-url="listUrl"
|
:api-url="listUrl"
|
||||||
>
|
>
|
||||||
|
@ -67,91 +67,87 @@
|
||||||
</data-table>
|
</data-table>
|
||||||
</b-card>
|
</b-card>
|
||||||
|
|
||||||
<queue-logs-modal ref="logs_modal" />
|
<queue-logs-modal ref="$logsModal" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import DataTable from '../Common/DataTable';
|
import DataTable from '../Common/DataTable';
|
||||||
import QueueLogsModal from './Queue/LogsModal';
|
import QueueLogsModal from './Queue/LogsModal';
|
||||||
import Icon from "~/components/Common/Icon";
|
import Icon from "~/components/Common/Icon";
|
||||||
import {DateTime} from 'luxon';
|
import {DateTime} from 'luxon';
|
||||||
import {useAzuraCast} from "~/vendor/azuracast";
|
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 */
|
const props = defineProps({
|
||||||
|
listUrl: {
|
||||||
export default {
|
type: String,
|
||||||
name: 'StationQueue',
|
required: true
|
||||||
components: {QueueLogsModal, DataTable, Icon},
|
|
||||||
props: {
|
|
||||||
listUrl: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
clearUrl: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
stationTimeZone: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
data() {
|
clearUrl: {
|
||||||
return {
|
type: String,
|
||||||
fields: [
|
required: true
|
||||||
{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}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
methods: {
|
stationTimeZone: {
|
||||||
formatTime(time) {
|
type: String,
|
||||||
const {timeConfig} = useAzuraCast();
|
required: true
|
||||||
|
|
||||||
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();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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>
|
</script>
|
||||||
|
|
|
@ -89,10 +89,10 @@ import '~/vendor/sweetalert';
|
||||||
import {mayNeedRestartProps, useMayNeedRestart} from "~/functions/useMayNeedRestart";
|
import {mayNeedRestartProps, useMayNeedRestart} from "~/functions/useMayNeedRestart";
|
||||||
import {useTranslate} from "~/vendor/gettext";
|
import {useTranslate} from "~/vendor/gettext";
|
||||||
import {ref} from "vue";
|
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 showFormatAndBitrate from "~/functions/showFormatAndBitrate";
|
||||||
|
import useHasDatatable from "~/functions/useHasDatatable";
|
||||||
|
import useHasEditModal from "~/functions/useHasEditModal";
|
||||||
|
import confirmAndDelete from "~/functions/confirmAndDelete";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
...mayNeedRestartProps,
|
...mayNeedRestartProps,
|
||||||
|
@ -111,40 +111,19 @@ const fields = [
|
||||||
];
|
];
|
||||||
|
|
||||||
const $dataTable = ref(); // DataTable
|
const $dataTable = ref(); // DataTable
|
||||||
|
const {relist} = useHasDatatable($dataTable);
|
||||||
const relist = () => {
|
|
||||||
$dataTable.value?.refresh();
|
|
||||||
};
|
|
||||||
|
|
||||||
const $editModal = ref(); // EditModal
|
const $editModal = ref(); // EditModal
|
||||||
|
const {doCreate, doEdit} = useHasEditModal($editModal);
|
||||||
const doCreate = () => {
|
|
||||||
$editModal.value?.create();
|
|
||||||
};
|
|
||||||
|
|
||||||
const doEdit = (url) => {
|
|
||||||
$editModal.value?.edit(url);
|
|
||||||
};
|
|
||||||
|
|
||||||
const {mayNeedRestart, needsRestart} = useMayNeedRestart(props.restartStatusUrl);
|
const {mayNeedRestart, needsRestart} = useMayNeedRestart(props.restartStatusUrl);
|
||||||
|
|
||||||
const {confirmDelete} = useSweetAlert();
|
const doDelete = (url) => confirmAndDelete(
|
||||||
const {wrapWithLoading, notifySuccess} = useNotify();
|
url,
|
||||||
const {axios} = useAxios();
|
$gettext('Delete Remote Relay?'),
|
||||||
|
() => {
|
||||||
const doDelete = (url) => {
|
needsRestart();
|
||||||
confirmDelete({
|
relist();
|
||||||
title: $gettext('Delete Remote Relay?'),
|
}
|
||||||
}).then((result) => {
|
);
|
||||||
if (result.value) {
|
|
||||||
wrapWithLoading(
|
|
||||||
axios.delete(url)
|
|
||||||
).then((resp) => {
|
|
||||||
notifySuccess(resp.data.message);
|
|
||||||
needsRestart();
|
|
||||||
relist();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
|
|
||||||
<data-table
|
<data-table
|
||||||
id="station_remotes"
|
id="station_remotes"
|
||||||
ref="datatable"
|
ref="$datatable"
|
||||||
:show-toolbar="false"
|
:show-toolbar="false"
|
||||||
:fields="fields"
|
:fields="fields"
|
||||||
:api-url="listUrl"
|
:api-url="listUrl"
|
||||||
|
@ -75,65 +75,50 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<sftp-users-edit-modal
|
<sftp-users-edit-modal
|
||||||
ref="editModal"
|
ref="$editModal"
|
||||||
:create-url="listUrl"
|
:create-url="listUrl"
|
||||||
@relist="relist"
|
@relist="relist"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import DataTable from "~/components/Common/DataTable";
|
import DataTable from "~/components/Common/DataTable";
|
||||||
import SftpUsersEditModal from "./SftpUsers/EditModal";
|
import SftpUsersEditModal from "./SftpUsers/EditModal";
|
||||||
import Icon from "~/components/Common/Icon";
|
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 */
|
const props = defineProps({
|
||||||
|
listUrl: {
|
||||||
export default {
|
type: String,
|
||||||
name: 'SftpUsers',
|
required: true
|
||||||
components: {Icon, SftpUsersEditModal, DataTable},
|
|
||||||
props: {
|
|
||||||
listUrl: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
connectionInfo: {
|
|
||||||
type: Object,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
data() {
|
connectionInfo: {
|
||||||
return {
|
type: Object,
|
||||||
fields: [
|
required: true
|
||||||
{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();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
|
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>
|
</script>
|
||||||
|
|
|
@ -44,7 +44,7 @@
|
||||||
|
|
||||||
<data-table
|
<data-table
|
||||||
id="station_streamers"
|
id="station_streamers"
|
||||||
ref="datatable"
|
ref="$datatable"
|
||||||
:fields="fields"
|
:fields="fields"
|
||||||
:api-url="listUrl"
|
:api-url="listUrl"
|
||||||
>
|
>
|
||||||
|
@ -94,7 +94,7 @@
|
||||||
no-body
|
no-body
|
||||||
>
|
>
|
||||||
<schedule
|
<schedule
|
||||||
ref="schedule"
|
ref="$schedule"
|
||||||
:schedule-url="scheduleUrl"
|
:schedule-url="scheduleUrl"
|
||||||
:station-time-zone="stationTimeZone"
|
:station-time-zone="stationTimeZone"
|
||||||
@click="doCalendarClick"
|
@click="doCalendarClick"
|
||||||
|
@ -108,17 +108,17 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<edit-modal
|
<edit-modal
|
||||||
ref="editModal"
|
ref="$editModal"
|
||||||
:create-url="listUrl"
|
:create-url="listUrl"
|
||||||
:station-time-zone="stationTimeZone"
|
:station-time-zone="stationTimeZone"
|
||||||
:new-art-url="newArtUrl"
|
:new-art-url="newArtUrl"
|
||||||
@relist="relist"
|
@relist="relist"
|
||||||
/>
|
/>
|
||||||
<broadcasts-modal ref="broadcastsModal" />
|
<broadcasts-modal ref="$broadcastsModal" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import DataTable from '~/components/Common/DataTable';
|
import DataTable from '~/components/Common/DataTable';
|
||||||
import EditModal from './Streamers/EditModal';
|
import EditModal from './Streamers/EditModal';
|
||||||
import BroadcastsModal from './Streamers/BroadcastsModal';
|
import BroadcastsModal from './Streamers/BroadcastsModal';
|
||||||
|
@ -126,75 +126,64 @@ import Schedule from '~/components/Common/ScheduleView';
|
||||||
import Icon from '~/components/Common/Icon';
|
import Icon from '~/components/Common/Icon';
|
||||||
import ConnectionInfo from "./Streamers/ConnectionInfo";
|
import ConnectionInfo from "./Streamers/ConnectionInfo";
|
||||||
import AlbumArt from "~/components/Common/AlbumArt";
|
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 */
|
const props = defineProps({
|
||||||
|
listUrl: {
|
||||||
export default {
|
type: String,
|
||||||
name: 'StationStreamers',
|
required: true
|
||||||
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
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
data() {
|
newArtUrl: {
|
||||||
return {
|
type: String,
|
||||||
fields: [
|
required: true
|
||||||
{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'}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
methods: {
|
scheduleUrl: {
|
||||||
relist() {
|
type: String,
|
||||||
this.$refs.datatable.refresh();
|
required: true
|
||||||
},
|
},
|
||||||
doCreate() {
|
stationTimeZone: {
|
||||||
this.$refs.editModal.create();
|
type: String,
|
||||||
},
|
required: true
|
||||||
doCalendarClick(event) {
|
},
|
||||||
this.doEdit(event.extendedProps.edit_url);
|
connectionInfo: {
|
||||||
},
|
type: Object,
|
||||||
doEdit(url) {
|
required: true
|
||||||
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();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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>
|
</script>
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
|
|
||||||
<data-table
|
<data-table
|
||||||
id="station_webhooks"
|
id="station_webhooks"
|
||||||
ref="datatable"
|
ref="$datatable"
|
||||||
:fields="fields"
|
:fields="fields"
|
||||||
:api-url="listUrl"
|
:api-url="listUrl"
|
||||||
>
|
>
|
||||||
|
@ -84,9 +84,9 @@
|
||||||
</data-table>
|
</data-table>
|
||||||
</b-card>
|
</b-card>
|
||||||
|
|
||||||
<streaming-log-modal ref="logModal" />
|
<streaming-log-modal ref="$logModal" />
|
||||||
<edit-modal
|
<edit-modal
|
||||||
ref="editModal"
|
ref="$editModal"
|
||||||
:create-url="listUrl"
|
:create-url="listUrl"
|
||||||
:webhook-types="webhookTypes"
|
:webhook-types="webhookTypes"
|
||||||
:trigger-titles="langTriggerTitles"
|
:trigger-titles="langTriggerTitles"
|
||||||
|
@ -96,125 +96,119 @@
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import DataTable from '~/components/Common/DataTable';
|
import DataTable from '~/components/Common/DataTable';
|
||||||
import EditModal from './Webhooks/EditModal';
|
import EditModal from './Webhooks/EditModal';
|
||||||
import Icon from '~/components/Common/Icon';
|
import Icon from '~/components/Common/Icon';
|
||||||
import InfoCard from "~/components/Common/InfoCard";
|
import InfoCard from "~/components/Common/InfoCard";
|
||||||
import {get, map} from 'lodash';
|
import {get, map} from 'lodash';
|
||||||
import StreamingLogModal from "~/components/Common/StreamingLogModal";
|
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 */
|
const props = defineProps({
|
||||||
|
listUrl: {
|
||||||
export default {
|
type: String,
|
||||||
name: 'StationWebhooks',
|
required: true
|
||||||
components: {StreamingLogModal, InfoCard, Icon, EditModal, DataTable},
|
|
||||||
props: {
|
|
||||||
listUrl: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
nowPlayingUrl: {
|
|
||||||
type: String,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
webhookTypes: {
|
|
||||||
type: Object,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
data() {
|
nowPlayingUrl: {
|
||||||
return {
|
type: String,
|
||||||
fields: [
|
required: true
|
||||||
{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'}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
computed: {
|
webhookTypes: {
|
||||||
langTriggerTitles() {
|
type: Object,
|
||||||
return {
|
required: true
|
||||||
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();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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>
|
</script>
|
||||||
|
|
29
frontend/vue/functions/confirmAndDelete.js
Normal file
29
frontend/vue/functions/confirmAndDelete.js
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
9
frontend/vue/functions/useHasDatatable.js
Normal file
9
frontend/vue/functions/useHasDatatable.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
export default function useHasDatatable($datatableRef) {
|
||||||
|
const relist = () => {
|
||||||
|
return $datatableRef.value?.relist();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
relist
|
||||||
|
};
|
||||||
|
}
|
15
frontend/vue/functions/useHasEditModal.js
Normal file
15
frontend/vue/functions/useHasEditModal.js
Normal file
|
@ -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";
|
import {useAxios} from "~/vendor/axios";
|
||||||
|
|
||||||
export const mayNeedRestartProps = {
|
export const mayNeedRestartProps = {
|
||||||
restartStatusUrl: String
|
restartStatusUrl: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export function useNeedsRestart() {
|
export function useNeedsRestart() {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user