feat(FE): webstreams and menu
This commit is contained in:
parent
c6b07fa803
commit
438220a664
8 changed files with 481 additions and 0 deletions
150
resources/js/components/content/Webstream.vue
Normal file
150
resources/js/components/content/Webstream.vue
Normal file
|
@ -0,0 +1,150 @@
|
|||
<script setup lang="ts">
|
||||
import {onBeforeMount, reactive, ref, watch} from "vue";
|
||||
import Table from "@partials/Table.vue";
|
||||
import ConfirmDelete from "@partials/dialogs/ConfirmDelete.vue";
|
||||
import {webstream_page} from "@/composables/content/webstream_page.ts";
|
||||
import {baseWebstream, type Webstream} from "@models/webstream.ts";
|
||||
import WebstreamForm from "@partials/WebstreamForm.vue";
|
||||
|
||||
const {items, listData, headers, selected, loading, search, getItems, editItem, deleteItem} = webstream_page()
|
||||
|
||||
const props = defineProps({
|
||||
hideColumns: {
|
||||
type: Array,
|
||||
required: false
|
||||
},
|
||||
isDraggable: {
|
||||
type: Boolean,
|
||||
required: false
|
||||
}
|
||||
});
|
||||
|
||||
const webstreamCreateEditMode = ref(false);
|
||||
let webstreamSelected = ref<Number | null>(null);
|
||||
|
||||
const visibleHeaders = ref(headers)
|
||||
|
||||
const bulk = ref({
|
||||
state: false,
|
||||
items: [] as Webstream[],
|
||||
})
|
||||
|
||||
const dialog = reactive({
|
||||
open: false,
|
||||
type: '',
|
||||
title: '',
|
||||
text: ''
|
||||
})
|
||||
|
||||
const openDialog = (type, title: string = '', text: string = '') => {
|
||||
dialog.open = true
|
||||
dialog.type = type
|
||||
dialog.title = title
|
||||
dialog.text = text
|
||||
}
|
||||
|
||||
const edit = (webstreamSelectedFromUser) => {
|
||||
webstreamSelected.value = webstreamSelectedFromUser.id
|
||||
webstreamCreateEditMode.value = true
|
||||
}
|
||||
|
||||
const create = () => {
|
||||
webstreamSelected.value = null
|
||||
webstreamCreateEditMode.value = true
|
||||
}
|
||||
|
||||
const saveItem = (item) => {
|
||||
const saved = editItem(item)
|
||||
closeDialog()
|
||||
}
|
||||
|
||||
const cancel = (item) => {
|
||||
let deleteMessage = 'Vuoi cancellare il webstream selezionato?'
|
||||
if(bulk.value.state) deleteMessage = 'Vuoi cancellare i webstream selezionati?'
|
||||
bulk.value.items = item
|
||||
webstreamSelected.value = item?.id
|
||||
openDialog(
|
||||
'delete',
|
||||
'Cancella',
|
||||
deleteMessage
|
||||
)
|
||||
}
|
||||
|
||||
const confirmDelete = (confirm) => {
|
||||
if (confirm) {
|
||||
const webstreamId = webstreamSelected.value == 0 ? null : webstreamSelected.value ;
|
||||
deleteItem(webstreamId)
|
||||
}
|
||||
closeDialog()
|
||||
}
|
||||
|
||||
const closeDialog = () => {
|
||||
dialog.open = false
|
||||
resetItemEdited()
|
||||
}
|
||||
|
||||
const updateSearch = (text) => {
|
||||
search.value = text
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
if(props.hideColumns != undefined) {
|
||||
visibleHeaders.value = headers.filter(el => {
|
||||
return !props.hideColumns.includes(el.value)
|
||||
});
|
||||
}
|
||||
})
|
||||
|
||||
const resetItemEdited = () => {
|
||||
webstreamSelected.value = null
|
||||
}
|
||||
|
||||
watch(search, (newValue, oldValue) => {
|
||||
const options = {...listData};
|
||||
getItems(options)
|
||||
})
|
||||
|
||||
const goBack = () => {
|
||||
webstreamCreateEditMode.value = false
|
||||
webstreamSelected.value = null
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<WebstreamForm v-if="webstreamCreateEditMode" :webstreamId="webstreamSelected" @go-back="goBack"/>
|
||||
<Table
|
||||
v-else
|
||||
:headers="visibleHeaders"
|
||||
v-model:selected="selected"
|
||||
v-model:search="search"
|
||||
:list-data="listData"
|
||||
:items="items"
|
||||
:loading="loading"
|
||||
:get-items="getItems"
|
||||
:actions="true"
|
||||
:show-select="true"
|
||||
:is-draggable="isDraggable"
|
||||
@update-table="getItems"
|
||||
@update-search="updateSearch"
|
||||
@delete-item="cancel"
|
||||
@edit-item="edit"
|
||||
>
|
||||
<template v-slot:header-buttons>
|
||||
<v-btn color="primary" @click="create">
|
||||
Crea un nuovo webstream
|
||||
</v-btn>
|
||||
</template>
|
||||
<template v-slot:dialog>
|
||||
<v-dialog v-model="dialog.open">
|
||||
<ConfirmDelete
|
||||
v-if="dialog.type === 'delete'"
|
||||
:title="dialog.title"
|
||||
:text="dialog.text"
|
||||
:bulk="bulk.state"
|
||||
@confirm="confirmDelete"
|
||||
@after-leave="closeDialog"
|
||||
/>
|
||||
</v-dialog>
|
||||
</template>
|
||||
</Table>
|
||||
</template>
|
|
@ -2,6 +2,7 @@
|
|||
import {ref} from "vue";
|
||||
import Archive from "@/components/content/Archive.vue";
|
||||
import Blocks from "@components/content/SmartBlock.vue";
|
||||
import Webstream from "@components/content/Webstream.vue";
|
||||
|
||||
const tab = ref(null)
|
||||
const tabs = [
|
||||
|
@ -13,6 +14,10 @@ const tabs = [
|
|||
id: 'blocks',
|
||||
title: 'Blocchi dinamici',
|
||||
},
|
||||
{
|
||||
id: 'webstream',
|
||||
title: 'Webstream',
|
||||
},
|
||||
]
|
||||
</script>
|
||||
|
||||
|
@ -42,6 +47,11 @@ const tabs = [
|
|||
:is-draggable="true"
|
||||
:hideColumns="['mtime', 'utime', 'actions']"
|
||||
/>
|
||||
<Webstream
|
||||
v-if="tab.id === 'webstream'"
|
||||
:show-select="false"
|
||||
:is-draggable="true"
|
||||
/>
|
||||
</v-tabs-window-item>
|
||||
</v-tabs-window>
|
||||
</template>
|
||||
|
|
|
@ -65,6 +65,22 @@ const rehydratateTracks = () => {
|
|||
track_info.block = track
|
||||
track_info.id = track.id
|
||||
}
|
||||
if (Object.hasOwn(track, 'webstream') && track.webstream !== null) {
|
||||
track_info.type = 'webstream'
|
||||
track_info.title = track.webstream.name
|
||||
track_info.subtitle = track.webstream.description
|
||||
track_info.db_element = track.webstream
|
||||
track_info.webstream = track.webstream
|
||||
track_info.id = track.stream_id
|
||||
}
|
||||
if (!Object.hasOwn(track, 'webstream') && track.type === 'webstream') {
|
||||
track_info.type = 'webstream'
|
||||
track_info.title = track.name
|
||||
track_info.subtitle = track.description
|
||||
track_info.db_element = track
|
||||
track_info.webstream = track
|
||||
track_info.id = track.id
|
||||
}
|
||||
return track_info
|
||||
})
|
||||
}
|
||||
|
@ -102,6 +118,15 @@ const change = (event) => {
|
|||
trackList.value[event.added.newIndex].title = trackList.value[event.added.newIndex].name;
|
||||
trackList.value[event.added.newIndex].subtitle = trackList.value[event.added.newIndex].creator.login;
|
||||
}
|
||||
// webstream
|
||||
if (
|
||||
Object.hasOwn(trackList.value[event.added.newIndex], 'url') &&
|
||||
!Object.hasOwn(trackList.value[event.added.newIndex], 'track_title')
|
||||
) {
|
||||
trackList.value[event.added.newIndex].type = 'webstream';
|
||||
trackList.value[event.added.newIndex].title = trackList.value[event.added.newIndex].name;
|
||||
trackList.value[event.added.newIndex].subtitle = trackList.value[event.added.newIndex].description;
|
||||
}
|
||||
}
|
||||
emit('updateTracks', trackList.value)
|
||||
}
|
||||
|
|
141
resources/js/components/content/partials/WebstreamForm.vue
Normal file
141
resources/js/components/content/partials/WebstreamForm.vue
Normal file
|
@ -0,0 +1,141 @@
|
|||
<script setup lang="ts">
|
||||
import {ref, onMounted, type PropType, reactive} from "vue";
|
||||
import {baseWebstream, createWebstream, getWebstream, updateWebstream, type Webstream} from "@models/webstream.ts";
|
||||
|
||||
// Props and emits
|
||||
const props = defineProps({
|
||||
webstreamId: {
|
||||
type: Number as PropType<number | null>,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
const emits = defineEmits(['go-back']);
|
||||
|
||||
// Data
|
||||
const loading = ref(false);
|
||||
const isFormValid = ref(false);
|
||||
const currentWebstream = ref<Webstream>(baseWebstream());
|
||||
|
||||
// Funcs
|
||||
onMounted(async () => {
|
||||
loading.value = true;
|
||||
if (props.webstreamId === null) {
|
||||
currentWebstream.value = baseWebstream();
|
||||
} else {
|
||||
currentWebstream.value = await getWebstream(props.webstreamId);
|
||||
}
|
||||
loading.value = false;
|
||||
});
|
||||
|
||||
const goBack = () => {
|
||||
emits('go-back');
|
||||
};
|
||||
|
||||
const saveWebstream = async () => {
|
||||
loading.value = true;
|
||||
try {
|
||||
if (currentWebstream.value.id) {
|
||||
await updateWebstream(currentWebstream.value);
|
||||
goBack();
|
||||
return
|
||||
}
|
||||
|
||||
await createWebstream(currentWebstream.value);
|
||||
goBack()
|
||||
return
|
||||
|
||||
} catch (error) {
|
||||
console.error("Error saving webstream:", error);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-card
|
||||
:disabled="loading"
|
||||
:loading="loading"
|
||||
>
|
||||
<template v-slot:loader="{ isActive }">
|
||||
<v-progress-linear
|
||||
:active="isActive"
|
||||
color="deep-purple"
|
||||
height="4"
|
||||
indeterminate
|
||||
></v-progress-linear>
|
||||
</template>
|
||||
|
||||
<v-card-title>
|
||||
<h3>Webstream</h3>
|
||||
</v-card-title>
|
||||
<v-form ref="form" v-model="isFormValid">
|
||||
<v-card-text>
|
||||
<v-row no-gutters>
|
||||
<!-- Name Field -->
|
||||
<v-col cols="12" md="6">
|
||||
<v-card>
|
||||
<v-text-field
|
||||
v-model="currentWebstream.name"
|
||||
label="Nome"
|
||||
density="compact"
|
||||
:rules="[v => !!v || 'Nome è obbligatorio']"
|
||||
required="true"
|
||||
/>
|
||||
</v-card>
|
||||
</v-col>
|
||||
|
||||
<!-- URL Field -->
|
||||
<v-col cols="12" md="6">7
|
||||
<v-card>
|
||||
<v-text-field
|
||||
v-model="currentWebstream.url"
|
||||
label="URL"
|
||||
density="compact"
|
||||
:rules="[v => !!v || 'URL è obbligatorio', v => /^http?:\/\//.test(v) || 'URL deve iniziare con http://']"
|
||||
required="true"
|
||||
/>
|
||||
</v-card>
|
||||
</v-col>
|
||||
|
||||
<!-- Description Field -->
|
||||
<v-col cols="12">
|
||||
<v-card>
|
||||
<v-textarea
|
||||
v-model="currentWebstream.description"
|
||||
label="Descrizione"
|
||||
density="compact"
|
||||
rows="2"
|
||||
:rules="[v => !!v || 'Descrizione è obbligatoria']"
|
||||
required="true"
|
||||
/>
|
||||
</v-card>
|
||||
</v-col>
|
||||
|
||||
<!-- Length Field -->
|
||||
<v-col cols="12">
|
||||
<v-card>
|
||||
<v-textarea
|
||||
v-model="currentWebstream.length"
|
||||
label="Durata"
|
||||
density="compact"
|
||||
rows="2"
|
||||
:rules="[v => !!v || 'Durata è obbligatoria']"
|
||||
required="true"
|
||||
/>
|
||||
</v-card>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions>
|
||||
<v-btn color="accent" @click="goBack">Torna indietro</v-btn>
|
||||
<v-btn color="primary" @click="saveWebstream" :disabled="!isFormValid">
|
||||
{{ currentWebstream.id ? 'Aggiorna' : 'Crea' }}
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-form>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
96
resources/js/composables/content/models/webstream.ts
Normal file
96
resources/js/composables/content/models/webstream.ts
Normal file
|
@ -0,0 +1,96 @@
|
|||
import axios, {type AxiosResponse} from "axios";
|
||||
import {cleanOptions} from "@/helpers/AxiosHelper.ts";
|
||||
|
||||
export interface Webstream {
|
||||
id?: number;
|
||||
name: string;
|
||||
description: string;
|
||||
url: string;
|
||||
length?: string;
|
||||
creator_id?: number;
|
||||
mtime?: Date;
|
||||
utime?: Date;
|
||||
lptime?: Date;
|
||||
mime?: string;
|
||||
}
|
||||
|
||||
export const baseWebstream = (): Webstream => {
|
||||
return {
|
||||
id: null,
|
||||
name: 'test',
|
||||
description: '',
|
||||
url: '',
|
||||
length: '00:00:00',
|
||||
creator_id: null,
|
||||
mtime: null,
|
||||
utime: null,
|
||||
lptime: null,
|
||||
mime: null
|
||||
}
|
||||
}
|
||||
|
||||
export const webstreamTableHeader = [
|
||||
{title: 'Nome', value: 'name'},
|
||||
{title: 'Descrizione', value: 'description'},
|
||||
{title: 'URL', value: 'url'},
|
||||
{title: 'Durata', value: 'length'},
|
||||
{title: 'Azioni', value: 'actions'}
|
||||
]
|
||||
|
||||
export const getWebstreams = async (options: {
|
||||
name?: string | null;
|
||||
description?: string | null;
|
||||
url?: string | null;
|
||||
mime?: string | null;
|
||||
page?: Number | null;
|
||||
per_page?: Number | null;
|
||||
}): Promise<Webstream[]> => {
|
||||
const filteredParams = cleanOptions(options);
|
||||
return await axios.get(`/webstream`, {params: filteredParams})
|
||||
.then((response: AxiosResponse) => {
|
||||
return response.data
|
||||
}).catch((error: Error) => {
|
||||
console.log("Error: " + error);
|
||||
})
|
||||
}
|
||||
|
||||
export const getWebstream = async (id: number): Promise<Webstream> => {
|
||||
return await axios.get(`/webstream/${id}`)
|
||||
.then((response: AxiosResponse) => {
|
||||
return response.data
|
||||
}).catch((error: Error) => {
|
||||
console.log("Error: " + error);
|
||||
throw error;
|
||||
})
|
||||
}
|
||||
|
||||
export const createWebstream = async (webstream: Webstream): Promise<Webstream> => {
|
||||
return await axios.post('/webstream', webstream)
|
||||
.then((response: AxiosResponse) => {
|
||||
return response.data
|
||||
}).catch((error: Error) => {
|
||||
console.log("Error: " + error);
|
||||
throw error;
|
||||
})
|
||||
}
|
||||
|
||||
export const updateWebstream = async (webstream: Webstream): Promise<Webstream> => {
|
||||
return await axios.put(`/webstream/${webstream.id}`, webstream)
|
||||
.then((response: AxiosResponse) => {
|
||||
return response.data
|
||||
}).catch((error: Error) => {
|
||||
console.log("Error: " + error);
|
||||
throw error;
|
||||
})
|
||||
}
|
||||
|
||||
export const deleteWebstream = async (webstreamIds: Number[]) => {
|
||||
try {
|
||||
for (const webstreamId of webstreamIds) {
|
||||
await axios.delete(`/webstream/${webstreamId}`)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error deleting webstream:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
52
resources/js/composables/content/webstream_page.ts
Normal file
52
resources/js/composables/content/webstream_page.ts
Normal file
|
@ -0,0 +1,52 @@
|
|||
import {reactive, ref} from "vue";
|
||||
import {deleteWebstream, getWebstreams, updateWebstream, type Webstream, webstreamTableHeader} from "@models/webstream.ts";
|
||||
|
||||
export function webstream_page() {
|
||||
const items = ref([])
|
||||
const selected = ref([])
|
||||
const loading = ref(false)
|
||||
const search = ref('')
|
||||
const listData = reactive({
|
||||
'itemsPerPage': 5,
|
||||
'first_page': null,
|
||||
'last_page': null,
|
||||
'total_items': 0,
|
||||
'page': 1,
|
||||
})
|
||||
|
||||
const headers = webstreamTableHeader
|
||||
|
||||
const getItems = async (options) => {
|
||||
loading.value = true;
|
||||
return getWebstreams(options).then(webstreamList => {
|
||||
items.value = webstreamList.data
|
||||
loading.value = false;
|
||||
}).catch(error => {
|
||||
console.log("Error: " + error);
|
||||
loading.value = false;
|
||||
})
|
||||
};
|
||||
|
||||
const editItem = (item: Webstream) => {
|
||||
return updateWebstream(item)
|
||||
.then((response) => {
|
||||
console.log(response);
|
||||
return response;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Error updating webstream:", error);
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
|
||||
const deleteItem = async (itemId: number | null = null) => {
|
||||
const webstreamId = itemId ? [itemId] : selected.value.map(item => item.id)
|
||||
await deleteWebstream(webstreamId).then(async () => {
|
||||
await getItems(listData)
|
||||
}).catch(error => {
|
||||
console.error("Error deleting webstream:", error);
|
||||
})
|
||||
};
|
||||
|
||||
return {items, listData, headers, selected, loading, search, getItems, editItem, deleteItem}
|
||||
}
|
|
@ -16,6 +16,7 @@ const tabs = {
|
|||
archive: defineAsyncComponent(() => import('@components/content/Archive.vue')),
|
||||
playlist: defineAsyncComponent(() => import('@components/content/Playlist.vue')),
|
||||
blocks: defineAsyncComponent(() => import('@components/content/SmartBlock.vue')),
|
||||
webstream: defineAsyncComponent(() => import('@components/content/Webstream.vue')),
|
||||
podcast: defineAsyncComponent(() => import('@components/content/Podcast.vue')),
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -30,6 +30,12 @@ const pages = [
|
|||
id: 'podcast',
|
||||
name: 'Podcast',
|
||||
component: '@components/content/Podcast.vue',
|
||||
component: '@components/content/Blocks.vue',
|
||||
},
|
||||
{
|
||||
id: 'webstream',
|
||||
name: 'Webstream',
|
||||
component: '@components/content/Webstream.vue',
|
||||
}
|
||||
];
|
||||
</script>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue