Merge branch 'spot' into dev

This commit is contained in:
Michael 2025-07-15 11:29:43 +02:00
commit 4e8664ee31
42 changed files with 549 additions and 155 deletions

View file

@ -3,6 +3,7 @@
namespace App\Filters;
use App\Filters\FiltersType\AllFilter;
use App\Filters\FiltersType\IsFilter;
use App\Filters\FiltersType\LikeFilter;
class FileFilters

View file

@ -0,0 +1,15 @@
<?php
namespace App\Filters\FiltersType;
use Log;
class SpotFilter
{
function __invoke($query, $spotTableName, $idColumnName) {
try {
return $query->whereIn('id', $spotTableName::select($idColumnName));
} catch (\Exception $e) {
Log::error($e);
}
}
}

View file

@ -2,44 +2,38 @@
namespace App\Filters\Show;
use App\Filters\FiltersType\AllFilter;
use App\Filters\FiltersType\LikeFilter;
use App\Models\Spot\SpotPlaylist;
class ShowFilters
{
protected $filters = [
'name' => LikeFilter::class,
'dj' => LikeFilter::class,
'genre' => LikeFilter::class,
'description' => LikeFilter::class,
'color' => LikeFilter::class,
'background_color' => LikeFilter::class,
'live_stream_using_airtime_auth' => LikeFilter::class,
'live_stream_using_custom_auth' => LikeFilter::class,
'live_stream_user' => LikeFilter::class,
'live_stream_pass' => LikeFilter::class,
'linked' => LikeFilter::class,
'is_linkable' => LikeFilter::class,
'image_path' => LikeFilter::class,
'has_autoplaylist' => LikeFilter::class,
'autoplaylist_id' => LikeFilter::class,
'autoplaylist_repeat' => LikeFilter::class,
'all' => AllFilter::class,
];
public function apply($query)
public function apply($query, $filters)
{
foreach ($this->receivedFilters() as $name => $value) {
if ($name != 'per_page') {
$filterInstance = new $this->filters[$name];
$query = $filterInstance($query, $name, $value);
foreach ($this->receivedFilters($filters) as $name => $value) {
switch ($name) {
case 'all':
$name = array_diff(array_keys($this->filters), ['all']);
$filterInstance = new $this->filters['all'];
break;
default:
$filterInstance = new $this->filters[$name];
break;
}
$query = $filterInstance($query, $name, $value);
}
return $query;
}
public function receivedFilters()
public function receivedFilters($filters)
{
return request()->only(array_keys($this->filters));
return $filters->only(array_keys($this->filters));
}
}

View file

@ -27,11 +27,17 @@ class FileController extends Controller
$pagination = $request->per_page;
}
return File::searchFilter($request)
->where('import_status', '=', 0)
->with('track_type')
->with('owner')
->orderBy('artist_name')
$files = File::searchFilter($request)
->where('import_status', '=', 0)
->with('track_type')
->with('owner');
if($request->track_type == 'spot'){
$trackTypes = TrackType::where('code', '=', 'SPOT')->orWhere('parent_id', '=', '3')->pluck('id');
$files = $files->whereIn('track_type_id', $trackTypes->toArray());
}
return $files->orderBy('artist_name')
->paginate($pagination)
->toJson();
}

View file

