Mroe Composition switches.

This commit is contained in:
Buster Neece 2023-01-06 22:23:32 -06:00
parent 962c0badde
commit 1ef68f64fc
No known key found for this signature in database
GPG Key ID: F1D2E64A0005E80E
7 changed files with 303 additions and 342 deletions

View File

@ -8,7 +8,7 @@
<b-form @submit.prevent="doRename">
<b-wrapped-form-group
id="new_directory_name"
:field="v$.form.newPath"
:field="v$.newPath"
autofocus
>
<template #label>
@ -24,7 +24,7 @@
{{ $gettext('Close') }}
</b-button>
<b-button
:variant="(v$.form.$invalid) ? 'danger' : 'primary'"
:variant="(v$.$invalid) ? 'danger' : 'primary'"
@click="doRename"
>
{{ $gettext('Rename') }}

View File

@ -3,7 +3,7 @@
id="clone_modal"
ref="$modal"
:title="$gettext('Duplicate Playlist')"
:disable-save-button="v$.form.$invalid"
:disable-save-button="v$.$invalid"
@submit="doSubmit"
@hidden="clearContents"
>
@ -11,7 +11,7 @@
<b-wrapped-form-group
id="form_edit_name"
class="col-md-12"
:field="v$.form.name"
:field="v$.name"
>
<template #label>
{{ $gettext('New Playlist Name') }}
@ -21,7 +21,7 @@
<b-wrapped-form-group
id="form_edit_clone"
class="col-md-12"
:field="v$.form.clone"
:field="v$.clone"
>
<template #label>
{{ $gettext('Customize Copy') }}

View File

