feat: FE show, show instances, show djs

This commit is contained in:
Michael 2025-03-26 10:34:06 +01:00
parent b389f37a1d
commit 5697449f2e
6 changed files with 388 additions and 105 deletions

View file

@ -0,0 +1,124 @@
<script setup lang="ts">
import {reactive, ref, watch} from "vue";
import Table from "@partials/Table.vue";
import ConfirmDelete from "@partials/dialogs/ConfirmDelete.vue";
import {show_page} from "@/composables/content/show/show_page.ts";
import ShowCreateEditForm from "@partials/ShowCreateEditForm.vue";
import {baseShow, type Show} from "@models/show/show";
const {items, listData, headers, selected, loading, search, getItems, editItem, deleteItem} = show_page()
let showSelected = ref(baseShow());
const bulk = ref({
state: false,
items: [] as Show[],
})
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 = (showSelectedFromUser) => {
return showSelected.value = {...showSelectedFromUser}
};
const create = () => {
showSelected.value = baseShow();
showSelected.value.id = 0
}
const saveItem = (item) => {
const saved = editItem(item)
closeDialog()
}
const cancel = (item) => {
let deleteMessage = 'Vuoi cancellare lo show selezionato?'
if(bulk.value.state) deleteMessage = 'Vuoi cancellare gli show selezionati?'
bulk.value.items = item
showSelected.value = item
openDialog(
'delete',
'Cancella',
deleteMessage
)
}
const confirmDelete = (confirm) => {
if (confirm) {
const showIds = bulk.value.items.map(item => item.id)
deleteItem(showIds)
}
closeDialog()
}
const closeDialog = () => {
dialog.open = false
resetItemEdited()
}
const updateSearch = (text) => {
search.value = text
}
const resetItemEdited = () => {
showSelected.value = baseShow()
}
watch(search, (newValue, oldValue) => {
const options = {...listData};
getItems(options)
})
</script>
<template>
<ShowCreateEditForm
v-if="showSelected.id !== null && !dialog.open"
:show="showSelected"
@go-back="resetItemEdited"
/>
<Table
v-else
:headers="headers"
v-model:selected="selected"
v-model:search="search"
:list-data="listData"
:items="items"
:loading="loading"
:get-items="getItems"
:actions="true"
:show-select="true"
@update-table="getItems"
@update-search="updateSearch"
@delete-item="cancel"
@edit-item="edit"
>
<template v-slot:header-buttons>
<v-btn color="primary" @click="create">
Crea una nuova trasmissione
</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>

View file

