Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions client/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion client/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "client",
"version": "0.27.0",
"version": "0.27.1",
"description": "DigiScript front end",
"author": "DreamTeamProd",
"private": true,
Expand Down
20 changes: 20 additions & 0 deletions client/src/store/modules/show.js
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,16 @@ export default {
Vue.$toast.error('Unable to edit cue type');
}
},
async GET_IMPORTABLE_CUE_TYPES() {
const response = await fetch(`${makeURL('/api/v1/show/cues/types/import')}`, {
method: 'GET',
});
if (!response.ok) {
log.error('Unable to fetch importable cue types');
throw new Error('Failed to fetch importable cue types');
}
return response.json();
},
async GET_SHOW_SESSION_DATA(context) {
const response = await fetch(`${makeURL('/api/v1/show/sessions')}`);
if (response.ok) {
Expand Down Expand Up @@ -642,6 +652,16 @@ export default {
Vue.$toast.error('Unable to delete session tag');
}
},
async GET_IMPORTABLE_SESSION_TAGS() {
const response = await fetch(`${makeURL('/api/v1/show/session/tags/import')}`, {
method: 'GET',
});
if (!response.ok) {
log.error('Unable to fetch importable session tags');
throw new Error('Failed to fetch importable session tags');
}
return response.json();
},
async UPDATE_SESSION_TAGS(context, { sessionId, tagIds }) {
const response = await fetch(`${makeURL('/api/v1/show/sessions/assign-tags')}`, {
method: 'PATCH',
Expand Down
108 changes: 107 additions & 1 deletion client/src/views/show/config/ConfigCues.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@
<b-button v-if="IS_SHOW_EDITOR" v-b-modal.new-cue-type variant="outline-success">
New Cue Type
</b-button>
<b-button
v-if="IS_SHOW_EDITOR"
variant="outline-info"
class="ml-2"
@click="openImportModal"
>
Import Cue Type
</b-button>
</template>
<template #cell(colour)="data">
<p :style="{ color: data.item.colour }">
Expand Down Expand Up @@ -166,6 +174,52 @@
</b-form-group>
</b-form>
</b-modal>
<b-modal
id="import-cue-type-modal"
ref="import-cue-type-modal"
title="Import Cue Type"
size="xl"
hide-footer
@hidden="resetImportState"
>
<div v-if="isLoadingImport" class="text-center">
<b-spinner />
</div>
<div v-else-if="importCueTypeGroups.length === 0">
<p class="text-muted">No cue types available to import from other shows.</p>
</div>
<div v-else>
<b-card v-for="show in importCueTypeGroups" :key="show.id" no-body class="mb-2">
<b-card-header style="cursor: pointer" @click="toggleImportShow(show.id)">
<div class="d-flex justify-content-between align-items-center">
<span>{{ show.name }}</span>
<b-icon-chevron-down v-if="cueTypeGroupExpanded[show.id]" font-scale="0.8" />
<b-icon-chevron-up v-else font-scale="0.8" />
</div>
</b-card-header>
<b-collapse :visible="cueTypeGroupExpanded[show.id]">
<b-table :items="show.cue_types" :fields="importCueTypeFields" small>
<template #cell(colour)="data">
<p :style="{ color: data.item.colour }">
<b-icon-square-fill />
</p>
</template>
<template #cell(action)="data">
<b-button
variant="outline-success"
size="sm"
:disabled="!!isImporting[data.item.id]"
@click="importCueType(data.item)"
>
<b-spinner v-if="isImporting[data.item.id]" small />
<span v-else>Import</span>
</b-button>
</template>
</b-table>
</b-collapse>
</b-card>
</div>
</b-modal>
</b-container>
</template>

Expand Down Expand Up @@ -199,6 +253,16 @@ export default {
submittingNewCueType: false,
submittingEditCueType: false,
deletingCueType: false,
importCueTypeGroups: [],
cueTypeGroupExpanded: {},
isLoadingImport: false,
isImporting: {},
importCueTypeFields: [
{ key: 'prefix', label: 'Prefix' },
{ key: 'description', label: 'Description' },
{ key: 'colour', label: 'Colour' },
{ key: 'action', label: '' },
],
};
},
validations: {
Expand Down Expand Up @@ -234,7 +298,13 @@ export default {
await this.GET_CUE_TYPES();
},
methods: {
...mapActions(['GET_CUE_TYPES', 'ADD_CUE_TYPE', 'DELETE_CUE_TYPE', 'UPDATE_CUE_TYPE']),
...mapActions([
'GET_CUE_TYPES',
'ADD_CUE_TYPE',
'DELETE_CUE_TYPE',
'UPDATE_CUE_TYPE',
'GET_IMPORTABLE_CUE_TYPES',
]),
resetNewCueTypeForm() {
this.newCueTypeForm = {
prefix: '',
Expand Down Expand Up @@ -334,6 +404,42 @@ export default {
const { $dirty, $error } = this.$v.editCueTypeFormState[name];
return $dirty ? !$error : null;
},
async openImportModal() {
this.$bvModal.show('import-cue-type-modal');
this.isLoadingImport = true;
try {
const data = await this.GET_IMPORTABLE_CUE_TYPES();
this.importCueTypeGroups = data.cue_type_groups;
data.cue_type_groups.forEach((show) => {
this.$set(this.cueTypeGroupExpanded, show.id, true);
});
} catch (e) {
log.error('Error loading importable cue types:', e);
} finally {
this.isLoadingImport = false;
}
},
toggleImportShow(showId) {
this.$set(this.cueTypeGroupExpanded, showId, !this.cueTypeGroupExpanded[showId]);
},
async importCueType(cueType) {
this.$set(this.isImporting, cueType.id, true);
try {
await this.ADD_CUE_TYPE({
prefix: cueType.prefix,
description: cueType.description,
colour: cueType.colour,
});
} finally {
this.$set(this.isImporting, cueType.id, false);
}
},
resetImportState() {
this.importCueTypeGroups = [];
this.cueTypeGroupExpanded = {};
this.isLoadingImport = false;
this.isImporting = {};
},
},
};
</script>
118 changes: 117 additions & 1 deletion client/src/vue_components/show/config/sessions/SessionTagList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@
<b-button v-if="IS_SHOW_EDITOR" v-b-modal.new-tag variant="outline-success">
New Tag
</b-button>
<b-button
v-if="IS_SHOW_EDITOR"
variant="outline-info"
class="ml-2"
@click="openImportModal"
>
Import Tag
</b-button>
</template>
<template #cell(tag)="data">
<span
Expand Down Expand Up @@ -141,6 +149,63 @@
</b-form-group>
</b-form>
</b-modal>
<b-modal
id="import-tag-modal"
ref="import-tag-modal"
title="Import Session Tag"
size="xl"
hide-footer
@hidden="resetImportState"
>
<div v-if="isLoadingImport" class="text-center">
<b-spinner />
</div>
<div v-else-if="importTagGroups.length === 0">
<p class="text-muted">No session tags available to import from other shows.</p>
</div>
<div v-else>
<b-card v-for="show in importTagGroups" :key="show.id" no-body class="mb-2">
<b-card-header style="cursor: pointer" @click="toggleImportShow(show.id)">
<div class="d-flex justify-content-between align-items-center">
<span>{{ show.name }}</span>
<b-icon-chevron-down v-if="tagGroupExpanded[show.id]" font-scale="0.8" />
<b-icon-chevron-up v-else font-scale="0.8" />
</div>
</b-card-header>
<b-collapse :visible="tagGroupExpanded[show.id]">
<b-table :items="show.tags" :fields="importTagFields" small>
<template #cell(tag)="data">
<span
class="tag-pill"
:style="{
backgroundColor: data.item.colour,
color: contrastColor({ bgColor: data.item.colour }),
}"
>
{{ data.item.tag }}
</span>
</template>
<template #cell(action)="data">
<span
v-b-tooltip.hover
:title="tagAlreadyExists(data.item) ? 'Already exists in this show' : ''"
>
<b-button
variant="outline-success"
size="sm"
:disabled="!!isImporting[data.item.id] || tagAlreadyExists(data.item)"
@click="importTag(data.item)"
>
<b-spinner v-if="isImporting[data.item.id]" small />
<span v-else>Import</span>
</b-button>
</span>
</template>
</b-table>
</b-collapse>
</b-card>
</div>
</b-modal>
</span>
</template>

Expand Down Expand Up @@ -196,6 +261,14 @@ export default {
isSubmittingNewTag: false,
isSubmittingEditTag: false,
isSubmittingDeleteTag: false,
importTagGroups: [],
tagGroupExpanded: {},
isLoadingImport: false,
isImporting: {},
importTagFields: [
{ key: 'tag', label: 'Tag' },
{ key: 'action', label: '' },
],
};
},
validations: {
Expand All @@ -222,9 +295,17 @@ export default {
},
computed: {
...mapGetters(['SESSION_TAGS', 'SHOW_SESSIONS_LIST', 'IS_SHOW_EDITOR']),
existingTagNames() {
return new Set((this.SESSION_TAGS || []).map((t) => t.tag.toLowerCase()));
},
},
methods: {
...mapActions(['ADD_SESSION_TAG', 'UPDATE_SESSION_TAG', 'DELETE_SESSION_TAG']),
...mapActions([
'ADD_SESSION_TAG',
'UPDATE_SESSION_TAG',
'DELETE_SESSION_TAG',
'GET_IMPORTABLE_SESSION_TAGS',
]),
contrastColor,
getSessionCountForTag(tagId) {
if (!this.SHOW_SESSIONS_LIST || !Array.isArray(this.SHOW_SESSIONS_LIST)) {
Expand Down Expand Up @@ -316,6 +397,41 @@ export default {
const { $dirty, $error } = this.$v.editTagForm[name];
return $dirty ? !$error : null;
},
async openImportModal() {
this.$bvModal.show('import-tag-modal');
this.isLoadingImport = true;
try {
const data = await this.GET_IMPORTABLE_SESSION_TAGS();
this.importTagGroups = data.tag_groups;
data.tag_groups.forEach((show) => {
this.$set(this.tagGroupExpanded, show.id, true);
});
} catch (e) {
log.error('Error loading importable session tags:', e);
} finally {
this.isLoadingImport = false;
}
},
toggleImportShow(showId) {
this.$set(this.tagGroupExpanded, showId, !this.tagGroupExpanded[showId]);
},
tagAlreadyExists(tag) {
return this.existingTagNames.has(tag.tag.toLowerCase());
},
async importTag(tag) {
this.$set(this.isImporting, tag.id, true);
try {
await this.ADD_SESSION_TAG({ tag: tag.tag, colour: tag.colour });
} finally {
this.$set(this.isImporting, tag.id, false);
}
},
resetImportState() {
this.importTagGroups = [];
this.tagGroupExpanded = {};
this.isLoadingImport = false;
this.isImporting = {};
},
async deleteTag(data) {
if (this.isSubmittingDeleteTag) {
return;
Expand Down
6 changes: 5 additions & 1 deletion docs/pages/cue_config.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ The **Cue Types** tab allows you to Add, Edit, and Delete different cue types. C

#### Creating Cue Types

Click **Add** to create a new cue type. For each cue type, you'll need to specify:
Click **New Cue Type** to create a new cue type. For each cue type, you'll need to specify:
- **Prefix**: A short identifier (e.g., "LX" for lighting, "SND" for sound)
- **Description**: A full description of the cue type
- **Color**: A color code to visually distinguish this cue type in the interface
Expand All @@ -21,6 +21,10 @@ After adding cue types, they will appear in the cue types overview:

The color you choose will be used throughout DigiScript to make different cue types instantly recognizable during configuration and live shows.

#### Importing Cue Types from Another Show

If you have already configured cue types for another show, you can import them into the current show using the **Import Cue Type** button. This opens a panel listing all cue types from your other shows, grouped by show name. Click **Import** next to any cue type to add an independent copy to the current show — changes made after import do not affect the original show.

### Adding Cues to the Script

The **Cue Configuration** tab allows you to add cues to your script. This interface is similar to the script editing page in layout and function. When first accessed, you'll see your script without any cues:
Expand Down
Loading
Loading