Add proper edit functionality support
This commit is contained in:
parent
fd4adedcb3
commit
fcbdfd038f
@ -198,6 +198,31 @@ module.exports = {
|
|||||||
lang: 'ts'
|
lang: 'ts'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
'vue/max-attributes-per-line': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
singleline: {
|
||||||
|
max: 2
|
||||||
|
},
|
||||||
|
multiline: {
|
||||||
|
max: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'vue/first-attribute-linebreak': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
singleline: 'beside',
|
||||||
|
multiline: 'below'
|
||||||
|
}
|
||||||
|
],
|
||||||
|
'vue/html-closing-bracket-newline': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
singleline: 'never',
|
||||||
|
multiline: 'always'
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
7
components.d.ts
vendored
7
components.d.ts
vendored
@ -7,13 +7,18 @@ export {}
|
|||||||
|
|
||||||
declare module 'vue' {
|
declare module 'vue' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
|
copy: typeof import('./src/composables/EditGoalDialog copy.vue')['default']
|
||||||
EditGoal: typeof import('./src/composables/EditGoal.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']
|
||||||
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']
|
||||||
GameList: typeof import('./src/composables/GameList.vue')['default']
|
GameList: typeof import('./src/composables/GameList.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']
|
GoalEditorDialog: typeof import('./src/components/GoalEditorDialog.vue')['default']
|
||||||
MarkdownRenderer: typeof import('./src/components/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']
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
|
@ -22,11 +22,20 @@
|
|||||||
<q-separator />
|
<q-separator />
|
||||||
|
|
||||||
<q-card-actions>
|
<q-card-actions>
|
||||||
<q-btn flat color="green" label="Add" />
|
<q-btn
|
||||||
|
flat
|
||||||
|
color="green"
|
||||||
|
label="Add"
|
||||||
|
/>
|
||||||
|
|
||||||
<q-space/>
|
<q-space/>
|
||||||
|
|
||||||
<q-btn flat color="blue" icon="upload" label="Import" />
|
<q-btn
|
||||||
|
flat
|
||||||
|
color="blue"
|
||||||
|
icon="upload"
|
||||||
|
label="Import"
|
||||||
|
/>
|
||||||
</q-card-actions>
|
</q-card-actions>
|
||||||
</q-card>
|
</q-card>
|
||||||
</div>
|
</div>
|
||||||
@ -37,16 +46,29 @@
|
|||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="row items-center justify-evenly">
|
<div class="row items-center justify-evenly">
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<q-input filled v-model="search" label="Search for games..." />
|
<q-input
|
||||||
|
filled
|
||||||
|
v-model="search"
|
||||||
|
label="Search for games..."
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col" v-for="(game, index) of filtered_games" :key="game.id" :style="{ 'max-width': '500px' }">
|
<div
|
||||||
|
class="col"
|
||||||
|
v-for="(game, index) of filtered_games"
|
||||||
|
:key="game.id"
|
||||||
|
:style="{ 'max-width': '500px' }"
|
||||||
|
>
|
||||||
<q-card flat bordered>
|
<q-card flat bordered>
|
||||||
<q-card-section horizontal>
|
<q-card-section horizontal>
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
<q-badge v-if="game.is_local" outline color="orange">
|
<q-badge
|
||||||
|
v-if="game.is_local"
|
||||||
|
outline
|
||||||
|
color="orange"
|
||||||
|
>
|
||||||
<div class="text-weight-bold q-my-xs">Local Game</div>
|
<div class="text-weight-bold q-my-xs">Local Game</div>
|
||||||
</q-badge>
|
</q-badge>
|
||||||
|
|
||||||
@ -72,18 +94,34 @@
|
|||||||
<q-separator />
|
<q-separator />
|
||||||
|
|
||||||
<q-card-actions>
|
<q-card-actions>
|
||||||
<q-btn flat icon="help" @click="selected_game = game">
|
<q-btn
|
||||||
|
flat
|
||||||
|
icon="help"
|
||||||
|
@click="learnMore(game)"
|
||||||
|
>
|
||||||
<q-tooltip>
|
<q-tooltip>
|
||||||
Learn more...
|
Learn more...
|
||||||
</q-tooltip>
|
</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
<template v-if="game.is_local">
|
<template v-if="game.is_local">
|
||||||
<q-btn flat icon="edit" color="green" :loading="loading">
|
<q-btn
|
||||||
|
flat
|
||||||
|
icon="edit"
|
||||||
|
color="green"
|
||||||
|
:loading="loading"
|
||||||
|
@click="editGame(game)"
|
||||||
|
>
|
||||||
<q-tooltip>
|
<q-tooltip>
|
||||||
Edit
|
Edit
|
||||||
</q-tooltip>
|
</q-tooltip>
|
||||||
</q-btn>
|
</q-btn>
|
||||||
<q-btn flat icon="delete" color="red" :loading="loading" @click="deleteGameInternal(game.id)">
|
<q-btn
|
||||||
|
flat
|
||||||
|
icon="delete"
|
||||||
|
color="red"
|
||||||
|
:loading="loading"
|
||||||
|
@click="deleteGameInternal(game.id)"
|
||||||
|
>
|
||||||
<q-tooltip>
|
<q-tooltip>
|
||||||
Delete
|
Delete
|
||||||
</q-tooltip>
|
</q-tooltip>
|
||||||
@ -91,10 +129,22 @@
|
|||||||
|
|
||||||
<q-space/>
|
<q-space/>
|
||||||
|
|
||||||
<q-btn flat color="blue" icon="download" label="Export" />
|
<q-btn
|
||||||
|
flat
|
||||||
|
color="blue"
|
||||||
|
icon="download"
|
||||||
|
label="Export"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<q-btn flat icon="content_copy" color="green" :loading="loading" :disabled="localGames.hasGame(game.id)" @click="saveGameInternal(game)">
|
<q-btn
|
||||||
|
flat
|
||||||
|
icon="content_copy"
|
||||||
|
color="green"
|
||||||
|
:loading="loading"
|
||||||
|
:disabled="localGames.hasGame(game.id)"
|
||||||
|
@click="cloneGame(game)"
|
||||||
|
>
|
||||||
<q-tooltip>
|
<q-tooltip>
|
||||||
Copy to local
|
Copy to local
|
||||||
</q-tooltip>
|
</q-tooltip>
|
||||||
@ -110,7 +160,11 @@
|
|||||||
<q-card style="min-width: 700px; max-width: 80vw; min-height: 10vh; max-height: 80vh;">
|
<q-card style="min-width: 700px; max-width: 80vw; min-height: 10vh; max-height: 80vh;">
|
||||||
<q-card-section class="row items-start q-col-gutter-md">
|
<q-card-section class="row items-start q-col-gutter-md">
|
||||||
<div class="col-2">
|
<div class="col-2">
|
||||||
<q-avatar square size="200px" style="width: 100%; height: auto; aspect-ratio: 1/1;">
|
<q-avatar
|
||||||
|
square
|
||||||
|
size="200px"
|
||||||
|
style="width: 100%; height: auto; aspect-ratio: 1/1;"
|
||||||
|
>
|
||||||
<q-img
|
<q-img
|
||||||
src="https://picsum.photos/200/200"
|
src="https://picsum.photos/200/200"
|
||||||
width="100%"
|
width="100%"
|
||||||
@ -131,6 +185,18 @@
|
|||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
</q-card>
|
||||||
</q-dialog>
|
</q-dialog>
|
||||||
|
|
||||||
|
<q-dialog
|
||||||
|
v-model="edit_game"
|
||||||
|
persistent
|
||||||
|
>
|
||||||
|
<GameEditorDialog
|
||||||
|
v-if="selected_game"
|
||||||
|
:game="selected_game"
|
||||||
|
@cancel="edit_game = false"
|
||||||
|
@save-game="updateGame"
|
||||||
|
/>
|
||||||
|
</q-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@ -154,12 +220,31 @@ onMounted(async() => {
|
|||||||
await localGames.fetchGames();
|
await localGames.fetchGames();
|
||||||
});
|
});
|
||||||
|
|
||||||
async function saveGameInternal(game: BingoGame) {
|
const edit_game = ref(false);
|
||||||
|
|
||||||
|
function editGame(game: BingoGame) {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
|
||||||
localGames.addGame(game);
|
selected_game.value = game;
|
||||||
// await saveGame(game);
|
edit_game.value = true;
|
||||||
// local_games.value = await getLocalGames();
|
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateGame(game: BingoGame) {
|
||||||
|
loading.value = true;
|
||||||
|
|
||||||
|
await localGames.updateGame(game);
|
||||||
|
|
||||||
|
edit_game.value = false;
|
||||||
|
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function cloneGame(game: BingoGame) {
|
||||||
|
loading.value = true;
|
||||||
|
|
||||||
|
await localGames.addGame(game);
|
||||||
|
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
@ -167,7 +252,7 @@ async function saveGameInternal(game: BingoGame) {
|
|||||||
async function deleteGameInternal(id: string) {
|
async function deleteGameInternal(id: string) {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
|
||||||
localGames.deleteGame(id);
|
await localGames.deleteGame(id);
|
||||||
|
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}
|
}
|
||||||
@ -193,15 +278,8 @@ const filtered_games = computed(() => {
|
|||||||
return combined_games.value.filter(game => game.name.toLocaleLowerCase().includes(search.value.toLocaleLowerCase()));
|
return combined_games.value.filter(game => game.name.toLocaleLowerCase().includes(search.value.toLocaleLowerCase()));
|
||||||
});
|
});
|
||||||
|
|
||||||
// On selected game change, open popup
|
function learnMore(game: BingoGame) {
|
||||||
watch(selected_game, newGame => {
|
selected_game.value = game;
|
||||||
if (newGame)
|
learn_more.value = true;
|
||||||
learn_more.value = true;
|
}
|
||||||
});
|
|
||||||
|
|
||||||
// On popup close, reset selected game
|
|
||||||
watch(learn_more, newValue => {
|
|
||||||
if (!newValue)
|
|
||||||
selected_game.value = undefined;
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
@ -9,9 +9,22 @@
|
|||||||
>
|
>
|
||||||
<template v-slot:header-goal="prop">
|
<template v-slot:header-goal="prop">
|
||||||
<div class="row items-center">
|
<div class="row items-center">
|
||||||
<q-btn v-if="editMode" icon="edit" flat round size="sm" color="orange"/>
|
<q-btn
|
||||||
|
v-if="editMode"
|
||||||
|
icon="edit"
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
size="sm"
|
||||||
|
color="orange"
|
||||||
|
/>
|
||||||
<div>{{ prop.node.label }}</div>
|
<div>{{ prop.node.label }}</div>
|
||||||
<q-badge outline v-for="tag in prop.node.goal.tags" :key="tag" :color="getColorForTag(tag)" class="q-ml-sm">
|
<q-badge
|
||||||
|
outline
|
||||||
|
v-for="tag in prop.node.goal.tags"
|
||||||
|
:key="tag"
|
||||||
|
:color="getColorForTag(tag)"
|
||||||
|
class="q-ml-sm"
|
||||||
|
>
|
||||||
<div class="text-weight-bold q-my-xs">{{ tag }}</div>
|
<div class="text-weight-bold q-my-xs">{{ tag }}</div>
|
||||||
</q-badge>
|
</q-badge>
|
||||||
</div>
|
</div>
|
||||||
@ -19,9 +32,20 @@
|
|||||||
|
|
||||||
<template v-slot:header-goal-list="prop">
|
<template v-slot:header-goal-list="prop">
|
||||||
<div class="row items-center">
|
<div class="row items-center">
|
||||||
<q-btn v-if="editMode" icon="edit" flat round size="sm" color="orange"/>
|
<q-btn
|
||||||
|
v-if="editMode"
|
||||||
|
icon="edit"
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
size="sm"
|
||||||
|
color="orange"
|
||||||
|
/>
|
||||||
<div>{{ prop.node.label }}</div>
|
<div>{{ prop.node.label }}</div>
|
||||||
<q-badge outline color="orange" class="q-ml-sm">
|
<q-badge
|
||||||
|
outline
|
||||||
|
color="orange"
|
||||||
|
class="q-ml-sm"
|
||||||
|
>
|
||||||
<div class="text-weight-bold q-my-xs">Goal List</div>
|
<div class="text-weight-bold q-my-xs">Goal List</div>
|
||||||
</q-badge>
|
</q-badge>
|
||||||
</div>
|
</div>
|
||||||
@ -29,9 +53,20 @@
|
|||||||
|
|
||||||
<template v-slot:header-category="prop">
|
<template v-slot:header-category="prop">
|
||||||
<div class="row items-center">
|
<div class="row items-center">
|
||||||
<q-btn v-if="editMode" icon="edit" flat round size="sm" color="orange"/>
|
<q-btn
|
||||||
|
v-if="editMode"
|
||||||
|
icon="edit"
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
size="sm"
|
||||||
|
color="orange"
|
||||||
|
/>
|
||||||
<div>{{ prop.node.label }}</div>
|
<div>{{ prop.node.label }}</div>
|
||||||
<q-badge outline color="blue" class="q-ml-sm">
|
<q-badge
|
||||||
|
outline
|
||||||
|
color="blue"
|
||||||
|
class="q-ml-sm"
|
||||||
|
>
|
||||||
<div class="text-weight-bold q-my-xs">Category</div>
|
<div class="text-weight-bold q-my-xs">Category</div>
|
||||||
</q-badge>
|
</q-badge>
|
||||||
</div>
|
</div>
|
||||||
@ -40,21 +75,39 @@
|
|||||||
<!-- Add Goal -->
|
<!-- Add Goal -->
|
||||||
<template v-slot:header-add-goal="prop">
|
<template v-slot:header-add-goal="prop">
|
||||||
<div class="row items-center">
|
<div class="row items-center">
|
||||||
<q-btn outline icon="add" label="Goal" color="light-blue" size="sm"></q-btn>
|
<q-btn
|
||||||
|
outline
|
||||||
|
icon="add"
|
||||||
|
label="Goal"
|
||||||
|
color="light-blue"
|
||||||
|
size="sm"
|
||||||
|
></q-btn>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- Add Goal List -->
|
<!-- Add Goal List -->
|
||||||
<template v-slot:header-add-goal-list="prop">
|
<template v-slot:header-add-goal-list="prop">
|
||||||
<div class="row items-center">
|
<div class="row items-center">
|
||||||
<q-btn outline icon="add" label="Goal List" color="light-blue" size="sm"></q-btn>
|
<q-btn
|
||||||
|
outline
|
||||||
|
icon="add"
|
||||||
|
label="Goal List"
|
||||||
|
color="light-blue"
|
||||||
|
size="sm"
|
||||||
|
></q-btn>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- Add Category -->
|
<!-- Add Category -->
|
||||||
<template v-slot:header-add-category="prop">
|
<template v-slot:header-add-category="prop">
|
||||||
<div class="row items-center">
|
<div class="row items-center">
|
||||||
<q-btn outline icon="add" label="Category" color="light-blue" size="sm"></q-btn>
|
<q-btn
|
||||||
|
outline
|
||||||
|
icon="add"
|
||||||
|
label="Category"
|
||||||
|
color="light-blue"
|
||||||
|
size="sm"
|
||||||
|
></q-btn>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</q-tree>
|
</q-tree>
|
||||||
@ -126,8 +179,6 @@ const selectByTag = (tag: string) => {
|
|||||||
ticked.value.push(...nodes.value.filter(node => {
|
ticked.value.push(...nodes.value.filter(node => {
|
||||||
return getAllGoals(node).map(goal => goal.tags.includes(tag));
|
return getAllGoals(node).map(goal => goal.tags.includes(tag));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
console.log(ticked.value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const nodes = computed(() => {
|
const nodes = computed(() => {
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<q-card flat bordered style="min-width: 700px; max-width: 80vw; min-height: 10vh; max-height: 80vh;">
|
<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-card-section class="q-gutter-y-md column">
|
||||||
<q-input
|
<q-input
|
||||||
v-model="goal_name"
|
v-model="goal_name"
|
||||||
@ -13,6 +17,28 @@
|
|||||||
type="text"
|
type="text"
|
||||||
></q-input>
|
></q-input>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
class="col-10"
|
||||||
|
v-model="goal_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-select
|
<q-select
|
||||||
label="Select Tags"
|
label="Select Tags"
|
||||||
square
|
square
|
||||||
@ -22,6 +48,7 @@
|
|||||||
use-chips
|
use-chips
|
||||||
multiple
|
multiple
|
||||||
clearable
|
clearable
|
||||||
|
hint="Optional"
|
||||||
hide-dropdown-icon
|
hide-dropdown-icon
|
||||||
input-debounce="0"
|
input-debounce="0"
|
||||||
:options="all_tags"
|
:options="all_tags"
|
||||||
@ -32,14 +59,46 @@
|
|||||||
<q-separator />
|
<q-separator />
|
||||||
|
|
||||||
<q-card-actions>
|
<q-card-actions>
|
||||||
<q-btn v-if="data.goal" flat color="red" icon="delete" :label="delete_label" @click="deleteGoal()" :disabled="!delete_enabled" />
|
<q-btn
|
||||||
|
v-if="data.goal"
|
||||||
|
flat
|
||||||
|
color="red"
|
||||||
|
icon="delete"
|
||||||
|
:label="delete_label"
|
||||||
|
@click="deleteGoal()"
|
||||||
|
:disabled="!delete_enabled"
|
||||||
|
/>
|
||||||
|
|
||||||
<q-space/>
|
<q-space/>
|
||||||
|
|
||||||
<q-btn flat color="red" icon="cancel" label="Cancel" @click="cancel()" />
|
<q-btn
|
||||||
<q-btn flat color="green" icon="save" label="Save" @click="emitGoal()" :disabled="!canSave()"/>
|
flat
|
||||||
|
color="red"
|
||||||
|
icon="cancel"
|
||||||
|
label="Cancel"
|
||||||
|
@click="cancel()"
|
||||||
|
/>
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
color="green"
|
||||||
|
icon="save"
|
||||||
|
label="Save"
|
||||||
|
@click="saveGoal()"
|
||||||
|
:disabled="!canSave()"
|
||||||
|
/>
|
||||||
</q-card-actions>
|
</q-card-actions>
|
||||||
</q-card>
|
</q-card>
|
||||||
|
|
||||||
|
<q-dialog v-model="markdown_preview">
|
||||||
|
<q-card
|
||||||
|
flat
|
||||||
|
bordered
|
||||||
|
>
|
||||||
|
<q-card-section>
|
||||||
|
<MarkdownRenderer :text="goal_description"/>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@ -47,13 +106,12 @@ import BingoGoal from '@/js/lib/BingoGoal.ts';
|
|||||||
import {
|
import {
|
||||||
stringCompare
|
stringCompare
|
||||||
} from '@/js/lib/Util.ts';
|
} from '@/js/lib/Util.ts';
|
||||||
import type BingoCategory from '@/js/lib/BingoCategory.ts';
|
|
||||||
import type BingoGoalList from '@/js/lib/BingoGoalList.ts';
|
|
||||||
|
|
||||||
const emit = defineEmits([
|
const emit = defineEmits([
|
||||||
'cancel',
|
'cancel',
|
||||||
'deleteGoal',
|
'createGoal',
|
||||||
'emitGoal'
|
'updateGoal',
|
||||||
|
'deleteGoal'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -61,16 +119,14 @@ const {
|
|||||||
} = defineProps<{
|
} = defineProps<{
|
||||||
data: {
|
data: {
|
||||||
all_tags: string[],
|
all_tags: string[],
|
||||||
|
reserved_names: string[],
|
||||||
goal?: BingoGoal,
|
goal?: BingoGoal,
|
||||||
},
|
},
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const goal_name = ref('');
|
|
||||||
const all_tags: Ref<string[]> = ref([ ...data.all_tags ]);
|
const all_tags: Ref<string[]> = ref([ ...data.all_tags ]);
|
||||||
const goal_tags: Ref<string[]> = ref([ ]);
|
|
||||||
|
|
||||||
goal_name.value = data.goal?.name ?? '';
|
const markdown_preview = ref(false);
|
||||||
goal_tags.value = data.goal?.tags ?? [];
|
|
||||||
|
|
||||||
const delete_label = ref('Delete (5)');
|
const delete_label = ref('Delete (5)');
|
||||||
const delete_enabled = ref(false);
|
const delete_enabled = ref(false);
|
||||||
@ -91,8 +147,17 @@ onMounted(() => {
|
|||||||
}, 1000);
|
}, 1000);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const goal_name = ref('');
|
||||||
|
const goal_description = ref('');
|
||||||
|
const goal_tags: Ref<string[]> = ref([ ]);
|
||||||
|
|
||||||
|
goal_name.value = data.goal?.name ?? '';
|
||||||
|
goal_description.value = data.goal?.description ?? '';
|
||||||
|
goal_tags.value = data.goal?.tags ?? [];
|
||||||
|
|
||||||
function canSave() {
|
function canSave() {
|
||||||
return goal_name.value?.length > 0;
|
return goal_name.value?.length > 0
|
||||||
|
&& !data.reserved_names.some(name => name !== data.goal?.name && stringCompare(name, goal_name.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
function addTag(value: string, done: Function) {
|
function addTag(value: string, done: Function) {
|
||||||
@ -111,24 +176,29 @@ function addTag(value: string, done: Function) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
done(value, 'toggle');
|
done(value, 'toggle');
|
||||||
|
|
||||||
console.log('All tags', all_tags);
|
|
||||||
console.log('Goal tags', goal_tags);
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteGoal() {
|
|
||||||
emit('deleteGoal', data.goal);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function cancel() {
|
function cancel() {
|
||||||
emit('cancel');
|
emit('cancel');
|
||||||
}
|
}
|
||||||
|
|
||||||
function emitGoal() {
|
function saveGoal() {
|
||||||
const goal = new BingoGoal(goal_name.value);
|
const goal = new BingoGoal(goal_name.value);
|
||||||
|
goal.description = goal_description.value;
|
||||||
goal.tags = goal_tags.value;
|
goal.tags = goal_tags.value;
|
||||||
|
|
||||||
emit('emitGoal', goal);
|
// Create new goal
|
||||||
|
if (!data.goal) {
|
||||||
|
emit('createGoal', goal);
|
||||||
|
}
|
||||||
|
// Update existing goal
|
||||||
|
else {
|
||||||
|
emit('updateGoal', data.goal, goal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteGoal() {
|
||||||
|
emit('deleteGoal', data.goal);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
204
src/composables/EditGoalListDialog.vue
Normal file
204
src/composables/EditGoalListDialog.vue
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
|
||||||
|
<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="goal_name"
|
||||||
|
square
|
||||||
|
filled
|
||||||
|
counter
|
||||||
|
clearable
|
||||||
|
maxlength="40"
|
||||||
|
label="Name"
|
||||||
|
type="text"
|
||||||
|
></q-input>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
class="col-10"
|
||||||
|
v-model="goal_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-select
|
||||||
|
label="Select Tags"
|
||||||
|
square
|
||||||
|
filled
|
||||||
|
v-model="goal_tags"
|
||||||
|
use-input
|
||||||
|
use-chips
|
||||||
|
multiple
|
||||||
|
clearable
|
||||||
|
hint="Optional"
|
||||||
|
hide-dropdown-icon
|
||||||
|
input-debounce="0"
|
||||||
|
:options="all_tags"
|
||||||
|
@new-value="addTag"
|
||||||
|
></q-select>
|
||||||
|
</q-card-section>
|
||||||
|
|
||||||
|
<q-separator />
|
||||||
|
|
||||||
|
<q-card-actions>
|
||||||
|
<q-btn
|
||||||
|
v-if="data.goal"
|
||||||
|
flat
|
||||||
|
color="red"
|
||||||
|
icon="delete"
|
||||||
|
:label="delete_label"
|
||||||
|
@click="deleteGoal()"
|
||||||
|
:disabled="!delete_enabled"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-space/>
|
||||||
|
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
color="red"
|
||||||
|
icon="cancel"
|
||||||
|
label="Cancel"
|
||||||
|
@click="cancel()"
|
||||||
|
/>
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
color="green"
|
||||||
|
icon="save"
|
||||||
|
label="Save"
|
||||||
|
@click="saveGoal()"
|
||||||
|
:disabled="!canSave()"
|
||||||
|
/>
|
||||||
|
</q-card-actions>
|
||||||
|
</q-card>
|
||||||
|
|
||||||
|
<q-dialog v-model="markdown_preview">
|
||||||
|
<q-card
|
||||||
|
flat
|
||||||
|
bordered
|
||||||
|
>
|
||||||
|
<q-card-section>
|
||||||
|
<MarkdownRenderer :text="goal_description"/>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import BingoGoal from '@/js/lib/BingoGoal.ts';
|
||||||
|
import {
|
||||||
|
stringCompare
|
||||||
|
} from '@/js/lib/Util.ts';
|
||||||
|
|
||||||
|
const emit = defineEmits([
|
||||||
|
'cancel',
|
||||||
|
'createGoal',
|
||||||
|
'updateGoal',
|
||||||
|
'deleteGoal'
|
||||||
|
]);
|
||||||
|
|
||||||
|
const {
|
||||||
|
data
|
||||||
|
} = defineProps<{
|
||||||
|
data: {
|
||||||
|
all_tags: string[],
|
||||||
|
reserved_names: string[],
|
||||||
|
goal?: BingoGoal,
|
||||||
|
},
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const all_tags: Ref<string[]> = ref([ ...data.all_tags ]);
|
||||||
|
|
||||||
|
const markdown_preview = ref(false);
|
||||||
|
|
||||||
|
const delete_label = ref('Delete (5)');
|
||||||
|
const delete_enabled = ref(false);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
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 goal_name = ref('');
|
||||||
|
const goal_description = ref('');
|
||||||
|
const goal_tags: Ref<string[]> = ref([ ]);
|
||||||
|
|
||||||
|
goal_name.value = data.goal?.name ?? '';
|
||||||
|
goal_description.value = data.goal?.description ?? '';
|
||||||
|
goal_tags.value = data.goal?.tags ?? [];
|
||||||
|
|
||||||
|
function canSave() {
|
||||||
|
return goal_name.value?.length > 0
|
||||||
|
&& !data.reserved_names.some(name => name !== data.goal?.name && stringCompare(name, goal_name.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
function addTag(value: string, done: Function) {
|
||||||
|
// Exit if tag already exists
|
||||||
|
if (goal_tags.value.some(tag => stringCompare(tag, value))) {
|
||||||
|
done();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tag = all_tags.value.find(tag => stringCompare(tag, value));
|
||||||
|
value = tag ?? value;
|
||||||
|
|
||||||
|
if (!data.all_tags.some(tag => stringCompare(tag, value))) {
|
||||||
|
all_tags.value.push(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
done(value, 'toggle');
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancel() {
|
||||||
|
emit('cancel');
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveGoal() {
|
||||||
|
const goal = new BingoGoal(goal_name.value);
|
||||||
|
goal.description = goal_description.value;
|
||||||
|
goal.tags = goal_tags.value;
|
||||||
|
|
||||||
|
// Create new goal
|
||||||
|
if (!data.goal) {
|
||||||
|
emit('createGoal', goal);
|
||||||
|
}
|
||||||
|
// Update existing goal
|
||||||
|
else {
|
||||||
|
emit('updateGoal', data.goal, goal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteGoal() {
|
||||||
|
emit('deleteGoal', data.goal);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
517
src/composables/GameEditorDialog.vue
Normal file
517
src/composables/GameEditorDialog.vue
Normal file
@ -0,0 +1,517 @@
|
|||||||
|
<template>
|
||||||
|
<q-card>
|
||||||
|
<q-card-section>
|
||||||
|
<q-tree
|
||||||
|
:nodes="nodes"
|
||||||
|
node-key="label"
|
||||||
|
v-model:expanded="expanded"
|
||||||
|
>
|
||||||
|
<template v-slot:header-goal="prop">
|
||||||
|
<div class="row items-center">
|
||||||
|
<q-btn
|
||||||
|
icon="edit"
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
size="sm"
|
||||||
|
color="orange"
|
||||||
|
@click="openEditGoalDialog(prop.node)"
|
||||||
|
/>
|
||||||
|
<div>{{ prop.node.item?.name || prop.node.label }}</div>
|
||||||
|
<q-btn
|
||||||
|
v-if="prop.node.goal?.description?.length"
|
||||||
|
icon="help_outline"
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
size="sm"
|
||||||
|
@click="openInfoDialog(prop.node.goal.description)"
|
||||||
|
></q-btn>
|
||||||
|
<q-badge
|
||||||
|
outline
|
||||||
|
v-for="tag in prop.node.goal.tags"
|
||||||
|
:key="tag"
|
||||||
|
:color="getColorForString(tag)"
|
||||||
|
class="q-ml-sm"
|
||||||
|
>
|
||||||
|
<div class="text-weight-bold q-my-xs">{{ tag }}</div>
|
||||||
|
</q-badge>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:header-goal-list="prop">
|
||||||
|
<div class="row items-center">
|
||||||
|
<q-btn
|
||||||
|
icon="edit"
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
size="sm"
|
||||||
|
color="orange"
|
||||||
|
@click="event => openEditGoalListDialog(prop.node, event)"
|
||||||
|
/>
|
||||||
|
<div>{{ prop.node.item?.name || prop.node.label }}</div>
|
||||||
|
<q-badge
|
||||||
|
outline
|
||||||
|
color="orange"
|
||||||
|
class="q-ml-sm"
|
||||||
|
>
|
||||||
|
<div class="text-weight-bold q-my-xs">Goal List</div>
|
||||||
|
</q-badge>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:header-category="prop">
|
||||||
|
<div class="row items-center">
|
||||||
|
<q-btn
|
||||||
|
icon="edit"
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
size="sm"
|
||||||
|
color="orange"
|
||||||
|
@click="event => openEditCategoryDialog(prop.node, event)"
|
||||||
|
/>
|
||||||
|
<div>{{ prop.node.item?.name || prop.node.label }}</div>
|
||||||
|
<q-badge
|
||||||
|
outline
|
||||||
|
color="blue"
|
||||||
|
class="q-ml-sm"
|
||||||
|
>
|
||||||
|
<div class="text-weight-bold q-my-xs">Category</div>
|
||||||
|
</q-badge>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- Add Goal -->
|
||||||
|
<template v-slot:header-add-goal="prop">
|
||||||
|
<div class="row items-center">
|
||||||
|
<q-btn
|
||||||
|
outline
|
||||||
|
icon="add"
|
||||||
|
label="Goal"
|
||||||
|
color="light-blue"
|
||||||
|
size="sm"
|
||||||
|
@click="openEditGoalDialog(prop.node)"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- Add Goal List -->
|
||||||
|
<template v-slot:header-add-goal-list="prop">
|
||||||
|
<div class="row items-center">
|
||||||
|
<q-btn
|
||||||
|
outline
|
||||||
|
icon="add"
|
||||||
|
label="Goal List"
|
||||||
|
color="light-blue"
|
||||||
|
size="sm"
|
||||||
|
@click="event => openEditGoalListDialog(prop.node, event)"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- Add Category -->
|
||||||
|
<template v-slot:header-add-category="prop">
|
||||||
|
<div class="row items-center">
|
||||||
|
<q-btn
|
||||||
|
outline
|
||||||
|
icon="add"
|
||||||
|
label="Category"
|
||||||
|
color="light-blue"
|
||||||
|
size="sm"
|
||||||
|
@click="event => openEditCategoryDialog(prop.node, event)"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</q-tree>
|
||||||
|
</q-card-section>
|
||||||
|
|
||||||
|
<q-separator />
|
||||||
|
|
||||||
|
<q-card-actions>
|
||||||
|
<q-space/>
|
||||||
|
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
color="red"
|
||||||
|
icon="cancel"
|
||||||
|
label="Cancel"
|
||||||
|
:loading="loading"
|
||||||
|
@click="cancel"
|
||||||
|
/>
|
||||||
|
<q-btn
|
||||||
|
flat
|
||||||
|
color="green"
|
||||||
|
icon="save"
|
||||||
|
label="Save"
|
||||||
|
:loading="loading"
|
||||||
|
@click="saveGame"
|
||||||
|
/>
|
||||||
|
</q-card-actions>
|
||||||
|
</q-card>
|
||||||
|
|
||||||
|
<q-dialog v-model="info_dialog">
|
||||||
|
<q-card
|
||||||
|
flat
|
||||||
|
bordered
|
||||||
|
>
|
||||||
|
<q-card-section>
|
||||||
|
<MarkdownRenderer :text="info_dialog_text"/>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog>
|
||||||
|
|
||||||
|
<q-dialog v-model="edit_goal_dialog" persistent>
|
||||||
|
<EditGoalDialog
|
||||||
|
:data="edit_goal_data"
|
||||||
|
@cancel="edit_goal_dialog = false"
|
||||||
|
@create-goal="create_goal"
|
||||||
|
@update-goal="update_goal"
|
||||||
|
@delete-goal="delete_goal"
|
||||||
|
/>
|
||||||
|
</q-dialog>
|
||||||
|
|
||||||
|
<!-- <q-dialog v-model="edit_goal_list_dialog" persistent>
|
||||||
|
<EditGoalListDialog
|
||||||
|
:data="edit_goal_list_data"
|
||||||
|
@cancel="edit_goal_list_dialog = false"
|
||||||
|
@delete-goal-list="delete_goal_list"
|
||||||
|
@emit-goal-list="emit_goal_list"
|
||||||
|
/>
|
||||||
|
</q-dialog> -->
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
interface Node {
|
||||||
|
label: string;
|
||||||
|
item?: BingoGoal | BingoGoalList | BingoCategory;
|
||||||
|
header?: string; // 'goal' | 'goal-list' | 'category' | 'add-goal' | 'add-goal-list' | 'add-category'
|
||||||
|
children?: Node[];
|
||||||
|
goal?: BingoGoal;
|
||||||
|
parent?: BingoCategory | BingoGoalList;
|
||||||
|
}
|
||||||
|
|
||||||
|
import {
|
||||||
|
getColorForString
|
||||||
|
} from '@/js/lib/Util.ts';
|
||||||
|
|
||||||
|
import BingoCategory from '@/js/lib/BingoCategory.ts';
|
||||||
|
import type BingoGame from '@/js/lib/BingoGame.ts';
|
||||||
|
import BingoGoal from '@/js/lib/BingoGoal.ts';
|
||||||
|
import BingoGoalList from '@/js/lib/BingoGoalList.ts';
|
||||||
|
import Parser from '@/js/lib/Parser.ts';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
game: BingoGame;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits([
|
||||||
|
'cancel',
|
||||||
|
'saveGame'
|
||||||
|
]);
|
||||||
|
|
||||||
|
const game = ref(Parser.getCopy(props.game));
|
||||||
|
game.value.is_local = true;
|
||||||
|
|
||||||
|
const expanded = ref<Node[]>([]);
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
function cancel() {
|
||||||
|
loading.value = true;
|
||||||
|
|
||||||
|
emit('cancel');
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveGame() {
|
||||||
|
loading.value = true;
|
||||||
|
|
||||||
|
emit('saveGame', game.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Info Dialog */
|
||||||
|
const info_dialog = ref(false);
|
||||||
|
const info_dialog_text = ref('');
|
||||||
|
|
||||||
|
function openInfoDialog(text: string) {
|
||||||
|
info_dialog_text.value = text;
|
||||||
|
info_dialog.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* End Info Dialog */
|
||||||
|
|
||||||
|
/* Edit Goal Dialog */
|
||||||
|
interface EditGoalData {
|
||||||
|
goal?: BingoGoal;
|
||||||
|
all_tags: string[];
|
||||||
|
reserved_names: string[];
|
||||||
|
parent_group: BingoCategory | BingoGoalList | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const edit_goal_data = ref<EditGoalData>({
|
||||||
|
all_tags: [] as string[],
|
||||||
|
reserved_names: [] as string[],
|
||||||
|
parent_group: undefined
|
||||||
|
});
|
||||||
|
const edit_goal_dialog = ref(false);
|
||||||
|
const openEditGoalDialog = (node: Node) => {
|
||||||
|
edit_goal_data.value.goal = node.goal;
|
||||||
|
edit_goal_data.value.reserved_names = game.value?.reserved_names ?? [];
|
||||||
|
edit_goal_data.value.parent_group = node.parent;
|
||||||
|
edit_goal_data.value.all_tags = game.value?.getAllTags() ?? [];
|
||||||
|
edit_goal_dialog.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const create_goal = (goal: BingoGoal) => {
|
||||||
|
// We always assume parent group is a goal list. You can't add goals to categories.
|
||||||
|
const goal_list = edit_goal_data.value.parent_group as BingoGoalList;
|
||||||
|
|
||||||
|
goal_list.goals.push(goal);
|
||||||
|
|
||||||
|
// Reset values
|
||||||
|
edit_goal_data.value = {
|
||||||
|
goal: undefined,
|
||||||
|
all_tags: [],
|
||||||
|
reserved_names: [],
|
||||||
|
parent_group: undefined
|
||||||
|
};
|
||||||
|
edit_goal_dialog.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const update_goal = (old_goal: BingoGoal, goal: BingoGoal) => {
|
||||||
|
// We always assume parent group is a goal list. You can't add goals to categories.
|
||||||
|
const goal_list = edit_goal_data.value.parent_group as BingoGoalList;
|
||||||
|
|
||||||
|
const index = goal_list.goals.findIndex(g => g === old_goal);
|
||||||
|
goal_list.goals.splice(index, 1, goal);
|
||||||
|
|
||||||
|
// Reset values
|
||||||
|
edit_goal_data.value = {
|
||||||
|
goal: undefined,
|
||||||
|
all_tags: [],
|
||||||
|
reserved_names: [],
|
||||||
|
parent_group: undefined
|
||||||
|
};
|
||||||
|
edit_goal_dialog.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const delete_goal = (goal: BingoGoal) => {
|
||||||
|
// We always assume parent group is a goal list. You can't add goals to categories.
|
||||||
|
const goal_list = edit_goal_data.value.parent_group as BingoGoalList;
|
||||||
|
|
||||||
|
const index = goal_list.goals.findIndex(g => g === goal);
|
||||||
|
goal_list.goals.splice(index, 1);
|
||||||
|
|
||||||
|
// Reset values
|
||||||
|
edit_goal_data.value = {
|
||||||
|
goal: undefined,
|
||||||
|
all_tags: [],
|
||||||
|
reserved_names: [],
|
||||||
|
parent_group: undefined
|
||||||
|
};
|
||||||
|
edit_goal_dialog.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* End Edit Goal Dialog */
|
||||||
|
|
||||||
|
/* Edit Goal List Dialog */
|
||||||
|
interface EditGoalListData {
|
||||||
|
goal_list?: BingoGoalList;
|
||||||
|
parent_group: BingoCategory | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const edit_goal_list_data = ref<EditGoalListData>({
|
||||||
|
parent_group: undefined
|
||||||
|
});
|
||||||
|
|
||||||
|
const edit_goal_list_dialog = ref(false);
|
||||||
|
|
||||||
|
function openEditGoalListDialog(node: Node, event: Event) {
|
||||||
|
// Stop expanding of the tree
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
edit_goal_list_data.value.goal_list = node.item as BingoGoalList;
|
||||||
|
edit_goal_list_data.value.parent_group = node.parent as BingoCategory;
|
||||||
|
edit_goal_list_dialog.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const emit_goal_list = (goal_list: BingoGoalList) => {
|
||||||
|
// We always assume parent group is a category. You can't add goal lists to goal lists.
|
||||||
|
const category = edit_goal_list_data.value.parent_group as BingoCategory;
|
||||||
|
|
||||||
|
// Add new goal list
|
||||||
|
if (!edit_goal_list_data.value.goal_list) {
|
||||||
|
category.goal_lists.push(goal_list);
|
||||||
|
}
|
||||||
|
// Replace existing goal list
|
||||||
|
else {
|
||||||
|
const index = category.goal_lists.findIndex(g => g === goal_list);
|
||||||
|
category.goal_lists.splice(index, 1, goal_list);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset values
|
||||||
|
edit_goal_list_data.value = {
|
||||||
|
parent_group: undefined
|
||||||
|
};
|
||||||
|
edit_goal_list_dialog.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const delete_goal_list = (goal_list: BingoGoalList) => {
|
||||||
|
// Prevent deletion if goal list still has goals
|
||||||
|
if (!goal_list.goals.length) {
|
||||||
|
// We always assume parent group is a category. You can't add goal lists to goal lists.
|
||||||
|
const category = edit_goal_list_data.value.parent_group as BingoCategory;
|
||||||
|
|
||||||
|
const index = category.goal_lists.findIndex(g => g === goal_list);
|
||||||
|
category.goal_lists.splice(index, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset values
|
||||||
|
edit_goal_list_data.value = {
|
||||||
|
parent_group: undefined
|
||||||
|
};
|
||||||
|
edit_goal_list_dialog.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* End Edit Goal List Dialog */
|
||||||
|
|
||||||
|
/* Edit Category Dialog */
|
||||||
|
|
||||||
|
interface EditCategoryData {
|
||||||
|
category?: BingoCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
const edit_category_data = ref<EditCategoryData>({
|
||||||
|
});
|
||||||
|
|
||||||
|
const edit_category_dialog = ref(false);
|
||||||
|
|
||||||
|
function openEditCategoryDialog(node: Node, event: Event) {
|
||||||
|
// Stop expanding of the tree
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
edit_category_data.value.category = node.item as BingoCategory;
|
||||||
|
edit_category_dialog.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = {
|
||||||
|
};
|
||||||
|
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 = {
|
||||||
|
};
|
||||||
|
edit_category_dialog.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* End Edit Category Dialog */
|
||||||
|
|
||||||
|
const nodes = computed(() => {
|
||||||
|
const results: Node[] = [];
|
||||||
|
|
||||||
|
if (!game.value) {
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
results.push(...game.value?.items.map(item => {
|
||||||
|
const is_category = item instanceof BingoCategory;
|
||||||
|
const is_goal_list = item instanceof BingoGoalList;
|
||||||
|
|
||||||
|
const group_node: Node = {
|
||||||
|
label: item.name,
|
||||||
|
item,
|
||||||
|
header: is_category
|
||||||
|
? 'category'
|
||||||
|
: is_goal_list
|
||||||
|
? 'goal-list'
|
||||||
|
: '',
|
||||||
|
children: []
|
||||||
|
};
|
||||||
|
|
||||||
|
if (is_category) {
|
||||||
|
group_node.children = item.goal_lists.map(goal_list => {
|
||||||
|
const goal_list_node: Node = {
|
||||||
|
label: goal_list.name,
|
||||||
|
item: goal_list,
|
||||||
|
header: 'goal-list',
|
||||||
|
children: []
|
||||||
|
};
|
||||||
|
|
||||||
|
goal_list_node.children?.push(...goal_list.goals.map(goal => {
|
||||||
|
const goal_node: Node = {
|
||||||
|
label: goal.name,
|
||||||
|
item: goal,
|
||||||
|
header: 'goal',
|
||||||
|
goal,
|
||||||
|
parent: goal_list
|
||||||
|
};
|
||||||
|
|
||||||
|
return goal_node;
|
||||||
|
}));
|
||||||
|
|
||||||
|
goal_list_node.children?.push({
|
||||||
|
label: 'Add Goal',
|
||||||
|
header: 'add-goal',
|
||||||
|
parent: goal_list
|
||||||
|
});
|
||||||
|
|
||||||
|
return goal_list_node;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
group_node.children?.push({
|
||||||
|
label: 'Add Goal List',
|
||||||
|
header: 'add-goal-list'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else if (is_goal_list) {
|
||||||
|
group_node.children = item.goals.map(goal => {
|
||||||
|
const goal_node: Node = {
|
||||||
|
label: goal.name,
|
||||||
|
item: goal,
|
||||||
|
header: 'goal',
|
||||||
|
goal,
|
||||||
|
parent: item
|
||||||
|
};
|
||||||
|
|
||||||
|
return goal_node;
|
||||||
|
});
|
||||||
|
|
||||||
|
group_node.children.push({
|
||||||
|
label: 'Add Goal',
|
||||||
|
header: 'add-goal',
|
||||||
|
parent: item
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return group_node;
|
||||||
|
}));
|
||||||
|
|
||||||
|
results.push({
|
||||||
|
label: 'Add Goal List',
|
||||||
|
header: 'add-goal-list'
|
||||||
|
});
|
||||||
|
|
||||||
|
results.push({
|
||||||
|
label: 'Add Category',
|
||||||
|
header: 'add-category'
|
||||||
|
});
|
||||||
|
|
||||||
|
return results;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
@ -1,308 +0,0 @@
|
|||||||
<template>
|
|
||||||
<q-tree
|
|
||||||
:nodes="nodes"
|
|
||||||
node-key="label"
|
|
||||||
tick-strategy="leaf"
|
|
||||||
v-model:selected="selected"
|
|
||||||
v-model:ticked="ticked"
|
|
||||||
v-model:expanded="expanded"
|
|
||||||
>
|
|
||||||
<template v-slot:header-goal="prop">
|
|
||||||
<div class="row items-center">
|
|
||||||
<q-btn
|
|
||||||
v-if="editMode"
|
|
||||||
icon="edit"
|
|
||||||
flat
|
|
||||||
round
|
|
||||||
size="sm"
|
|
||||||
color="orange"
|
|
||||||
@click="openEditGoalDialog(prop.node)"
|
|
||||||
/>
|
|
||||||
<div>{{ prop.node.label }}</div>
|
|
||||||
<q-badge outline v-for="tag in prop.node.goal.tags" :key="tag" :color="getColorForString(tag)" class="q-ml-sm">
|
|
||||||
<div class="text-weight-bold q-my-xs">{{ tag }}</div>
|
|
||||||
</q-badge>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:header-goal-list="prop">
|
|
||||||
<div class="row items-center">
|
|
||||||
<q-btn v-if="editMode" icon="edit" flat round size="sm" color="orange"/>
|
|
||||||
<div>{{ prop.node.label }}</div>
|
|
||||||
<q-badge outline color="orange" class="q-ml-sm">
|
|
||||||
<div class="text-weight-bold q-my-xs">Goal List</div>
|
|
||||||
</q-badge>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-slot:header-category="prop">
|
|
||||||
<div class="row items-center">
|
|
||||||
<q-btn v-if="editMode" icon="edit" flat round size="sm" color="orange"/>
|
|
||||||
<div>{{ prop.node.label }}</div>
|
|
||||||
<q-badge outline color="blue" class="q-ml-sm">
|
|
||||||
<div class="text-weight-bold q-my-xs">Category</div>
|
|
||||||
</q-badge>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- Add Goal -->
|
|
||||||
<template v-slot:header-add-goal="prop">
|
|
||||||
<div class="row items-center">
|
|
||||||
<q-btn outline icon="add" label="Goal" color="light-blue" size="sm" @click="openEditGoalDialog(prop.node)"></q-btn>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- Add Goal List -->
|
|
||||||
<template v-slot:header-add-goal-list="prop">
|
|
||||||
<div class="row items-center">
|
|
||||||
<q-btn outline icon="add" label="Goal List" color="light-blue" size="sm"></q-btn>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- Add Category -->
|
|
||||||
<template v-slot:header-add-category="prop">
|
|
||||||
<div class="row items-center">
|
|
||||||
<q-btn outline icon="add" label="Category" color="light-blue" size="sm"></q-btn>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</q-tree>
|
|
||||||
|
|
||||||
<q-dialog v-model="edit_goal_dialog" persistent>
|
|
||||||
<EditGoalDialog
|
|
||||||
:data="edit_goal_data"
|
|
||||||
@cancel="edit_goal_dialog = false"
|
|
||||||
@delete-goal="delete_goal"
|
|
||||||
@emit-goal="emit_goal"
|
|
||||||
/>
|
|
||||||
</q-dialog>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
interface Node {
|
|
||||||
label: string;
|
|
||||||
header?: string; // 'goal' | 'goal-list' | 'category' | 'add-goal' | 'add-goal-list' | 'add-category'
|
|
||||||
children?: Node[];
|
|
||||||
goal?: BingoGoal;
|
|
||||||
parent?: BingoCategory | BingoGoalList;
|
|
||||||
tickable?: boolean;
|
|
||||||
noTick?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
import {
|
|
||||||
getColorForString
|
|
||||||
} from '@/js/lib/Util.ts';
|
|
||||||
|
|
||||||
import BingoCategory from '@/js/lib/BingoCategory.ts';
|
|
||||||
import type BingoGame from '@/js/lib/BingoGame.ts';
|
|
||||||
import BingoGoal from '@/js/lib/BingoGoal.ts';
|
|
||||||
import BingoGoalList from '@/js/lib/BingoGoalList.ts';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
|
||||||
editMode: boolean;
|
|
||||||
game: BingoGame | undefined;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const editMode = props.editMode;
|
|
||||||
|
|
||||||
const selected = ref<Node[]>([]);
|
|
||||||
const ticked = ref<Node[]>([]);
|
|
||||||
const expanded = ref<Node[]>([]);
|
|
||||||
|
|
||||||
/* Edit Goal Dialog */
|
|
||||||
interface EditGoalData {
|
|
||||||
goal?: BingoGoal;
|
|
||||||
all_tags: string[];
|
|
||||||
parent_group: BingoCategory | BingoGoalList | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const edit_goal_data = ref<EditGoalData>({
|
|
||||||
all_tags: [] as string[],
|
|
||||||
parent_group: undefined
|
|
||||||
});
|
|
||||||
const edit_goal_dialog = ref(false);
|
|
||||||
const openEditGoalDialog = (node: Node) => {
|
|
||||||
edit_goal_data.value.goal = node.goal;
|
|
||||||
edit_goal_data.value.parent_group = node.parent;
|
|
||||||
edit_goal_data.value.all_tags = props.game?.getAllTags() ?? [];
|
|
||||||
edit_goal_dialog.value = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const delete_goal = (goal: BingoGoal) => {
|
|
||||||
// We always assume parent group is a goal list. You can't add goals to categories.
|
|
||||||
const goal_list = edit_goal_data.value.parent_group as BingoGoalList;
|
|
||||||
|
|
||||||
const index = goal_list.goals.findIndex(g => g === goal);
|
|
||||||
goal_list.goals.splice(index, 1);
|
|
||||||
|
|
||||||
// Reset values
|
|
||||||
edit_goal_data.value = {
|
|
||||||
goal: undefined,
|
|
||||||
all_tags: [],
|
|
||||||
parent_group: undefined
|
|
||||||
};
|
|
||||||
edit_goal_dialog.value = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const emit_goal = (goal: BingoGoal) => {
|
|
||||||
// We always assume parent group is a goal list. You can't add goals to categories.
|
|
||||||
const goal_list = edit_goal_data.value.parent_group as BingoGoalList;
|
|
||||||
|
|
||||||
// Add new goal
|
|
||||||
if (!edit_goal_data.value.goal) {
|
|
||||||
goal_list.goals.push(goal);
|
|
||||||
}
|
|
||||||
// Replace existing goal
|
|
||||||
else {
|
|
||||||
const index = goal_list.goals.findIndex(g => g === edit_goal_data.value.goal);
|
|
||||||
goal_list.goals.splice(index, 1, goal);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset values
|
|
||||||
edit_goal_data.value = {
|
|
||||||
goal: undefined,
|
|
||||||
all_tags: [],
|
|
||||||
parent_group: undefined
|
|
||||||
};
|
|
||||||
edit_goal_dialog.value = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
// const getAllGoals = (node: Node): BingoGoal[] => {
|
|
||||||
// const goals: BingoGoal[] = [];
|
|
||||||
// if (node.children) {
|
|
||||||
// for (const child of node.children) {
|
|
||||||
// goals.push(...getAllGoals(child));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else if (node.goal) {
|
|
||||||
// goals.push(node.goal);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return goals;
|
|
||||||
// };
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
// const selectByTag = (tag: string) => {
|
|
||||||
// ticked.value.length = 0;
|
|
||||||
// ticked.value.push(...nodes.value.filter(node => {
|
|
||||||
// return getAllGoals(node).map(goal => goal.tags.includes(tag));
|
|
||||||
// }));
|
|
||||||
|
|
||||||
// console.log(ticked.value);
|
|
||||||
// };
|
|
||||||
|
|
||||||
const nodes = computed(() => {
|
|
||||||
const results: Node[] = [];
|
|
||||||
|
|
||||||
if (!props.game) {
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
results.push(...props.game.items.map(item => {
|
|
||||||
const is_category = item instanceof BingoCategory;
|
|
||||||
const is_goal_list = item instanceof BingoGoalList;
|
|
||||||
|
|
||||||
const group_node: Node = {
|
|
||||||
label: item.name,
|
|
||||||
header: is_category
|
|
||||||
? 'category'
|
|
||||||
: is_goal_list
|
|
||||||
? 'goal-list'
|
|
||||||
: '',
|
|
||||||
children: [],
|
|
||||||
tickable: !editMode,
|
|
||||||
noTick: editMode
|
|
||||||
};
|
|
||||||
|
|
||||||
if (is_category) {
|
|
||||||
group_node.children = item.goal_lists.map(goal_list => {
|
|
||||||
const goal_list_node: Node = {
|
|
||||||
label: goal_list.name,
|
|
||||||
header: 'goal-list',
|
|
||||||
children: [],
|
|
||||||
tickable: !editMode,
|
|
||||||
noTick: editMode
|
|
||||||
};
|
|
||||||
|
|
||||||
goal_list_node.children?.push(...goal_list.goals.map(goal => {
|
|
||||||
const goal_node: Node = {
|
|
||||||
label: goal.name,
|
|
||||||
header: 'goal',
|
|
||||||
goal,
|
|
||||||
parent: goal_list,
|
|
||||||
tickable: !editMode,
|
|
||||||
noTick: editMode
|
|
||||||
};
|
|
||||||
|
|
||||||
return goal_node;
|
|
||||||
}));
|
|
||||||
|
|
||||||
if (editMode) {
|
|
||||||
goal_list_node.children?.push({
|
|
||||||
label: 'Add Goal',
|
|
||||||
header: 'add-goal',
|
|
||||||
parent: goal_list,
|
|
||||||
tickable: false,
|
|
||||||
noTick: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return goal_list_node;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (editMode) {
|
|
||||||
group_node.children?.push({
|
|
||||||
label: 'Add Goal List',
|
|
||||||
header: 'add-goal-list',
|
|
||||||
tickable: false,
|
|
||||||
noTick: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (is_goal_list) {
|
|
||||||
group_node.children = item.goals.map(goal => {
|
|
||||||
const goal_node: Node = {
|
|
||||||
label: goal.name,
|
|
||||||
header: 'goal',
|
|
||||||
goal,
|
|
||||||
parent: item,
|
|
||||||
tickable: !editMode,
|
|
||||||
noTick: editMode
|
|
||||||
};
|
|
||||||
|
|
||||||
return goal_node;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (editMode) {
|
|
||||||
group_node.children.push({
|
|
||||||
label: 'Add Goal',
|
|
||||||
header: 'add-goal',
|
|
||||||
parent: item,
|
|
||||||
tickable: false,
|
|
||||||
noTick: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return group_node;
|
|
||||||
}));
|
|
||||||
|
|
||||||
if (editMode) {
|
|
||||||
results.push({
|
|
||||||
label: 'Add Goal List',
|
|
||||||
header: 'add-goal-list',
|
|
||||||
tickable: false,
|
|
||||||
noTick: true
|
|
||||||
});
|
|
||||||
|
|
||||||
results.push({
|
|
||||||
label: 'Add Category',
|
|
||||||
header: 'add-category',
|
|
||||||
tickable: false,
|
|
||||||
noTick: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
355
src/composables/GameListDialog__UNUSED_NEEDS_EDITS.vue
Normal file
355
src/composables/GameListDialog__UNUSED_NEEDS_EDITS.vue
Normal file
@ -0,0 +1,355 @@
|
|||||||
|
<template>
|
||||||
|
<span>TODO: Show a big list with all games and let user tick things. Use that for generation.</span>
|
||||||
|
<!-- <q-tree
|
||||||
|
:nodes="nodes"
|
||||||
|
node-key="label"
|
||||||
|
tick-strategy="leaf"
|
||||||
|
v-model:selected="selected"
|
||||||
|
v-model:ticked="ticked"
|
||||||
|
v-model:expanded="expanded"
|
||||||
|
>
|
||||||
|
<template v-slot:header-goal="prop">
|
||||||
|
<div class="row items-center">
|
||||||
|
<q-btn
|
||||||
|
v-if="editMode"
|
||||||
|
icon="edit"
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
size="sm"
|
||||||
|
color="orange"
|
||||||
|
@click="openEditGoalDialog(prop.node)"
|
||||||
|
/>
|
||||||
|
<div>{{ prop.node.label }}</div>
|
||||||
|
<q-badge
|
||||||
|
outline
|
||||||
|
v-for="tag in prop.node.goal.tags"
|
||||||
|
:key="tag"
|
||||||
|
:color="getColorForString(tag)"
|
||||||
|
class="q-ml-sm"
|
||||||
|
>
|
||||||
|
<div class="text-weight-bold q-my-xs">{{ tag }}</div>
|
||||||
|
</q-badge>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:header-goal-list="prop">
|
||||||
|
<div class="row items-center">
|
||||||
|
<q-btn
|
||||||
|
v-if="editMode"
|
||||||
|
icon="edit"
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
size="sm"
|
||||||
|
color="orange"
|
||||||
|
/>
|
||||||
|
<div>{{ prop.node.label }}</div>
|
||||||
|
<q-badge
|
||||||
|
outline
|
||||||
|
color="orange"
|
||||||
|
class="q-ml-sm"
|
||||||
|
>
|
||||||
|
<div class="text-weight-bold q-my-xs">Goal List</div>
|
||||||
|
</q-badge>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:header-category="prop">
|
||||||
|
<div class="row items-center">
|
||||||
|
<q-btn
|
||||||
|
v-if="editMode"
|
||||||
|
icon="edit"
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
size="sm"
|
||||||
|
color="orange"
|
||||||
|
/>
|
||||||
|
<div>{{ prop.node.label }}</div>
|
||||||
|
<q-badge
|
||||||
|
outline
|
||||||
|
color="blue"
|
||||||
|
class="q-ml-sm"
|
||||||
|
>
|
||||||
|
<div class="text-weight-bold q-my-xs">Category</div>
|
||||||
|
</q-badge>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:header-add-goal="prop">
|
||||||
|
<div class="row items-center">
|
||||||
|
<q-btn
|
||||||
|
outline
|
||||||
|
icon="add"
|
||||||
|
label="Goal"
|
||||||
|
color="light-blue"
|
||||||
|
size="sm"
|
||||||
|
@click="openEditGoalDialog(prop.node)"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:header-add-goal-list="prop">
|
||||||
|
<div class="row items-center">
|
||||||
|
<q-btn
|
||||||
|
outline
|
||||||
|
icon="add"
|
||||||
|
label="Goal List"
|
||||||
|
color="light-blue"
|
||||||
|
size="sm"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:header-add-category="prop">
|
||||||
|
<div class="row items-center">
|
||||||
|
<q-btn
|
||||||
|
outline
|
||||||
|
icon="add"
|
||||||
|
label="Category"
|
||||||
|
color="light-blue"
|
||||||
|
size="sm"
|
||||||
|
></q-btn>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</q-tree>
|
||||||
|
|
||||||
|
<q-dialog v-model="edit_goal_dialog" persistent>
|
||||||
|
<EditGoalDialog
|
||||||
|
:data="edit_goal_data"
|
||||||
|
@cancel="edit_goal_dialog = false"
|
||||||
|
@delete-goal="delete_goal"
|
||||||
|
@emit-goal="emit_goal"
|
||||||
|
/>
|
||||||
|
</q-dialog> -->
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
// interface Node {
|
||||||
|
// label: string;
|
||||||
|
// header?: string; // 'goal' | 'goal-list' | 'category' | 'add-goal' | 'add-goal-list' | 'add-category'
|
||||||
|
// children?: Node[];
|
||||||
|
// goal?: BingoGoal;
|
||||||
|
// parent?: BingoCategory | BingoGoalList;
|
||||||
|
// tickable?: boolean;
|
||||||
|
// noTick?: boolean;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// import {
|
||||||
|
// getColorForString
|
||||||
|
// } from '@/js/lib/Util.ts';
|
||||||
|
|
||||||
|
// import BingoCategory from '@/js/lib/BingoCategory.ts';
|
||||||
|
// import type BingoGame from '@/js/lib/BingoGame.ts';
|
||||||
|
// import BingoGoal from '@/js/lib/BingoGoal.ts';
|
||||||
|
// import BingoGoalList from '@/js/lib/BingoGoalList.ts';
|
||||||
|
// import Parser from '@/js/lib/Parser.ts';
|
||||||
|
|
||||||
|
// const props = defineProps<{
|
||||||
|
// editMode: boolean;
|
||||||
|
// game: BingoGame;
|
||||||
|
// }>();
|
||||||
|
|
||||||
|
// const game = Parser.getCopy(props.game);
|
||||||
|
// const editMode = props.editMode;
|
||||||
|
|
||||||
|
// const selected = ref<Node[]>([]);
|
||||||
|
// const ticked = ref<Node[]>([]);
|
||||||
|
// const expanded = ref<Node[]>([]);
|
||||||
|
|
||||||
|
// /* Edit Goal Dialog */
|
||||||
|
// interface EditGoalData {
|
||||||
|
// goal?: BingoGoal;
|
||||||
|
// all_tags: string[];
|
||||||
|
// parent_group: BingoCategory | BingoGoalList | undefined;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const edit_goal_data = ref<EditGoalData>({
|
||||||
|
// all_tags: [] as string[],
|
||||||
|
// parent_group: undefined
|
||||||
|
// });
|
||||||
|
// const edit_goal_dialog = ref(false);
|
||||||
|
// const openEditGoalDialog = (node: Node) => {
|
||||||
|
// edit_goal_data.value.goal = node.goal;
|
||||||
|
// edit_goal_data.value.parent_group = node.parent;
|
||||||
|
// edit_goal_data.value.all_tags = game?.getAllTags() ?? [];
|
||||||
|
// edit_goal_dialog.value = true;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const delete_goal = (goal: BingoGoal) => {
|
||||||
|
// // We always assume parent group is a goal list. You can't add goals to categories.
|
||||||
|
// const goal_list = edit_goal_data.value.parent_group as BingoGoalList;
|
||||||
|
|
||||||
|
// const index = goal_list.goals.findIndex(g => g === goal);
|
||||||
|
// goal_list.goals.splice(index, 1);
|
||||||
|
|
||||||
|
// // Reset values
|
||||||
|
// edit_goal_data.value = {
|
||||||
|
// goal: undefined,
|
||||||
|
// all_tags: [],
|
||||||
|
// parent_group: undefined
|
||||||
|
// };
|
||||||
|
// edit_goal_dialog.value = false;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// const emit_goal = (goal: BingoGoal) => {
|
||||||
|
// // We always assume parent group is a goal list. You can't add goals to categories.
|
||||||
|
// const goal_list = edit_goal_data.value.parent_group as BingoGoalList;
|
||||||
|
|
||||||
|
// // Add new goal
|
||||||
|
// if (!edit_goal_data.value.goal) {
|
||||||
|
// goal_list.goals.push(goal);
|
||||||
|
// }
|
||||||
|
// // Replace existing goal
|
||||||
|
// else {
|
||||||
|
// const index = goal_list.goals.findIndex(g => g === edit_goal_data.value.goal);
|
||||||
|
// goal_list.goals.splice(index, 1, goal);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Reset values
|
||||||
|
// edit_goal_data.value = {
|
||||||
|
// goal: undefined,
|
||||||
|
// all_tags: [],
|
||||||
|
// parent_group: undefined
|
||||||
|
// };
|
||||||
|
// edit_goal_dialog.value = false;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// // const getAllGoals = (node: Node): BingoGoal[] => {
|
||||||
|
// // const goals: BingoGoal[] = [];
|
||||||
|
// // if (node.children) {
|
||||||
|
// // for (const child of node.children) {
|
||||||
|
// // goals.push(...getAllGoals(child));
|
||||||
|
// // }
|
||||||
|
// // }
|
||||||
|
// // else if (node.goal) {
|
||||||
|
// // goals.push(node.goal);
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// // return goals;
|
||||||
|
// // };
|
||||||
|
|
||||||
|
// // eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
// // const selectByTag = (tag: string) => {
|
||||||
|
// // ticked.value.length = 0;
|
||||||
|
// // ticked.value.push(...nodes.value.filter(node => {
|
||||||
|
// // return getAllGoals(node).map(goal => goal.tags.includes(tag));
|
||||||
|
// // }));
|
||||||
|
|
||||||
|
// // console.log(ticked.value);
|
||||||
|
// // };
|
||||||
|
|
||||||
|
// const nodes = computed(() => {
|
||||||
|
// const results: Node[] = [];
|
||||||
|
|
||||||
|
// if (!game) {
|
||||||
|
// return results;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// results.push(...game.items.map(item => {
|
||||||
|
// const is_category = item instanceof BingoCategory;
|
||||||
|
// const is_goal_list = item instanceof BingoGoalList;
|
||||||
|
|
||||||
|
// const group_node: Node = {
|
||||||
|
// label: item.name,
|
||||||
|
// header: is_category
|
||||||
|
// ? 'category'
|
||||||
|
// : is_goal_list
|
||||||
|
// ? 'goal-list'
|
||||||
|
// : '',
|
||||||
|
// children: [],
|
||||||
|
// tickable: !editMode,
|
||||||
|
// noTick: editMode
|
||||||
|
// };
|
||||||
|
|
||||||
|
// if (is_category) {
|
||||||
|
// group_node.children = item.goal_lists.map(goal_list => {
|
||||||
|
// const goal_list_node: Node = {
|
||||||
|
// label: goal_list.name,
|
||||||
|
// header: 'goal-list',
|
||||||
|
// children: [],
|
||||||
|
// tickable: !editMode,
|
||||||
|
// noTick: editMode
|
||||||
|
// };
|
||||||
|
|
||||||
|
// goal_list_node.children?.push(...goal_list.goals.map(goal => {
|
||||||
|
// const goal_node: Node = {
|
||||||
|
// label: goal.name,
|
||||||
|
// header: 'goal',
|
||||||
|
// goal,
|
||||||
|
// parent: goal_list,
|
||||||
|
// tickable: !editMode,
|
||||||
|
// noTick: editMode
|
||||||
|
// };
|
||||||
|
|
||||||
|
// return goal_node;
|
||||||
|
// }));
|
||||||
|
|
||||||
|
// if (editMode) {
|
||||||
|
// goal_list_node.children?.push({
|
||||||
|
// label: 'Add Goal',
|
||||||
|
// header: 'add-goal',
|
||||||
|
// parent: goal_list,
|
||||||
|
// tickable: false,
|
||||||
|
// noTick: true
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return goal_list_node;
|
||||||
|
// });
|
||||||
|
|
||||||
|
// if (editMode) {
|
||||||
|
// group_node.children?.push({
|
||||||
|
// label: 'Add Goal List',
|
||||||
|
// header: 'add-goal-list',
|
||||||
|
// tickable: false,
|
||||||
|
// noTick: true
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// else if (is_goal_list) {
|
||||||
|
// group_node.children = item.goals.map(goal => {
|
||||||
|
// const goal_node: Node = {
|
||||||
|
// label: goal.name,
|
||||||
|
// header: 'goal',
|
||||||
|
// goal,
|
||||||
|
// parent: item,
|
||||||
|
// tickable: !editMode,
|
||||||
|
// noTick: editMode
|
||||||
|
// };
|
||||||
|
|
||||||
|
// return goal_node;
|
||||||
|
// });
|
||||||
|
|
||||||
|
// if (editMode) {
|
||||||
|
// group_node.children.push({
|
||||||
|
// label: 'Add Goal',
|
||||||
|
// header: 'add-goal',
|
||||||
|
// parent: item,
|
||||||
|
// tickable: false,
|
||||||
|
// noTick: true
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return group_node;
|
||||||
|
// }));
|
||||||
|
|
||||||
|
// if (editMode) {
|
||||||
|
// results.push({
|
||||||
|
// label: 'Add Goal List',
|
||||||
|
// header: 'add-goal-list',
|
||||||
|
// tickable: false,
|
||||||
|
// noTick: true
|
||||||
|
// });
|
||||||
|
|
||||||
|
// results.push({
|
||||||
|
// label: 'Add Category',
|
||||||
|
// header: 'add-category',
|
||||||
|
// tickable: false,
|
||||||
|
// noTick: true
|
||||||
|
// });
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return results;
|
||||||
|
// });
|
||||||
|
</script>
|
||||||
|
|
@ -51,6 +51,24 @@ export default class BingoGame {
|
|||||||
this.description = description;
|
this.description = description;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get reserved_names(): string[] {
|
||||||
|
const names: string[] = [];
|
||||||
|
|
||||||
|
this.items.map(group => {
|
||||||
|
const goal_lists = group instanceof BingoGoalList ? [ group ] : group.goal_lists;
|
||||||
|
|
||||||
|
goal_lists.map(subgroup => {
|
||||||
|
subgroup.goals.map(goal => {
|
||||||
|
if (goal.name) {
|
||||||
|
names.push(goal.name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
addItem(group: BingoCategoryOrGoalList): SuccessResponse {
|
addItem(group: BingoCategoryOrGoalList): SuccessResponse {
|
||||||
const existingGroup = this.items.find(g => g === group);
|
const existingGroup = this.items.find(g => g === group);
|
||||||
if (existingGroup) {
|
if (existingGroup) {
|
||||||
|
@ -7,6 +7,8 @@ import {
|
|||||||
export default class BingoGoal {
|
export default class BingoGoal {
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
|
description: string = '';
|
||||||
|
|
||||||
tags: string[] = [];
|
tags: string[] = [];
|
||||||
|
|
||||||
possible_spaces: number[] = [];
|
possible_spaces: number[] = [];
|
||||||
|
@ -17,9 +17,16 @@ export default class Parser {
|
|||||||
return game;
|
return game;
|
||||||
}
|
}
|
||||||
|
|
||||||
static to_json(game: BingoGame): Record<string, any> {
|
static to_json(game: BingoGame): JSON {
|
||||||
const json = instanceToPlain(game);
|
const json = instanceToPlain(game);
|
||||||
|
|
||||||
return json;
|
return json as JSON;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getCopy(game: BingoGame): BingoGame {
|
||||||
|
const json: JSON = this.to_json(game);
|
||||||
|
const copy = this.from_json_raw(json);
|
||||||
|
|
||||||
|
return copy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,6 @@ export const useLocalGamesStore = defineStore('localGames', () => {
|
|||||||
|
|
||||||
games.value.length = 0;
|
games.value.length = 0;
|
||||||
|
|
||||||
console.log(games_array);
|
|
||||||
games.value = games_array.map(game => {
|
games.value = games_array.map(game => {
|
||||||
const bingo_game = Parser.from_json_raw(game);
|
const bingo_game = Parser.from_json_raw(game);
|
||||||
|
|
||||||
@ -42,7 +41,6 @@ export const useLocalGamesStore = defineStore('localGames', () => {
|
|||||||
// });
|
// });
|
||||||
|
|
||||||
console.info('--- Finished fetching games!');
|
console.info('--- Finished fetching games!');
|
||||||
console.log(games.value);
|
|
||||||
|
|
||||||
console.info('');
|
console.info('');
|
||||||
}
|
}
|
||||||
@ -62,7 +60,7 @@ export const useLocalGamesStore = defineStore('localGames', () => {
|
|||||||
return games.value.some(game => game.id === id);
|
return games.value.some(game => game.id === id);
|
||||||
}
|
}
|
||||||
|
|
||||||
function addGame(game: BingoGame) {
|
async function addGame(game: BingoGame) {
|
||||||
if (hasGame(game.id)) {
|
if (hasGame(game.id)) {
|
||||||
console.error(`Failed to clone game with ID ${ game.id } because it already exists`);
|
console.error(`Failed to clone game with ID ${ game.id } because it already exists`);
|
||||||
|
|
||||||
@ -76,10 +74,27 @@ export const useLocalGamesStore = defineStore('localGames', () => {
|
|||||||
cloned_game.is_local = true;
|
cloned_game.is_local = true;
|
||||||
|
|
||||||
games.value.push(cloned_game);
|
games.value.push(cloned_game);
|
||||||
saveGames();
|
|
||||||
|
await saveGames();
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteGame(id: string) {
|
async function updateGame(game: BingoGame) {
|
||||||
|
if (!hasGame(game.id)) {
|
||||||
|
console.error(`Failed to update game with ID ${ game.id } because it doesn't exist`);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.info(`--- Updating game with ID '${ game.id }' ...`);
|
||||||
|
|
||||||
|
const index = games.value.findIndex(g => g.id === game.id);
|
||||||
|
|
||||||
|
games.value[index] = game;
|
||||||
|
|
||||||
|
await saveGames();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteGame(id: string) {
|
||||||
if (hasGame(id)) {
|
if (hasGame(id)) {
|
||||||
console.error(`Failed to delete game with ID ${ id } because it doesn't exist`);
|
console.error(`Failed to delete game with ID ${ id } because it doesn't exist`);
|
||||||
|
|
||||||
@ -89,7 +104,8 @@ export const useLocalGamesStore = defineStore('localGames', () => {
|
|||||||
console.info(`--- Deleting game with ID '${ id }' ...`);
|
console.info(`--- Deleting game with ID '${ id }' ...`);
|
||||||
|
|
||||||
games.value = games.value.filter(game => game.id !== id);
|
games.value = games.value.filter(game => game.id !== id);
|
||||||
saveGames();
|
|
||||||
|
await saveGames();
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -100,6 +116,7 @@ export const useLocalGamesStore = defineStore('localGames', () => {
|
|||||||
|
|
||||||
hasGame,
|
hasGame,
|
||||||
addGame,
|
addGame,
|
||||||
|
updateGame,
|
||||||
deleteGame
|
deleteGame
|
||||||
};
|
};
|
||||||
}, {
|
}, {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- <EditorComponent/> -->
|
<EditorComponent/>
|
||||||
|
|
||||||
<GameList :game="yakuza" :edit-mode="true"/>
|
<!-- <GameList :game="yakuza" :edit-mode="true"/> -->
|
||||||
|
|
||||||
|
|
||||||
<!-- <q-dialog v-model="dialog">
|
<!-- <q-dialog v-model="dialog">
|
||||||
@ -12,24 +12,24 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type BingoGame from '@/js/lib/BingoGame.ts';
|
// import type BingoGame from '@/js/lib/BingoGame.ts';
|
||||||
|
|
||||||
import {
|
// import {
|
||||||
useLocalGamesStore
|
// useLocalGamesStore
|
||||||
} from '@/stores/local-games.ts';
|
// } from '@/stores/local-games.ts';
|
||||||
const localGames = useLocalGamesStore();
|
// const localGames = useLocalGamesStore();
|
||||||
|
|
||||||
const yakuza = ref();
|
// const yakuza = ref();
|
||||||
onMounted(async() => {
|
// onMounted(async() => {
|
||||||
await localGames.fetchGames();
|
// await localGames.fetchGames();
|
||||||
|
|
||||||
yakuza.value = localGames.games[0];
|
// yakuza.value = localGames.games[0];
|
||||||
console.log(yakuza.value);
|
// console.log(yakuza.value);
|
||||||
});
|
// });
|
||||||
|
|
||||||
|
|
||||||
const dialog = ref<boolean>(false);
|
// const dialog = ref<boolean>(false);
|
||||||
const selected_game = ref<BingoGame | undefined>();
|
// const selected_game = ref<BingoGame | undefined>();
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
Loading…
Reference in New Issue
Block a user