@ -2,20 +2,10 @@ import type {ShowInstance} from "@models/show/showInstance.ts";
import type {ShowDays} from "@models/show/showDays";
import type {ShowDjs} from "@models/show/showDjs";
import axios, {type AxiosResponse} from "axios";
import type {
AutocompleteFieldConfig,
CheckboxFieldConfig,
CheckBoxConditionalType,
ColorPickerFieldConfig,
FieldDefinition,
FieldDefinitions, FileFieldConfig,
TextareaConfig,
TextFieldConfig
} from "@models/misc/FormInterface.ts";
import {markRaw} from "vue";
import {VCheckbox, VColorPicker, VSelect, VFileInput, VTextarea, VTextField} from "vuetify/components";
import {VCheckbox, VFileInput, VTextarea, VTextField} from "vuetify/components";
import ColorPickerButton from "@partials/fields/misc/ColorPickerButton.vue";
import CheckBoxConditional from "@partials/fields/misc/CheckBoxConditional.vue";
import PlaylistSelect from "@partials/fields/playlist/PlaylistSelect.vue";
export interface Show {
id?: number;
@ -55,84 +45,140 @@ export const baseShow = (): Show => {
liveStreamPass: '',
imagePath: '',
hasAutoplaylist: false,
autoplaylistId: 0,
autoplaylistRepeat: false,
showDjs: null
}
}
const fieldDefinitions: FieldDefinitions = {
const fieldDefinitions = {
name: {
label: 'Nome',
component: VTextField,
disabled: false
} as TextFieldConfig,
required: true,
},
url: {
label: 'URL',
component: VTextField,
disabled: false
} as TextFieldConfig,
required: true,
},
genre: {
label: 'Genere',
component: VTextField,
disabled: false
} as TextFieldConfig,
required: true,
},
description: {
label: 'Descrizione',
component: VTextarea,
disabled: false
} as TextareaConfig,
required: false,
},
backgroundColor: {
label: 'Colore di sfondo',
component: ColorPickerButton,
disabled: false,
} as ColorPickerFieldConfig,
required: false,
},
liveStreamUsingAirtimeAuth: {
component: CheckBoxConditional,
props: {
checkBoxForm: {
checkBoxField: {label: 'Autenticazione Airtime'},
fields: {
liveStreamUser: {
label: 'Utente streaming',
component: VTextField,
disabled: true
} as TextFieldConfig,
liveStreamPass: {
label: 'Password di streaming',
component: VTextField,
disabled: true
} as TextFieldConfig,
}
}
}
}, // TODO Add the ts interface
label: 'Autenticazione Airtime',
required: false,
},
imagePath: {
label: 'Percorso immagine',
component: VFileInput,
props: {type: 'file'},
disabled: false
} as FileFieldConfig,
required: false,
},
hasAutoplaylist: {
label: 'Ha playlist automatica',
component: VCheckbox,
disabled: false
} as CheckboxFieldConfig,
required: false,
},
autoplaylistRepeat: {
label: 'Ripeti playlist automatica',
component: VCheckbox,
disabled: false
} as CheckboxFieldConfig
required: false,
},
showDjs: {
label: 'DJs',
required: false
}
};
export function showForm(item: Show) {
const fields = {};
Object.keys(fieldDefinitions).forEach((key) => {
fields[key] = {
...fieldDefinitions[key],
value: item[key as keyof Show]
};
});
return fields;
}
export const showForm = (item: Show) => {
const fields = {};
Object.keys(fieldDefinitions).forEach((key) => {
fields[key] = {
label: fieldDefinitions[key].label,
value: item !== null ? item[key as keyof Show] : '',
required: fieldDefinitions[key].required,
component: null // Placeholder, will be set in the switch case
};
switch (key) {
case 'name':
case 'url':
case 'genre':
fields[key].component = VTextField;
break;
case 'description':
fields[key].component = VTextarea;
break;
case 'backgroundColor':
fields[key].component = ColorPickerButton;
break;
case 'liveStreamUsingAirtimeAuth':
fields[key].component = CheckBoxConditional;
fields[key].value = {
value: item !== null ? item.liveStreamUsingAirtimeAuth : false,
};
fields[key].props = {
checkBoxForm: {
checkBoxField: { label: fields[key].label },
fields: {
liveStreamUser: {
label: 'Utente streaming',
component: VTextField,
value: item !== null ? item.liveStreamUser : '',
disabled: true
},
liveStreamPass: {
label: 'Password di streaming',
component: VTextField,
value: item !== null ? item.liveStreamPass : '',
disabled: true
},
}
}
};
break;
case 'imagePath':
fields[key].component = VFileInput;
fields[key].props = { type: 'file' };
break;
case 'hasAutoplaylist':
fields[key].component = CheckBoxConditional;
fields[key].value = {
value: item !== null ? item.hasAutoplaylist : false,
};
fields[key].props = {
checkBoxForm: {
checkBoxField: { label: fields[key].label },
fields: {
liveStreamUser: {
label: 'Ripetere playlist?',
component: VCheckbox,
value: item.autoplaylistRepeat,
disabled: true
},
playlistId: {
label: 'Playlist',
component: PlaylistSelect,
value: item.autoplaylistId,
disabled: true
},
}
}
};
break;
case 'DJs':
fields[key].component = CheckBoxConditional;
fields[key].value = {
value: item.showDjs
};
}
});
return fields;
};
export const showTableHeader = [
{title: 'Nome', value: 'name'},

View file

@ -4,11 +4,8 @@ export interface ShowDjs {
id?: number;
subjsId: number;
showId: number;
dj?: User;
}
// Assuming User interface exists
export interface User {
id: number;
login: string;

View file

@ -1,8 +1,8 @@
import {VSelect, VTextField} from "vuetify/components";
import {VDatePicker, VSelect, VTextField} from "vuetify/components";
import type {Show} from "@models/show/show";
import axios, {type AxiosResponse} from "axios";
export interface ShowInstances {
export interface ShowInstance {
id?: number;
starts: string; // ISO datetime string
ends: string; // ISO datetime string
@ -19,16 +19,27 @@ export interface ShowInstances {
show?: Show; // Reference to Show interface
}
export function showInstancesForm(item: ShowInstances) {
export const baseShowInstance = (): ShowInstance => {
return {
id: null,
starts: '',
ends: '',
showId: 0,
record: 0,
rebroadcast: 0,
timeFilled: '00:00:00',
created: null,
modifiedInstance: false,
autoplaylistBuilt: false,
};
};
export function showInstancesForm(item: ShowInstance) {
const visibleFields = {
starts: 'Inizio',
ends: 'Fine',
showId: 'Programma',
record: 'Registrazione',
rebroadcast: 'Ritrasmissione',
timeFilled: 'Durata riempita',
modifiedInstance: 'Istanza modificata',
autoplaylistBuilt: 'Autoplaylist generata'
description: 'Descrizione',
};
return () => {
@ -36,7 +47,7 @@ export function showInstancesForm(item: ShowInstances) {
Object.keys(visibleFields).forEach((key) => {
fields[key] = {
label: visibleFields[key],
value: item[key as keyof ShowInstances],
value: item[key as keyof ShowInstance],
component: VTextField,
disabled: false
};
@ -44,38 +55,19 @@ export function showInstancesForm(item: ShowInstances) {
switch (key) {
case 'starts':
case 'ends':
fields[key].component = VDatePicker;
fields[key].props = {
type: 'datetime-local',
step: 300 // 5-minute increments
};
break;
case 'modifiedInstance':
case 'autoplaylistBuilt':
case 'description':
fields[key].component = VSelect;
fields[key].props = {
items: [
{text: 'Sì', value: true},
{text: 'No', value: false}
]
type: 'datetime-local',
step: 300 // 5-minute increments
};
break;
case 'record':
case 'rebroadcast':
fields[key].component = VSelect;
fields[key].props = {
items: [
{text: 'Sì', value: 1},
{text: 'No', value: 0}
]
};
break;
case 'showId':
fields[key].value = item.show?.name || '';
fields[key].disabled = true;
break;
case 'timeFilled':
fields[key].props = {type: 'time'};
break;
}
});
@ -88,7 +80,7 @@ export async function getShowInstances(options: {
starts?: string | null;
ends?: string | null;
withShow?: boolean | null;
}): Promise<ShowInstances[]> {
}): Promise<ShowInstance[]> {
const filteredParams = Object.fromEntries(
Object.entries(options).filter(([_, value]) => value !== undefined && value !== null)
);
@ -99,3 +91,8 @@ export async function getShowInstances(options: {
console.log("Error: " + error);
});
}
export const deleteShowInstance = async (showInstancesIds: Number[]) => {
return axios.delete(`showInstances`, {data: {'showInstancesIds': showInstancesIds}})
}

View file

@ -0,0 +1,70 @@
import {reactive, ref} from "vue";
import axios from "axios";
export function playlist_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 = [
// {title: '', key: 'artwork'},
{title: 'Nome playlist', value: 'name'},
{title: 'Creato da', value: 'creator'},
{title: 'Durata', value: 'length'},
{title: 'Ultima modifica', value: 'utime'},
{title: 'Azioni', value: 'actions'}
];
const getItems = async (page_info) => {
loading.value = true;
return await axios.get(`/playlist`, {
params: {
page: page_info.page,
per_page: page_info.itemsPerPage,
all: search.value
}
}).then((response) => {
console.log(response)
listData.itemsPerPage = response.data.per_page;
listData.first_page = response.data.from;
listData.last_page = response.data.last_page;
listData.page = response.data.current_page;
listData.total_items = response.data.total;
items.value = response.data.data
loading.value = false;
}).catch((error) => {
console.log("Error: "+error);
})
}
const editItem = (item) => {
item['_method'] = 'PUT'
return axios.post(`file/${item.id}`, item
).then((response) => {
console.log(response)
})
}
const deleteItem = (id) => {
return axios.post(`file/${id}`, {
_method: 'DELETE'
}).then((response) => {
getItems(listData)
// items.value = response.status === 200 ? items.value.filter(obj => obj.id !== id) : items
})
}
return { items, listData, headers, selected, loading, search, getItems, editItem, deleteItem }
}

View file

@ -0,0 +1,49 @@
import {reactive, ref} from "vue";
import axios, {type AxiosResponse} from "axios";
import {deleteShow, getShow, type Show, showTableHeader} from "@models/show/show.ts";
export function show_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 = showTableHeader
const getItems = async (options) => {
return getShow(options).then(showList => {
listData.itemsPerPage = showList.per_page;
listData.first_page = showList.from;
listData.last_page = showList.last_page;
listData.page = showList.current_page;
listData.total_items = showList.total;
items.value = showList.data
loading.value = false;
}).catch(error => {
console.log("Error: " + error);
})
};
const editItem = (item) => {
item['_method'] = 'PUT'
return axios.post(`show/${item.id}`, item
).then((response) => {
console.log(response)
})
}
const deleteItem = async () => await deleteShow(selected.value).then(async () => {
await getItems(listData)
});
return {items, listData, headers, selected, loading, search, getItems, editItem, deleteItem}
}