@ -8,11 +8,13 @@ use App\Models\File;
use App\Models\Playlist;
use App\Models\PlaylistContent;
use App\Models\SmartBlock;
use App\Models\Spot\SpotPlaylist;
use App\Models\Webstream;
use DateInterval;
use DateTime;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
class PlaylistController extends Controller
{
@ -22,17 +24,37 @@ class PlaylistController extends Controller
* @return string
*/
public function index(Request $request) {
if (!isset($request->per_page) || is_null($request)) {
$pagination = 5;
} else {
$pagination = $request->per_page;
}
try {
if ( ! isset($request->per_page) || is_null($request)) {
$pagination = 5;
} else {
$pagination = $request->per_page;
}
return Playlist::searchFilter($request)
->with(['creator', 'tracks.file', 'tracks.block', 'tracks.block.criteria', 'tracks.block.creator'])
->orderBy('name')
->paginate($pagination)
->toJson();
$playlists = Playlist::searchFilter($request)
->with(
[
'creator',
'tracks.file',
'tracks.block',
'tracks.block.criteria',
'tracks.block.creator'
]
);
if($request->has('playlistType')) {
$playlistType = $request->get('playlistType');
$playlists = ($playlistType == 'show')
? $playlists->doesntHave('spotPlaylist')
: $playlists->has('spotPlaylist');
}
return $playlists->orderBy('name')
->paginate($pagination)
->toJson();
} catch (\Exception $e) {
Log::error($e);
}
}
/**
@ -56,6 +78,9 @@ class PlaylistController extends Controller
'creator_id' => $user->id,
'description' => $request->description,
]);
if($request['playlist_type'] === 'spot') {
$dbPlaylist->spotPlaylist()->create();
}
// dd($request->tracks);
foreach($request->tracks as $key => $file) {
if (!isset($file['id'])) {

View file

@ -25,15 +25,25 @@ class ShowController extends Controller
use ShowInstancesTrait;
use ShowDjTrait;
public function index(ShowFilters $filters)
public function index(Request $request)
{
if ( ! isset($filters->per_page) || is_null($filters)) {
$pagination = 20;
} else {
$pagination = $filters->per_page;
$pagination = 20;
if(isset($request->per_page) || is_null($request)) {
$pagination = $request->per_page;
}
$shows = Show::searchFilter($request);
if($request->has('showType')) {
$showType = $request->get('showType');
$shows = ($showType == 'show')
? $shows->doesntHave('spotShow')
: $shows->has('spotShow');
}
return Show::searchFilter($filters)->cursorPaginate($pagination)->toJson();
return $shows->orderBy('name')
->paginate($pagination)
->toJson();
}
/**

View file

@ -14,6 +14,7 @@ use http\Exception\BadMethodCallException;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
class SmartBlockController extends Controller
{
@ -29,9 +30,17 @@ class SmartBlockController extends Controller
} else {
$pagination = $request->per_page;
}
$smartblocks = SmartBlock::searchFilter($request)
->with(['creator', 'tracks', 'tracks.file', 'criteria']);
return SmartBlock::searchFilter($request)
->with(['creator', 'tracks', 'tracks.file', 'criteria'])
if($request->has('smartblockType')) {
$smartblockType = $request->get('smartblockType');
$smartblocks = ($smartblockType == 'show')
? $smartblocks->doesntHave('spotSmartblock')
: $smartblocks->has('spotSmartblock');
}
return $smartblocks
->paginate($pagination)
->toJson();
}
@ -71,37 +80,37 @@ class SmartBlockController extends Controller
* @return mixed string
*/
public function save(Request $request) {
$user = Auth::user();
//dd($user);
$request->validate([
'name' => 'required|string',
'type' => 'required|string',
'criteria' => 'required|array'
]);
try {
$user = Auth::user();//dd($user);
$request->validate([
'name' => 'required|string',
'type' => 'required|string',
'criteria' => 'required|array'
]);
$criteria = $this->createCriteria($request);
$length = 0;
$dbSmartBlock = SmartBlock::firstOrNew(['id' => $request->id]);
$dbSmartBlock->fill([
'name' => $request->name,
'creator_id' => $user->id,
'description' => $request->description,
'length' => $request->length,
])->save();
if ($request['smart_block_type'] === 'spot') {
$dbSmartBlock->spotSmartBlock()->create();
}
$this->saveCriteria($dbSmartBlock, $criteria);//ToDo: save content
if (is_array($request->tracks) && count($request->tracks) > 0) {
SmartBlockContent::where('block_id', '=', $dbSmartBlock->id)->delete();
foreach ($request->tracks as $key => $track) {
$this->saveContent($track['file'], $dbSmartBlock->id, $key);
}
}
$criteria = $this->createCriteria($request);
$length = 0;
$dbSmartBlock = SmartBlock::firstOrNew(['id' => $request->id]);
$dbSmartBlock->fill([
'name' => $request->name,
'creator_id' => $user->id,
'description' => $request->description,
'length' => $request->length,
])->save();
$this->saveCriteria($dbSmartBlock, $criteria);
//ToDo: save content
if (is_array($request->tracks) && count($request->tracks) > 0) {
SmartBlockContent::where('block_id','=',$dbSmartBlock->id)->delete();
foreach ($request->tracks as $key => $track) {
$this->saveContent($track['file'], $dbSmartBlock->id, $key);
}
}
return $dbSmartBlock->toJson();
return $dbSmartBlock->toJson();
} catch (\Exception $e) {
Log::error($e);
}
}
/**

View file

@ -3,6 +3,7 @@
namespace App\Models;
use App\Filters\PlaylistFilter;
use App\Models\Spot\SpotPlaylist;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use MusicBrainz\Value\Track;
@ -30,6 +31,10 @@ class Playlist extends Model
return $this->hasMany(PlaylistContent::class);
}
public function spotPlaylist(){
return $this->hasOne(SpotPlaylist::class, 'playlist_id');
}
public function scopeSearchFilter($query, $request) {
$filters = new PlaylistFilter();
return $filters->apply($query, $request);

View file

@ -2,14 +2,14 @@
namespace App\Models\Show;
use App\Filters\Show\ShowFilters;
use App\Models\Playlist;
use App\Models\ShowInstances\ShowInstances;
use App\Models\SmartBlock;
use App\Models\Spot\SpotShow;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
class Show extends Model
{
@ -66,13 +66,18 @@ class Show extends Model
return $this->hasMany(ShowInstances::class, 'show_id');
}
public function scopeSearchFilter($query, $filters)
public function scopeSearchFilter($query, $request)
{
return $filters->apply($query);
$filters = new ShowFilters();
return $filters->apply($query, $request);
}
public function playlist()
{
return $this->belongsTo(Playlist::class, 'autoplaylist_id', 'id');
}
public function spotShow(){
return $this->hasOne(SpotShow::class, 'show_id');
}
}

View file

@ -3,6 +3,7 @@
namespace App\Models;
use App\Filters\PlaylistFilter;
use App\Models\Spot\SpotSmartBlock;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
@ -41,4 +42,8 @@ class SmartBlock extends Model
$filters = new PlaylistFilter();
return $filters->apply($query, $request);
}
public function spotSmartBlock(){
return $this->hasOne(SpotSmartBlock::class, 'block_id');
}
}

View file

@ -0,0 +1,23 @@
<?php
namespace App\Models\Spot;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class SpotPlaylist extends Model
{
protected $table = 'wa_spot_playlist';
protected $fillable = [
'playlist_id',
];
/**
* Get the playlist associated with this spot.
*/
public function playlist(): BelongsTo
{
return $this->belongsTo(Playlist::class, 'playlist_id');
}
}

