Add category editing dialog
This commit is contained in:
parent
b5add9b8a0
commit
f149e9d702
6
components.d.ts
vendored
6
components.d.ts
vendored
@ -7,17 +7,13 @@ export {}
|
|||||||
|
|
||||||
declare module 'vue' {
|
declare module 'vue' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
copy: typeof import('./src/composables/EditGoalDialog copy.vue')['default']
|
EditCategoryDialog: typeof import('./src/composables/EditCategoryDialog.vue')['default']
|
||||||
EditGoal: typeof import('./src/composables/EditGoal.vue')['default']
|
|
||||||
EditGoalDialog: typeof import('./src/composables/EditGoalDialog.vue')['default']
|
EditGoalDialog: typeof import('./src/composables/EditGoalDialog.vue')['default']
|
||||||
EditGoalListDialog: typeof import('./src/composables/EditGoalListDialog.vue')['default']
|
EditGoalListDialog: typeof import('./src/composables/EditGoalListDialog.vue')['default']
|
||||||
EditorComponent: typeof import('./src/components/EditorComponent.vue')['default']
|
EditorComponent: typeof import('./src/components/EditorComponent.vue')['default']
|
||||||
GameEditor: typeof import('./src/composables/GameEditor.vue')['default']
|
|
||||||
GameEditorDialog: typeof import('./src/composables/GameEditorDialog.vue')['default']
|
GameEditorDialog: typeof import('./src/composables/GameEditorDialog.vue')['default']
|
||||||
GameList: typeof import('./src/composables/GameList.vue')['default']
|
|
||||||
GameListDialog__UNUSED_NEEDS_EDITS: typeof import('./src/composables/GameListDialog__UNUSED_NEEDS_EDITS.vue')['default']
|
GameListDialog__UNUSED_NEEDS_EDITS: typeof import('./src/composables/GameListDialog__UNUSED_NEEDS_EDITS.vue')['default']
|
||||||
GeneratorComponent: typeof import('./src/components/GeneratorComponent.vue')['default']
|
GeneratorComponent: typeof import('./src/components/GeneratorComponent.vue')['default']
|
||||||
GoalEditorDialog: typeof import('./src/components/GoalEditorDialog.vue')['default']
|
|
||||||
MarkdownRenderer: typeof import('./src/composables/MarkdownRenderer.vue')['default']
|
MarkdownRenderer: typeof import('./src/composables/MarkdownRenderer.vue')['default']
|
||||||
NavbarComponent: typeof import('./src/components/NavbarComponent.vue')['default']
|
NavbarComponent: typeof import('./src/components/NavbarComponent.vue')['default']
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
|
198
src/composables/EditCategoryDialog.vue
Normal file
198
src/composables/EditCategoryDialog.vue
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
|
||||||
|
<template>
|
||||||
|
<q-card
|
||||||
|
flat
|
||||||
|
bordered
|
||||||
|
style="min-width: 700px; max-width: 80vw; min-height: 10vh; max-height: 80vh;"
|
||||||
|
>
|
||||||
|
<q-card-section class="q-gutter-y-md column">
|
||||||
|
<q-input
|
||||||
|
v-model="category_name"
|
||||||
|
:error="!!name_error.length"
|
||||||
|
:error-message="name_error"
|
||||||
|
square
|
||||||
|
filled
|
||||||
|
counter
|
||||||
|
clearable
|
||||||
|
maxlength="40"
|
||||||
|
label="Name"
|
||||||
|
type="text"
|
||||||
|
></q-input>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
class="col-10"
|
||||||
|
v-model="category_description"
|
||||||
|
square
|
||||||
|
filled
|
||||||
|
counter
|
||||||
|
autogrow
|
||||||
|
maxlength="500"
|
||||||
|
label="Description"
|
||||||
|
hint="Optional"
|
||||||
|
type="text"
|
||||||
|
>
|
||||||
|
<template v-slot:after>
|
||||||
|
<q-btn
|
||||||
|
label="Preview"
|
||||||
|
outline
|
||||||
|
color="green"
|
||||||
|
@click="markdown_preview = true"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-card-section>
|
||||||
|
|
||||||
|
<q-separator />
|
||||||
|
|
||||||
|
<q-card-actions>
|
||||||
|
<q-btn
|
||||||
|
v-if="data.category"
|
||||||
|
flat
|
||||||
|
color="red"
|
||||||
|
icon="delete"
|
||||||
|
:label="delete_label"
|
||||||
|
@click="deleteCategory()"
|
||||||
|
:disabled="!delete_actually_enabled"
|
||||||
|
>
|
||||||
|
<q-tooltip v-if="has_goal_lists">
|
||||||
|
Cannot delete category with goal lists
|
||||||
|
</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
|
||||||
|
<q-space/>
|
||||||
|
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
color="red"
|
||||||
|
icon="cancel"
|
||||||
|
label="Cancel"
|
||||||
|
@click="cancel()"
|
||||||
|
/>
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
color="green"
|
||||||
|
icon="save"
|
||||||
|
label="Save"
|
||||||
|
@click="saveCategory()"
|
||||||
|
:disabled="!canSave()"
|
||||||
|
/>
|
||||||
|
</q-card-actions>
|
||||||
|
</q-card>
|
||||||
|
|
||||||
|
<q-dialog v-model="markdown_preview">
|
||||||
|
<q-card
|
||||||
|
flat
|
||||||
|
bordered
|
||||||
|
>
|
||||||
|
<q-card-section>
|
||||||
|
<MarkdownRenderer :text="category_description"/>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import BingoCategory from '@/js/lib/BingoCategory.ts';
|
||||||
|
import {
|
||||||
|
stringCompare
|
||||||
|
} from '@/js/lib/Util.ts';
|
||||||
|
|
||||||
|
const emit = defineEmits([
|
||||||
|
'cancel',
|
||||||
|
'createCategory',
|
||||||
|
'updateCategory',
|
||||||
|
'deleteCategory'
|
||||||
|
]);
|
||||||
|
|
||||||
|
const {
|
||||||
|
data
|
||||||
|
} = defineProps<{
|
||||||
|
data: {
|
||||||
|
reserved_names: string[],
|
||||||
|
category?: BingoCategory,
|
||||||
|
},
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const markdown_preview = ref(false);
|
||||||
|
|
||||||
|
const delete_label = ref('Delete (5)');
|
||||||
|
const delete_enabled = ref(false);
|
||||||
|
|
||||||
|
const has_goal_lists = computed(() => {
|
||||||
|
return data.category?.goal_lists?.length;
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (has_goal_lists) {
|
||||||
|
delete_label.value = 'Delete';
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let countdown = 5;
|
||||||
|
let interval = setInterval(() => {
|
||||||
|
if (--countdown <= 0) {
|
||||||
|
clearInterval(interval);
|
||||||
|
delete_label.value = 'Delete';
|
||||||
|
delete_enabled.value = true;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
delete_label.value = `Delete (${ countdown })`;
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
const delete_actually_enabled = computed(() => {
|
||||||
|
return delete_enabled.value && !has_goal_lists;
|
||||||
|
});
|
||||||
|
|
||||||
|
const category_name = ref('');
|
||||||
|
const category_description = ref('');
|
||||||
|
|
||||||
|
category_name.value = data.category?.name ?? '';
|
||||||
|
category_description.value = data.category?.description ?? '';
|
||||||
|
|
||||||
|
const name_error = computed(() => {
|
||||||
|
if (isNameReserved(category_name.value)) {
|
||||||
|
return 'Name is reserved';
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
});
|
||||||
|
function isNameReserved(name: string) {
|
||||||
|
return name.length && data.reserved_names.some(reserved_name =>
|
||||||
|
reserved_name !== data.category?.name
|
||||||
|
&& stringCompare(reserved_name, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
function canSave() {
|
||||||
|
return !isNameReserved(category_name.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancel() {
|
||||||
|
emit('cancel');
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveCategory() {
|
||||||
|
const category = new BingoCategory(category_name.value);
|
||||||
|
category.description = category_description.value;
|
||||||
|
|
||||||
|
// Create new category
|
||||||
|
if (!data.category) {
|
||||||
|
emit('createCategory', category);
|
||||||
|
}
|
||||||
|
// Update existing category
|
||||||
|
else {
|
||||||
|
category.goal_lists = data.category.goal_lists;
|
||||||
|
|
||||||
|
emit('updateCategory', data.category, category);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteCategory() {
|
||||||
|
emit('deleteCategory', data.category);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
@ -145,6 +145,14 @@
|
|||||||
@click="event => openEditCategoryDialog(prop.node, event)"
|
@click="event => openEditCategoryDialog(prop.node, event)"
|
||||||
/>
|
/>
|
||||||
<div>{{ prop.node.item?.name || prop.node.label }}</div>
|
<div>{{ prop.node.item?.name || prop.node.label }}</div>
|
||||||
|
<q-btn
|
||||||
|
v-if="prop.node.item?.description?.length"
|
||||||
|
icon="help_outline"
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
size="sm"
|
||||||
|
@click="event => openInfoDialog(prop.node.item?.description, event)"
|
||||||
|
></q-btn>
|
||||||
<q-badge
|
<q-badge
|
||||||
outline
|
outline
|
||||||
color="blue"
|
color="blue"
|
||||||
@ -312,6 +320,16 @@
|
|||||||
@delete-goal-list="delete_goal_list"
|
@delete-goal-list="delete_goal_list"
|
||||||
/>
|
/>
|
||||||
</q-dialog>
|
</q-dialog>
|
||||||
|
|
||||||
|
<q-dialog v-model="edit_category_dialog" persistent>
|
||||||
|
<EditCategoryDialog
|
||||||
|
:data="edit_category_data"
|
||||||
|
@cancel="edit_category_dialog = false"
|
||||||
|
@create-category="create_category"
|
||||||
|
@update-category="update_category"
|
||||||
|
@delete-category="delete_category"
|
||||||
|
/>
|
||||||
|
</q-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@ -573,10 +591,12 @@ const delete_goal_list = (goal_list: BingoGoalList) => {
|
|||||||
/* Edit Category Dialog */
|
/* Edit Category Dialog */
|
||||||
|
|
||||||
interface EditCategoryData {
|
interface EditCategoryData {
|
||||||
|
reserved_names: string[];
|
||||||
category?: BingoCategory;
|
category?: BingoCategory;
|
||||||
}
|
}
|
||||||
|
|
||||||
const edit_category_data = ref<EditCategoryData>({
|
const edit_category_data = ref<EditCategoryData>({
|
||||||
|
reserved_names: [] as string[]
|
||||||
});
|
});
|
||||||
|
|
||||||
const edit_category_dialog = ref(false);
|
const edit_category_dialog = ref(false);
|
||||||
@ -589,19 +609,23 @@ function openEditCategoryDialog(node: Node, event: Event) {
|
|||||||
edit_category_dialog.value = true;
|
edit_category_dialog.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const emit_category = (category: BingoCategory) => {
|
const create_category = (category: BingoCategory) => {
|
||||||
// Add new category
|
game.value?.items.push(category);
|
||||||
if (!edit_category_data.value.category) {
|
|
||||||
game.value?.items.push(category);
|
|
||||||
}
|
|
||||||
// Replace existing category
|
|
||||||
else {
|
|
||||||
const index = game.value?.items.findIndex(g => g === category);
|
|
||||||
game.value?.items.splice(index, 1, category);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset values
|
// Reset values
|
||||||
edit_category_data.value = {
|
edit_category_data.value = {
|
||||||
|
reserved_names: []
|
||||||
|
};
|
||||||
|
edit_category_dialog.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const update_category = (old_category: BingoCategory, category: BingoCategory) => {
|
||||||
|
const index = game.value?.items.findIndex(g => g === old_category);
|
||||||
|
game.value?.items.splice(index, 1, category);
|
||||||
|
|
||||||
|
// Reset values
|
||||||
|
edit_category_data.value = {
|
||||||
|
reserved_names: []
|
||||||
};
|
};
|
||||||
edit_category_dialog.value = false;
|
edit_category_dialog.value = false;
|
||||||
};
|
};
|
||||||
@ -612,10 +636,40 @@ const delete_category = (category: BingoCategory) => {
|
|||||||
|
|
||||||
// Reset values
|
// Reset values
|
||||||
edit_category_data.value = {
|
edit_category_data.value = {
|
||||||
|
reserved_names: []
|
||||||
};
|
};
|
||||||
edit_category_dialog.value = false;
|
edit_category_dialog.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// const emit_category = (category: BingoCategory) => {
|
||||||
|
// // Add new category
|
||||||
|
// if (!edit_category_data.value.category) {
|
||||||
|
// game.value?.items.push(category);
|
||||||
|
// }
|
||||||
|
// // Replace existing category
|
||||||
|
// else {
|
||||||
|
// const index = game.value?.items.findIndex(g => g === category);
|
||||||
|
// game.value?.items.splice(index, 1, category);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Reset values
|
||||||
|
// edit_category_data.value = {
|
||||||
|
// reserved_names: []
|
||||||
|
// };
|
||||||
|
// edit_category_dialog.value = false;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const delete_category = (category: BingoCategory) => {
|
||||||
|
// const index = game.value?.items.findIndex(g => g === category);
|
||||||
|
// game.value?.items.splice(index, 1);
|
||||||
|
|
||||||
|
// // Reset values
|
||||||
|
// edit_category_data.value = {
|
||||||
|
// reserved_names: []
|
||||||
|
// };
|
||||||
|
// edit_category_dialog.value = false;
|
||||||
|
// };
|
||||||
|
|
||||||
/* End Edit Category Dialog */
|
/* End Edit Category Dialog */
|
||||||
|
|
||||||
const nodes = computed(() => {
|
const nodes = computed(() => {
|
||||||
|
@ -8,6 +8,8 @@ import {
|
|||||||
export default class BingoCategory {
|
export default class BingoCategory {
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
|
description: string = '';
|
||||||
|
|
||||||
@Type(() => BingoGoalList)
|
@Type(() => BingoGoalList)
|
||||||
goal_lists: BingoGoalList[] = [];
|
goal_lists: BingoGoalList[] = [];
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user