@ -86,9 +86,6 @@
:responsive="false"
:items="listeners"
>
<template #cell(ip)="row">
{{ row.item.ip }}
</template>
<template #cell(time)="row">
{{ formatTime(row.item.connected_time) }}
</template>
@ -148,117 +145,109 @@
</div>
</template>
<script>
<script setup>
import StationReportsListenersMap from "./Listeners/Map";
import Icon from "~/components/Common/Icon";
import formatTime from "~/functions/formatTime";
import DataTable from "~/components/Common/DataTable";
import DateRangeDropdown from "~/components/Common/DateRangeDropdown";
import {DateTime} from 'luxon';
import {computed, onMounted, ref, shallowRef} from "vue";
import {useTranslate} from "~/vendor/gettext";
import {useNotify} from "~/vendor/bootstrapVue";
import {useAxios} from "~/vendor/axios";
/* TODO Options API */
const props = defineProps({
apiUrl: {
type: String,
required: true
},
attribution: {
type: String,
required: true
},
stationTimeZone: {
type: String,
required: true
},
});
export default {
name: 'StationReportsListeners',
components: {DateRangeDropdown, DataTable, StationReportsListenersMap, Icon},
props: {
apiUrl: {
type: String,
required: true
},
attribution: {
type: String,
required: true
},
stationTimeZone: {
type: String,
required: true
},
},
data() {
const nowTz = DateTime.now().setZone(this.stationTimeZone);
const isLive = ref(true);
const listeners = shallowRef([]);
return {
isLive: true,
listeners: [],
dateRange: {
startDate: nowTz.minus({days: 1}).toJSDate(),
endDate: nowTz.toJSDate()
},
fields: [
{key: 'ip', label: this.$gettext('IP'), sortable: false},
{key: 'time', label: this.$gettext('Time'), sortable: false},
{key: 'time_sec', label: this.$gettext('Time (sec)'), sortable: false},
{key: 'user_agent', isRowHeader: true, label: this.$gettext('User Agent'), sortable: false},
{key: 'stream', label: this.$gettext('Stream'), sortable: false},
{key: 'location', label: this.$gettext('Location'), sortable: false}
]
};
},
computed: {
nowTz() {
return DateTime.now().setZone(this.stationTimeZone);
},
minDate() {
return this.nowTz.minus({years: 5}).toJSDate();
},
maxDate() {
return this.nowTz.plus({days: 5}).toJSDate();
},
exportUrl() {
let exportUrl = new URL(this.apiUrl, document.location);
let exportUrlParams = exportUrl.searchParams;
exportUrlParams.set('format', 'csv');
const nowTz = DateTime.now().setZone(props.stationTimeZone);
if (!this.isLive) {
exportUrlParams.set('start', DateTime.fromJSDate(this.dateRange.startDate).toISO());
exportUrlParams.set('end', DateTime.fromJSDate(this.dateRange.endDate).toISO());
}
const minDate = nowTz.minus({years: 5}).toJSDate();
const maxDate = nowTz.plus({days: 5}).toJSDate();
return exportUrl.toString();
},
totalListenerHours() {
let tlh_seconds = 0;
this.listeners.forEach(function (listener) {
tlh_seconds += listener.connected_time;
});
const dateRange = ref({
startDate: nowTz.minus({days: 1}).toJSDate(),
endDate: nowTz.toJSDate()
});
let tlh_hours = tlh_seconds / 3600;
return Math.round((tlh_hours + 0.00001) * 100) / 100;
}
},
mounted() {
this.updateListeners();
},
methods: {
setIsLive(newValue) {
this.isLive = newValue;
this.updateListeners();
},
formatTime(time) {
return formatTime(time);
},
updateListeners() {
let params = {};
if (!this.isLive) {
params.start = DateTime.fromJSDate(this.dateRange.startDate).toISO();
params.end = DateTime.fromJSDate(this.dateRange.endDate).toISO();
}
const {$gettext} = useTranslate();
this.$wrapWithLoading(
this.axios.get(this.apiUrl, {params: params})
).then((resp) => {
this.listeners = resp.data;
const fields = [
{key: 'ip', label: $gettext('IP'), sortable: false},
{key: 'time', label: $gettext('Time'), sortable: false},
{key: 'time_sec', label: $gettext('Time (sec)'), sortable: false},
{key: 'user_agent', isRowHeader: true, label: $gettext('User Agent'), sortable: false},
{key: 'stream', label: $gettext('Stream'), sortable: false},
{key: 'location', label: $gettext('Location'), sortable: false}
];
if (this.isLive) {
setTimeout(this.updateListeners, (!document.hidden) ? 15000 : 30000);
}
}).catch((error) => {
if (this.isLive && (!error.response || error.response.data.code !== 403)) {
setTimeout(this.updateListeners, (!document.hidden) ? 30000 : 120000);
}
});
}
const exportUrl = computed(() => {
let exportUrl = new URL(props.apiUrl, document.location);
let exportUrlParams = exportUrl.searchParams;
exportUrlParams.set('format', 'csv');
if (!isLive.value) {
exportUrlParams.set('start', DateTime.fromJSDate(dateRange.value.startDate).toISO());
exportUrlParams.set('end', DateTime.fromJSDate(dateRange.value.endDate).toISO());
}
return exportUrl.toString();
});
const totalListenerHours = computed(() => {
let tlh_seconds = 0;
listeners.value.forEach(function (listener) {
tlh_seconds += listener.connected_time;
});
let tlh_hours = tlh_seconds / 3600;
return Math.round((tlh_hours + 0.00001) * 100) / 100;
});
const {wrapWithLoading} = useNotify();
const {axios} = useAxios();
const updateListeners = () => {
let params = {};
if (!isLive.value) {
params.start = DateTime.fromJSDate(dateRange.value.startDate).toISO();
params.end = DateTime.fromJSDate(dateRange.value.endDate).toISO();
}
wrapWithLoading(
axios.get(props.apiUrl, {params: params})
).then((resp) => {
listeners.value = resp.data;
if (isLive.value) {
setTimeout(updateListeners, (!document.hidden) ? 15000 : 30000);
}
}
}).catch((error) => {
if (isLive.value && (!error.response || error.response.data.code !== 403)) {
setTimeout(updateListeners, (!document.hidden) ? 30000 : 120000);
}
});
};
onMounted(updateListeners);
const setIsLive = (newValue) => {
isLive.value = newValue;
updateListeners();
};
</script>

View File

@ -104,7 +104,7 @@
</section>
</template>
<script>
<script setup>
import {DateTime} from "luxon";
import DateRangeDropdown from "~/components/Common/DateRangeDropdown";
import ListenersByTimePeriodTab from "./Overview/ListenersByTimePeriodTab";
@ -114,67 +114,51 @@ import CountriesTab from "./Overview/CountriesTab";
import StreamsTab from "./Overview/StreamsTab";
import ClientsTab from "./Overview/ClientsTab";
import ListeningTimeTab from "~/components/Stations/Reports/Overview/ListeningTimeTab";
import {ref} from "vue";
/* TODO Options API */
const props = defineProps({
stationTimeZone: {
type: String,
required: true
},
showFullAnalytics: {
type: Boolean,
required: true
},
listenersByTimePeriodUrl: {
type: String,
required: true
},
bestAndWorstUrl: {
type: String,
required: true
},
byStreamUrl: {
type: String,
required: true
},
byClientUrl: {
type: String,
required: true
},
byBrowserUrl: {
type: String,
required: true
},
byCountryUrl: {
type: String,
required: true
},
listeningTimeUrl: {
type: String,
required: true
}
});
export default {
components: {
ListeningTimeTab,
ClientsTab,
StreamsTab,
CountriesTab,
BrowsersTab,
BestAndWorstTab,
ListenersByTimePeriodTab,
DateRangeDropdown
},
props: {
stationTimeZone: {
type: String,
required: true
},
showFullAnalytics: {
type: Boolean,
required: true
},
listenersByTimePeriodUrl: {
type: String,
required: true
},
bestAndWorstUrl: {
type: String,
required: true
},
byStreamUrl: {
type: String,
required: true
},
byClientUrl: {
type: String,
required: true
},
byBrowserUrl: {
type: String,
required: true
},
byCountryUrl: {
type: String,
required: true
},
listeningTimeUrl: {
type: String,
required: true
}
},
data() {
let nowTz = DateTime.now().setZone(this.stationTimeZone);
let nowTz = DateTime.now().setZone(props.stationTimeZone);
return {
dateRange: {
startDate: nowTz.minus({days: 13}).toJSDate(),
endDate: nowTz.toJSDate(),
},
};
},
};
const dateRange = ref({
startDate: nowTz.minus({days: 13}).toJSDate(),
endDate: nowTz.toJSDate(),
});
</script>

View File

@ -77,104 +77,108 @@
</b-card>
</template>
<script>
<script setup>
import DataTable from '~/components/Common/DataTable';
import Icon from "~/components/Common/Icon";
import {DateTime} from 'luxon';
import {useAzuraCast} from "~/vendor/azuracast";
import {computed, ref} from "vue";
import {useTranslate} from "~/vendor/gettext";
import {useSweetAlert} from "~/vendor/sweetalert";
import {useNotify} from "~/vendor/bootstrapVue";
/* TODO Options API */
export default {
name: 'StationRequests',
components: {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 {
activeType: 'pending',
fields: [
{key: 'timestamp', label: this.$gettext('Date Requested'), sortable: false},
{key: 'played_at', label: this.$gettext('Date Played'), sortable: false},
{key: 'song_title', isRowHeader: true, label: this.$gettext('Song Title'), sortable: false},
{key: 'ip', label: this.$gettext('Requester IP'), sortable: false},
{key: 'actions', label: this.$gettext('Actions'), sortable: false}
]
}
clearUrl: {
type: String,
required: true
},
computed: {
tabs() {
return [
{
type: 'pending',
title: this.$gettext('Pending Requests')
},
{
type: 'history',
title: this.$gettext('Request History')
}
]
},
listUrlForType() {
return this.listUrl + '?type=' + this.activeType;
}
},
methods: {
setType(type) {
this.activeType = type;
this.relist();
},
relist() {
this.$refs.datatable.refresh();
},
formatTime(time) {
const {timeConfig} = useAzuraCast();
return DateTime.fromSeconds(time).setZone(this.stationTimeZone).toLocaleString(
{...DateTime.DATETIME_MED, ...timeConfig}
);
},
doDelete(url) {
this.$confirmDelete({
title: this.$gettext('Delete Request?'),
}).then((result) => {
if (result.value) {
this.$wrapWithLoading(
this.axios.delete(url)
).then((resp) => {
this.$notifySuccess(resp.data.message);
this.relist();
});
}
});
},
doClear() {
this.$confirmDelete({
title: this.$gettext('Clear All Pending Requests?'),
confirmButtonText: this.$gettext('Clear'),
}).then((result) => {
if (result.value) {
this.$wrapWithLoading(
this.axios.post(this.clearUrl)
).then((resp) => {
this.$notifySuccess(resp.data.message);
this.relist();
});
}
});
}
stationTimeZone: {
type: String,
required: true
}
});
const activeType = ref('pending');
const {$gettext} = useTranslate();
const fields = [
{key: 'timestamp', label: $gettext('Date Requested'), sortable: false},
{key: 'played_at', label: $gettext('Date Played'), sortable: false},
{key: 'song_title', isRowHeader: true, label: $gettext('Song Title'), sortable: false},
{key: 'ip', label: $gettext('Requester IP'), sortable: false},
{key: 'actions', label: $gettext('Actions'), sortable: false}
];
const tabs = [
{
type: 'pending',
title: $gettext('Pending Requests')
},
{
type: 'history',
title: $gettext('Request History')
}
];
const listUrlForType = computed(() => {
return props.listUrl + '?type=' + activeType.value;
});
const $datatable = ref(); // Template Ref
const relist = () => {
$datatable.value.refresh();
};
const setType = (type) => {
activeType.value = type;
relist();
};
const formatTime = (time) => {
const {timeConfig} = useAzuraCast();
return DateTime.fromSeconds(time).setZone(props.stationTimeZone).toLocaleString(
{...DateTime.DATETIME_MED, ...timeConfig}
);
};
const {confirmDelete} = useSweetAlert();
const {wrapWithLoading, notifySuccess} = useNotify();
const doDelete = (url) => {
confirmDelete({
title: $gettext('Delete Request?'),
}).then((result) => {
if (result.value) {
wrapWithLoading(
axios.delete(url)
).then((resp) => {
notifySuccess(resp.data.message);
relist();
});
}
});
};
const doClear = () => {
confirmDelete({
title: $gettext('Clear All Pending Requests?'),
confirmButtonText: $gettext('Clear'),
}).then((result) => {
if (result.value) {
wrapWithLoading(
axios.post(props.clearUrl)
).then((resp) => {
notifySuccess(resp.data.message);
relist();
});
}
});
};
</script>

View File

@ -17,71 +17,62 @@
</b-wrapped-form-group>
</template>
<script>
<script setup>
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
import {useTranslate} from "~/vendor/gettext";
/* TODO Options API */
export default {
name: 'CommonRateLimitFields',
components: {BWrappedFormGroup},
props: {
form: {
type: Object,
required: true
}
},
computed: {
langSeconds() {
return this.$gettext('%{ seconds } seconds');
},
langMinutes() {
return this.$gettext('%{ minutes } minutes');
},
rateLimitOptions() {
return [
{
text: this.$gettext('No Limit'),
value: 0,
},
{
text: this.$gettextInterpolate(this.langSeconds, {seconds: 15}),
value: 15,
},
{
text: this.$gettextInterpolate(this.langSeconds, {seconds: 30}),
value: 30,
},
{
text: this.$gettextInterpolate(this.langSeconds, {seconds: 60}),
value: 60,
},
{
text: this.$gettextInterpolate(this.langMinutes, {minutes: 2}),
value: 120,
},
{
text: this.$gettextInterpolate(this.langMinutes, {minutes: 5}),
value: 300,
},
{
text: this.$gettextInterpolate(this.langMinutes, {minutes: 10}),
value: 600,
},
{
text: this.$gettextInterpolate(this.langMinutes, {minutes: 15}),
value: 900,
},
{
text: this.$gettextInterpolate(this.langMinutes, {minutes: 30}),
value: 1800,
},
{
text: this.$gettextInterpolate(this.langMinutes, {minutes: 60}),
value: 3600,
}
];
},
const props = defineProps({
form: {
type: Object,
required: true
}
}
});
const {$gettext, $gettextInterpolate} = useTranslate();
const langSeconds = $gettext('%{ seconds } seconds');
const langMinutes = $gettext('%{ minutes } minutes');
const rateLimitOptions = [
{
text: $gettext('No Limit'),
value: 0,
},
{
text: $gettextInterpolate(langSeconds, {seconds: 15}),
value: 15,
},
{
text: $gettextInterpolate(langSeconds, {seconds: 30}),
value: 30,
},
{
text: this.$gettextInterpolate(this.langSeconds, {seconds: 60}),
value: 60,
},
{
text: $gettextInterpolate(langMinutes, {minutes: 2}),
value: 120,
},
{
text: $gettextInterpolate(langMinutes, {minutes: 5}),
value: 300,
},
{
text: $gettextInterpolate(langMinutes, {minutes: 10}),
value: 600,
},
{
text: $gettextInterpolate(langMinutes, {minutes: 15}),
value: 900,
},
{
text: $gettextInterpolate(langMinutes, {minutes: 30}),
value: 1800,
},
{
text: $gettextInterpolate(langMinutes, {minutes: 60}),
value: 3600,
}
];
</script>

View File

@ -81,30 +81,23 @@
</div>
</template>
<script>
<script setup>
import BWrappedFormGroup from "~/components/Form/BWrappedFormGroup";
import CommonFormattingInfo from "./FormattingInfo";
import {includes} from 'lodash';
/* TODO Options API */
export default {
name: 'CommonSocialPostFields',
components: {CommonFormattingInfo, BWrappedFormGroup},
props: {
form: {
type: Object,
required: true
},
nowPlayingUrl: {
type: String,
required: true
}
const props = defineProps({
form: {
type: Object,
required: true
},
methods: {
hasTrigger(trigger) {
return includes(this.form.triggers.$model, trigger);
}
nowPlayingUrl: {
type: String,
required: true
}
}
});
const hasTrigger = (trigger) => {
return includes(props.form.triggers.$model, trigger);
};
</script>