View file

@ -0,0 +1,24 @@
<?php
namespace App\Models\Spot;
use App\Models\Show\Show;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class SpotShow extends Model
{
protected $table = 'wa_spot_show';
protected $fillable = [
'show_id',
];
/**
* Get the show associated with this spot.
*/
public function show(): BelongsTo
{
return $this->belongsTo(Show::class, 'show_id');
}
}

View file

@ -0,0 +1,24 @@
<?php
namespace App\Models\Spot;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use PhpParser\Node\Stmt\Block;
class SpotSmartBlock extends Model
{
protected $table = 'wa_spot_block';
protected $fillable = [
'block_id',
];
/**
* Get the block associated with this spot.
*/
public function block(): BelongsTo
{
return $this->belongsTo(Block::class, 'block_id');
}
}

View file

@ -6,6 +6,7 @@ use App\Http\Resources\ShowResource;
use App\Lib\RabbitMQSender;
use App\Models\Show\Show;
use App\Models\Show\ShowHosts;
use App\Models\Spot\SpotShow;
use Exception;
use Illuminate\Support\Facades\DB;
use Throwable;
@ -31,6 +32,10 @@ trait ShowTrait
}
$this->manageShowInstances($show);
$show->save();
## TODO Add show to table of spots
if($showData['show_type'] === 'spot') {
$show->spotShow()->create();
}
DB::commit();
} catch (Exception $e) {
DB::rollBack();

View file

@ -0,0 +1,25 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateWaSpotPlaylistTable extends Migration
{
public function up()
{
if (!Schema::hasTable('wa_spot_playlist')) {
Schema::create('wa_spot_playlist', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('playlist_id');
$table->timestamps();
$table->foreign('playlist_id')->references('id')->on('cc_playlist')->onDelete('cascade');
});
}
}
public function down()
{
Schema::dropIfExists('wa_spot_playlist');
}
}

View file

@ -0,0 +1,25 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateWaSpotShowTable extends Migration
{
public function up()
{
if (!Schema::hasTable('wa_spot_show')) {
Schema::create('wa_spot_show', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('show_id');
$table->timestamps();
$table->foreign('show_id')->references('id')->on('cc_show')->onDelete('cascade');
});
}}
public function down()
{
Schema::dropIfExists('wa_spot_show');
}
}

View file

@ -0,0 +1,26 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateWaSpotBlockTable extends Migration
{
public function up()
{
if (!Schema::hasTable('wa_spot_block')) {
Schema::create('wa_spot_block', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('block_id');
$table->timestamps();
$table->foreign('block_id')->references('id')->on('cc_block')->onDelete('cascade');
});
}
}
public function down()
{
Schema::dropIfExists('wa_spot_block');
}
}

View file

@ -1,5 +1,5 @@
<script setup lang="ts">
import {computed, onActivated, onDeactivated, ref} from "vue";
import {computed, onActivated, onDeactivated, onMounted, ref} from "vue";
import {useAuthStore} from "@/stores/auth.store.ts";
import {deleteShow} from "@models/show/show.ts";
import {baseShowInstance, deleteShowInstance, getShowInstances} from "@models/show/showInstance.ts";
@ -76,7 +76,7 @@ const goBack = async () => {
// so reducing the network calls sent
// That requires the handling of the context menu
// and the show/instance id in a different way though
onActivated(async () => {
onMounted(async () => {
await triggerFetchShowInstances(new Date());
intervalId = setInterval(async () => {
if (!isRunning.value) {

View file

@ -3,19 +3,20 @@ import {playlist_page} from "@/composables/content/playlist_page.ts";
import Table from "@/components/content/partials/Table.vue";
import PlaylistEditor from "@/components/content/partials/PlaylistEditor.vue";
import ConfirmDelete from "@/components/content/partials/dialogs/ConfirmDelete.vue";
import {reactive, ref, watch} from "vue";
import {onBeforeMount, onMounted, type PropType, reactive, ref, watch} from "vue";
import {usePlaylistStore} from "@/stores/playlist.store.ts";
import {baseSmartBlock} from "@models/smartblock/smartblock.ts";
import {basePlaylist} from "@models/playlist/playlist.ts";
import {useShowTypeStore} from "@stores/showType.store.ts";
const playlistStore = usePlaylistStore();
const { items, listData, headers, selected, loading, search, getItems, editItem, deleteItem } = playlist_page();
// Props, data, stores
const itemEdited = ref({
id: null
});
const bulk = ref(false)
const dialog = reactive({
open: false,
@ -23,7 +24,9 @@ const dialog = reactive({
title: '',
text: ''
})
const showTypeStore = useShowTypeStore();
// Funcs
const openDialog = (type, title = '', text = '', bulk = false) => {
dialog.open = true
dialog.type = type
@ -37,10 +40,11 @@ const edit = (item) => {
item = basePlaylist();
}
playlistStore.loadPlaylist(item);
playlistStore.currentPlaylist.playlist_type = showTypeStore.currentType;
itemEdited.value = item;
}
const save = (item) => {
const save = (item) => {5
if (item.name === '') {
//Check required fields
console.log('error!')
@ -91,7 +95,6 @@ const resetItemEdited = () => {
watch(search, (newValue, oldValue) => {
getItems(listData)
})
</script>
<template>

View file

@ -1,13 +1,15 @@
<script setup lang="ts">
import {reactive, ref, watch} from "vue";
import {onActivated, onBeforeMount, onMounted, type PropType, 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 ShowForm from "@partials/show/ShowForm.vue";
import {baseShow, type Show} from "@models/show/show";
import {useShowTypeStore} from "@stores/showType.store.ts";
const {items, listData, headers, selected, loading, search, getItems, editItem, deleteItem} = show_page()
const showTypeStore = useShowTypeStore();
const showCreateEditMode = ref(false);
let showSelected = ref<Number | null>(null);
@ -23,6 +25,7 @@ const dialog = reactive({
text: ''
})
// Funcs
const openDialog = (type, title: string = '', text: string = '') => {
dialog.open = true
dialog.type = type
@ -46,8 +49,8 @@ const saveItem = (item) => {
}
const cancel = (item) => {
let deleteMessage = 'Vuoi cancellare lo show selezionato?'
if(bulk.value.state) deleteMessage = 'Vuoi cancellare gli show selezionati?'
let deleteMessage = `Vuoi cancellare lo ${showTypeStore.currentType} selezionato?`
if (bulk.value.state) deleteMessage = `Vuoi cancellare gli ${showTypeStore.currentType} selezionati?`
bulk.value.items = item
showSelected.value = item?.id
openDialog(
@ -59,7 +62,7 @@ const cancel = (item) => {
const confirmDelete = (confirm) => {
if (confirm) {
const showId = showSelected.value == 0 ? null : showSelected.value ;
const showId = showSelected.value == 0 ? null : showSelected.value;
deleteItem(showId)
}
closeDialog()
@ -78,19 +81,19 @@ const resetItemEdited = () => {
showSelected.value = null
}
watch(search, (newValue, oldValue) => {
const options = {...listData};
getItems(options)
})
const goBack = () => {
showCreateEditMode.value = false
showSelected.value = null
}
watch(search, (newValue, oldValue) => {
const options = {...listData};
getItems(options)
})
</script>
<template>
<ShowForm v-if="showCreateEditMode" :showId="showSelected" @go-back="goBack"/>
<ShowForm v-if="showCreateEditMode" :showId="showSelected" :showType="showTypeStore.currentType" @go-back="goBack"/>
<Table
v-else
:headers="headers"
@ -109,7 +112,7 @@ const goBack = () => {
>
<template v-slot:header-buttons>
<v-btn color="primary" @click="create">
Crea una nuova trasmissione
<span>Crea un nuovo {{ showTypeStore.currentType }} </span>
</v-btn>
</template>
<template v-slot:dialog>

View file

@ -1,12 +1,16 @@
<script setup lang="ts">
import Table from "@/components/content/partials/Table.vue";
import ConfirmDelete from "@/components/content/partials/dialogs/ConfirmDelete.vue";
import {onBeforeMount, reactive, ref, watch} from "vue";
import {onBeforeMount, type PropType, reactive, ref, watch} from "vue";
import {smartblock_page} from "@/composables/content/smartblock_page.ts";
import SmartBlockEditor from "@partials/SmartBlockEditor.vue";
import {useSmartBlockStore} from "@/stores/smartblock.store.ts";
import {baseSmartBlock} from "@models/smartblock/smartblock.ts";
import {useShowTypeStore} from "@stores/showType.store.ts";
const { items, listData, headers, selected, loading, search, getItems, editItem, deleteItem } = smartblock_page()
// Props, data and stores
const props = defineProps({
hideColumns: {
type: Array,
@ -15,14 +19,9 @@ const props = defineProps({
isDraggable: {
type: Boolean,
required: false
}
},
});
const smartBlockStore = useSmartBlockStore()
const { items, listData, headers, selected, loading, search, getItems, editItem, deleteItem } = smartblock_page()
const itemEdited = ref({
id: null
})
@ -37,6 +36,11 @@ const dialog = reactive({
const visibleHeaders = ref(headers)
const showTypeStore = useShowTypeStore();
const smartBlockStore = useSmartBlockStore()
// Funcs
const openDialog = (type, title = '', text = '') => {
dialog.open = true
dialog.type = type
@ -49,6 +53,7 @@ const edit = (item) => {
item = baseSmartBlock();
}
smartBlockStore.loadSmartBlock(item);
smartBlockStore.currentSmartBlock.smart_block_type = showTypeStore.currentType;
itemEdited.value = item;
console.log(smartBlockStore)
}
@ -72,7 +77,7 @@ const cancel = (item) => {
const confirmDelete = (confirm) => {
if (confirm) {
if (!bulk) {
if (!bulk.value) {
deleteItem(itemEdited.value.id)
} else {
itemEdited.value.forEach(el => {

View file

@ -6,26 +6,24 @@ import {smartblock} from "@models/smartblock/smartblock.ts";
import {smartblock_page} from "@/composables/content/smartblock_page.ts";
import {formatFromSeconds} from "@/helpers/TimeFormatter.ts";
import {useSmartBlockStore} from "@/stores/smartblock.store.ts";
const auth = useAuthStore();
import {baseSmartBlockCriteria} from "@models/smartblock/smartblockCriteria.ts";
const { loading, getTracklist } = smartblock_page()
const emit = defineEmits([
'saveItem'
])
//
const auth = useAuthStore();
const smartBlockStore = useSmartBlockStore();
const item = smartBlockStore.currentSmartBlock;
console.log(smartBlockStore.currentSmartBlock)
const length = ref([]);
const smartblockFields = smartblock(item)
const length = ref([]);
//Is true if there is a required field empty or while saving
const disabledSaveButton = ref(true)
// Funcs
const update = (list) => {
item.tracks = list
}
@ -61,8 +59,25 @@ const showPreview = async () => {
})
}
function hiddenSmartBlockCriteria(){
if(!smartBlockStore.currentSmartBlock.smart_block_type) return
let showSmartBlockCriteria = baseSmartBlockCriteria()
showSmartBlockCriteria.criteria = 'track_type_id'
showSmartBlockCriteria.modifier = 'is not'
showSmartBlockCriteria.value = '3'
if (smartBlockStore.currentSmartBlock.smart_block_type == 'spot') {
showSmartBlockCriteria.modifier = 'is'
}
smartBlockStore.currentSmartBlock.criteria.push(showSmartBlockCriteria)
}
// Hook and watch
onMounted(() => {
smartBlockStore.updateField({key: 'creator_id', value: auth.userData.user.id})
hiddenSmartBlockCriteria()
})
watch(loading.value, (newVal, oldVal) => {

View file

@ -1,8 +1,9 @@
<script setup lang="ts">
import {ref} from "vue";
import {onBeforeMount, ref} from "vue";
import Archive from "@/components/content/Archive.vue";
import Blocks from "@components/content/SmartBlock.vue";
import Webstream from "@components/content/Webstream.vue";
import {useShowTypeStore} from "@stores/showType.store.ts";
const tab = ref(null)
const tabs = [
@ -19,6 +20,15 @@ const tabs = [
title: 'Webstream',
},
]
onBeforeMount(() => {
const showTypeStore = useShowTypeStore()
if(showTypeStore.currentType == 'spot') {
const webstreamIndex = tabs.findIndex(tab => tab.id == 'webstream')
tabs.splice(webstreamIndex, 1)
}
})
</script>
<template>

View file

@ -1,11 +1,13 @@
<script setup lang="ts">
import {ref, onMounted, type PropType} from "vue";;
import {ref, onMounted, type PropType} from "vue";
;
import ShowScheduleForm from "@partials/show/ShowScheduleForm.vue";
import {useShowStore} from "@/stores/show.store.ts";
import {useShowStore} from "@stores/show/show.store.ts";
import {getUser} from "@models/User.ts";
import {getPlaylist} from "@models/playlist.ts";
import ColorPickerButton from "@partials/fields/misc/ColorPickerButton.vue";
import {useShowDaysStore} from "@/stores/showDays.store.ts";
import {useShowDaysStore} from "@stores/show/showDays.store.ts";
// Props and emits
const props = defineProps({
@ -13,6 +15,11 @@ const props = defineProps({
type: Number as PropType<number | null>,
required: true,
},
showType: {
type: String as PropType<'show' | 'spot'>,
required: true,
validator: (value: string) => ['show', 'spot'].includes(value),
},
});
const emits = defineEmits(['go-back']);
@ -31,15 +38,27 @@ const showDaysStore = useShowDaysStore()
// Funcs
onMounted(async () => {
loading.value = true
if (props.showId === null ) {
// Prepare show store
if (props.showId === null) {
showStore.resetShow()
showStore.currentShow.showType = props.showType
} else {
const selectedShow = await showStore.getShow(props.showId, {withDjs: true})
const withDjs = props.showType === 'show';
const selectedShow = await showStore.getShow(props.showId, {showType: props.showType, withDjs: withDjs})
showStore.loadShow(selectedShow)
showStore.currentShow.showType = props.showType
}
usersDJs.value = await getUser({role: 'dj'});
playlists.value = await getPlaylist({});
loading.value = false
// fill store
let playlistOptions: { playlistType: 'show' | 'spot' } = { playlistType: 'spot' };
if (props.showType === 'show') {
usersDJs.value = await getUser({role: 'dj'});
playlistOptions.playlistType = 'show';
}
playlists.value = await getPlaylist(playlistOptions);
loading.value = false;
})
const toggleShowScheduleForm = () => {
@ -105,7 +124,7 @@ const createShow = () => {
</v-col>
<!-- URL Field -->
<v-col cols="12" md="6" lg="4">
<v-col v-if="props.showType == 'show'" cols="12" md="6" lg="4">
<v-card>
<v-text-field
v-model="showStore.currentShow.url"
@ -197,7 +216,7 @@ const createShow = () => {
<!-- TODO Instead of the dj name, obj obj is shown -->
<!-- DJs Select -->
<v-col cols="12" md="6" lg="4">
<v-col v-if="props.showType == 'show'" cols="12" md="6" lg="4">
<v-card>
<v-select
v-model="showStore.currentShow.showDjs"
@ -216,8 +235,11 @@ const createShow = () => {
<v-card-actions>
<v-btn color="accent" @click="goBack">Torna indietro</v-btn>
<v-btn v-if="showStore.currentShow.id" color="accent" @click="showStore.updateShow()" :disabled="!isFormValid" >Salva</v-btn>
<v-btn color="accent" @click="toggleShowScheduleForm" :disabled="!isFormValid" >Regole di programmazione</v-btn>
<v-btn v-if="showStore.currentShow.id" color="accent" @click="showStore.updateShow()"
:disabled="!isFormValid">Salva
</v-btn>
<v-btn color="accent" @click="toggleShowScheduleForm" :disabled="!isFormValid">Regole di programmazione
</v-btn>
</v-card-actions>
</v-form>
</template>

View file

@ -1,6 +1,6 @@
<script setup lang="ts">
import ShowStartEndTime from "@partials/fields/show/ShowStartEndTime.vue";
import {useShowInstanceStore} from "@/stores/showInstance.store.ts";
import {useShowInstanceStore} from "@stores/show/showInstance.store.ts";
import {onMounted, ref, watch, type PropType} from "vue";
import {baseShowInstance, type ShowInstance} from "@models/show/showInstance.ts";
import {extractTime} from "@/helpers/DateFormatter.ts";
@ -8,7 +8,7 @@ import {getPlaylistContent} from "@models/playlist.ts";
import Sources from "@partials/Sources.vue";
import TrackList from "@partials/TrackList.vue";
import {DateTime} from "luxon";
import {useShowStore} from "@/stores/show.store.ts";
import {useShowStore} from "@stores/show/show.store.ts";
const emits = defineEmits(['toggle-menu-edit-instance']);
// Props

View file

@ -3,7 +3,7 @@ import {onMounted, type PropType, ref, watch} from 'vue';
import {showRepetitionData} from "@models/show/ShowRepetition.ts";
import DaysCheckbox from "@partials/fields/show/DaysCheckbox.vue";
import ShowStartEndTime from "@partials/fields/show/ShowStartEndTime.vue";
import {useShowDaysStore} from "@/stores/showDays.store.ts";
import {useShowDaysStore} from "@stores/show/showDays.store.ts";
// Emits and props
const emits = defineEmits(['toggle-show-schedule-form', 'trigger-show-creation'])

View file

@ -1,5 +1,6 @@
import axios from "axios";
import {ref, reactive, computed} from "vue";
import {useShowTypeStore} from "@stores/showType.store.ts";
export function archive_page() {
const items = ref([])
@ -13,6 +14,7 @@ export function archive_page() {
'total_items': 0,
'page': 1,
})
const showTypeStore = useShowTypeStore();
const headers = [
// {title: '', key: 'artwork'},
@ -30,12 +32,14 @@ export function archive_page() {
*/
const getItems = async (page_info) => {
loading.value = true;
let options = {
page: page_info.page,
per_page: page_info.itemsPerPage,
all: search.value
}
options['track_type'] = showTypeStore.currentType
return await axios.get(`/file`, {
params: {
page: page_info.page,
per_page: page_info.itemsPerPage,
all: search.value
}
params: options
}).then((response) => {
//console.log(response)
listData.itemsPerPage = response.data.per_page;

View file

@ -40,6 +40,7 @@ export function playlist(item) {
}
// TODO playlist interface
// TODO Add filter if playlist is spot
export const getPlaylist = async (options: {
id?: number | null;
scheduled?: number | null;
@ -48,6 +49,7 @@ export const getPlaylist = async (options: {
page?: Number | null;
per_page?: Number | null;
all?: string | null;
playlistType?: 'show' | 'spot' | null;
}): Promise<any> => {
const filteredParams = cleanOptions(options);
return await axios.get(`/playlist`, {params: filteredParams})

View file

@ -11,6 +11,7 @@ export interface Playlist {
description?: string; // Opzionale
length: number; // Durata della playlist
contents: PlaylistContent[];
playlist_type: 'show' | 'spot' | null
}
export const basePlaylist = (): Playlist => {
@ -21,6 +22,7 @@ export const basePlaylist = (): Playlist => {
description: '',
length: 0,
contents: [],
playlist_type: null
}
}

View file

@ -2,7 +2,7 @@ import type {ShowInstance} from "@models/show/showInstance.ts";
import type {ShowDays} from "@models/show/showDays";
import type {ShowDJs} from "@models/show/showDJs.ts";
import axios, {type AxiosResponse} from "axios";
import {cleanOptions} from "@/helpers/AxiosHelper.ts";
import {camelToSnake, cleanOptions} from "@/helpers/AxiosHelper.ts";
export interface Show {
id?: number;
@ -27,6 +27,9 @@ export interface Show {
showDjs?: ShowDJs[];
showInstances?: ShowInstance[];
playlist?: any;
// Extra
showType: 'show' | "spot" | null // Either show or spot
}
export const baseShow = (): Show => {
@ -46,6 +49,7 @@ export const baseShow = (): Show => {
autoplaylistRepeat: false,
showDjs: null,
showDays: null,
showType: null
}
}
@ -66,6 +70,7 @@ export const getShows = async (options: {
page?: Number | null;
per_page?: Number | null;
all?: string | null;
showType?: 'show' | 'spot' | null;
}): Promise<Show[]> => {
const filteredParams = cleanOptions(options);
return await axios.get(`/show`, {params: filteredParams})

View file

@ -17,6 +17,7 @@ export interface SmartBlock {
contents?: SmartBlockContent[]; // Contenuti associati (opzionale)
criteria?: SmartBlockCriteria[]; // Criteri associati (opzionale)
tracks?: SmartBlockContent[];
smart_block_type: 'show' | 'spot' | null;
}
export const baseSmartBlock = (): SmartBlock => {
@ -30,6 +31,7 @@ export const baseSmartBlock = (): SmartBlock => {
contents: null,
criteria: [],
tracks: [],
smart_block_type: null,
}
}
@ -46,6 +48,7 @@ export const getSmartBlock = async (options: {
page?: Number | null;
per_page?: Number | null;
all?: string | null;
smartblockType?: 'show' | 'spot' | null;
}): Promise<SmartBlock[]> => {
const filteredParams = cleanOptions(options);
return await axios.get(`/smartblock`, {params: filteredParams})

View file

@ -1,6 +1,7 @@
import {reactive, ref} from "vue";
import axios from "axios";
import {timeFormatter} from "@/helpers/TimeFormatter.ts";
import {useShowTypeStore} from "@stores/showType.store.ts";
export function playlist_page() {
const items = ref([])
@ -14,6 +15,7 @@ export function playlist_page() {
'total_items': 0,
'page': 1,
})
const showTypeStore = useShowTypeStore();
const headers = [
// {title: '', key: 'artwork'},
@ -30,7 +32,8 @@ export function playlist_page() {
params: {
page: page_info.page,
per_page: page_info.itemsPerPage,
all: search.value
all: search.value,
playlistType: showTypeStore.currentType,
}
}).then((response) => {
console.log(response)

View file

@ -1,6 +1,7 @@
import {reactive, ref} from "vue";
import axios, {type AxiosResponse} from "axios";
import {deleteShow, getShows, type Show, showTableHeader} from "@models/show/show.ts";
import {useShowTypeStore} from "@stores/showType.store.ts";
export function show_page() {
const items = ref([])
@ -15,10 +16,18 @@ export function show_page() {
'page': 1,
})
const showTypeStore = useShowTypeStore();
const headers = showTableHeader
const getItems = async (options) => {
return getShows(options).then(showList => {
const showSearchOptions = {
page: options?.page,
per_page: options?.itemsPerPage,
all: search.value,
showType: showTypeStore.currentType,
}
return getShows(showSearchOptions).then(showList => {
listData.itemsPerPage = showList.per_page;
listData.first_page = showList.from;
listData.last_page = showList.last_page;

View file

@ -4,6 +4,7 @@ import {timeFormatter} from "@/helpers/TimeFormatter.ts";
import {deleteSmartBlock, getSmartBlock, SmartBlockTableHeader} from "@models/smartblock/smartblock.ts";
import {showTableHeader} from "@models/show/show.ts";
import {useAuthStore} from "@/stores/auth.store.ts";
import {useShowTypeStore} from "@stores/showType.store.ts";
export function smartblock_page() {
const items = ref([])
@ -17,6 +18,7 @@ export function smartblock_page() {
'total_items': 0,
'page': 1,
})
const showTypeStore = useShowTypeStore();
const auth = useAuthStore();
const timezone = auth.userData.timezone;
@ -28,7 +30,8 @@ export function smartblock_page() {
return getSmartBlock({
page: options.page,
per_page: options.itemsPerPage,
all: search.value
all: search.value,
smartblockType: showTypeStore.currentType,
}).then((smartblockList) => {
console.log(smartblockList)
listData.itemsPerPage = smartblockList.per_page;

View file

@ -1,12 +1,15 @@
<script setup lang="ts">
import {computed, defineAsyncComponent} from 'vue';
import {computed, defineAsyncComponent, ref, watch} from 'vue';
import { useShowTypeStore } from '@/stores/showType.store';
// Props and data
const props = defineProps({
page: Object,
});
const currentPage = computed(() => props.page.id)
const showTypeStore = useShowTypeStore();
/**
* ToDo:
*/
@ -18,14 +21,33 @@ const tabs = {
blocks: defineAsyncComponent(() => import('@components/content/SmartBlock.vue')),
webstream: defineAsyncComponent(() => import('@components/content/Webstream.vue')),
podcast: defineAsyncComponent(() => import('@components/content/Podcast.vue')),
spot: defineAsyncComponent(() => import('@components/content/Show.vue')),
'spot-playlist': defineAsyncComponent(() => import('@components/content/Playlist.vue')),
'spot-blocks': defineAsyncComponent(() => import('@components/content/SmartBlock.vue')),
}
watch(currentPage, (newVal) => {
const showTypes = ['show', 'playlist', 'blocks']
const spotTypes = ['spot', 'spot-playlist', 'spot-blocks']
switch (true) {
case showTypes.includes(newVal):
showTypeStore.setAsShows()
break
case spotTypes.includes(newVal):
showTypeStore.setAsSpots()
break
default:
showTypeStore.clearType()
break
}
}, {immediate: true})
</script>
<template>
<v-col>
<!-- <keep-alive>-->
<Component :is="tabs[currentPage]" />
<!-- </keep-alive>-->
<!-- <keep-alive>-->
<Component :is="tabs[currentPage]" />
<!-- </keep-alive>-->
</v-col>
</template>

View file

@ -30,13 +30,27 @@ 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',
}
},
{
id: 'spot',
name: 'Spot',
component: '@components/content/Show.vue',
},
{
id: 'spot-playlist',
name: 'Spot playlist',
component: '@components/content/Playlist.vue',
},
{
id: 'spot-blocks',
name: 'Spot Blocchi dinamici',
component: '@components/content/SmartBlock.vue',
},
];
</script>

View file

@ -18,7 +18,7 @@ export const usePlaylistStore = defineStore('playlist', {
updateField(payload: { key: string; value: any }) {
this.currentPlaylist[payload.key] = payload.value
},
updatePlaylistContentsField(payload: { key: string; value: any }) {
updatePlaylistContentsField(pEayload: { key: string; value: any }) {
this.currentPlaylist.currentPlaylistContents[payload.key] = payload.value;
},
}

View file

@ -1,9 +1,9 @@
import {defineStore} from 'pinia'
import {type Show, baseShow} from '@models/show/show'
import type {ShowDays} from "@models/show/showDays";
import {type Show, baseShow} from '@models/show/show.ts'
import type {ShowDays} from "@models/show/showDays.ts";
import axios, {type AxiosResponse} from "axios";
import {camelToSnake, cleanOptions, snakeToCamel} from "@/helpers/AxiosHelper.ts";
import { extractTimeUTC} from "@/helpers/DateFormatter.ts";
import {extractTimeUTC} from "@/helpers/DateFormatter.ts";
import {DateTime} from "luxon";
export const useShowStore = defineStore('show', {
@ -19,19 +19,28 @@ export const useShowStore = defineStore('show', {
this.currentShow[payload.key] = payload.value
},
resetShow() {
this.currentShow = { ...baseShow() };
this.currentShow = {...baseShow()};
},
async getShow(id: Number, options: {
withDjs?: boolean | null;
isScheduled?: boolean | null;
}) {
const filteredParams = cleanOptions(options);
async getShow(
id: number,
{
showType = 'show',
withDjs,
isScheduled,
}: {
showType?: 'show' | 'spot';
withDjs?: boolean | null;
isScheduled?: boolean | null;
} = {}
) {
const filteredParams = cleanOptions({showType, withDjs, isScheduled});
return await axios.get(`/show/${id}`, {params: filteredParams})
.then((response: AxiosResponse) => {
return snakeToCamel(response.data);
}).catch((error: Error) => {
console.log("Error: " + error);
})
});
},
async createShow() {
let showData = {...this.currentShow};

View file

@ -1,5 +1,5 @@
import {defineStore} from 'pinia'
import type {ShowDays} from "@models/show/showDays";
import type {ShowDays} from "@models/show/showDays.ts";
import axios, {type AxiosResponse} from "axios";
import {camelToSnake, cleanOptions, snakeToCamel} from "@/helpers/AxiosHelper.ts";
import {extractTime, extractTimeUTC} from "@/helpers/DateFormatter.ts";

View file

@ -0,0 +1,32 @@
import {defineStore} from "pinia";
export type ShowType = 'show' | 'spot' | null;
export const useShowTypeStore = defineStore('showType', {
state: () => ({
type: null as ShowType,
}),
getters: {
// Returns the current type, useful for direct access
currentType: (state): ShowType => state.type,
// Returns a boolean, useful for v-if directives
isShowingShow: (state): boolean => state.type === 'show',
isShowingSpot: (state): boolean => state.type === 'spot',
hasActiveType: (state): boolean => state.type !== null,
},
actions: {
setShowType(newType: ShowType) {
this.type = newType;
},
setAsShows() {
this.type = 'show';
},
setAsSpots() {
this.type = 'spot';
},
clearType() {
this.type = null;
}
}
})

View file

@ -13,7 +13,8 @@
"@/*": ["resources/js/*"],
"@models/*":["resources/js/composables/content/models/*"],
"@partials/*":["resources/js/components/content/partials/*"],
"@components/*":["resources/js/components/*"]
"@components/*":["resources/js/components/*"],
"@stores/*":["resources/js/stores/*"]
},
"types": ["vite/client"]
}