Add proper edit functionality support
This commit is contained in:
parent
fd4adedcb3
commit
fcbdfd038f
@ -198,6 +198,31 @@ module.exports = {
|
||||
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' {
|
||||
export interface GlobalComponents {
|
||||
copy: typeof import('./src/composables/EditGoalDialog copy.vue')['default']
|
||||
EditGoal: typeof import('./src/composables/EditGoal.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']
|
||||
GameEditor: typeof import('./src/composables/GameEditor.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']
|
||||
GeneratorComponent: typeof import('./src/components/GeneratorComponent.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']
|
||||
RouterLink: typeof import('vue-router')['RouterLink']
|
||||
RouterView: typeof import('vue-router')['RouterView']
|
||||
|
@ -22,11 +22,20 @@
|
||||
<q-separator />
|
||||
|
||||
<q-card-actions>
|
||||
<q-btn flat color="green" label="Add" />
|
||||
<q-btn
|
||||
flat
|
||||
color="green"
|
||||
label="Add"
|
||||
/>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
@ -37,16 +46,29 @@
|
||||
<div class="col-12">
|
||||
<div class="row items-center justify-evenly">
|
||||
<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 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-section horizontal>
|
||||
<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>
|
||||
</q-badge>
|
||||
|
||||
@ -72,18 +94,34 @@
|
||||
<q-separator />
|
||||
|
||||
<q-card-actions>
|
||||
<q-btn flat icon="help" @click="selected_game = game">
|
||||
<q-btn
|
||||
flat
|
||||
icon="help"
|
||||
@click="learnMore(game)"
|
||||
>
|
||||
<q-tooltip>
|
||||
Learn more...
|
||||
</q-tooltip>
|
||||
</q-btn>
|
||||
<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>
|
||||
Edit
|
||||
</q-tooltip>
|
||||
</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>
|
||||
Delete
|
||||
</q-tooltip>
|
||||
@ -91,10 +129,22 @@
|
||||
|
||||
<q-space/>
|
||||
|
||||
<q-btn flat color="blue" icon="download" label="Export" />
|
||||
<q-btn
|
||||
flat
|
||||
color="blue"
|
||||
icon="download"
|
||||
label="Export"
|
||||
/>
|
||||
</template>
|
||||
<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>
|
||||
Copy to local
|
||||
</q-tooltip>
|
||||
@ -110,7 +160,11 @@
|
||||
<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">
|
||||
<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
|
||||
src="https://picsum.photos/200/200"
|
||||
width="100%"
|
||||
@ -131,6 +185,18 @@
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</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>
|
||||
|
||||
<script setup lang="ts">
|
||||
@ -154,12 +220,31 @@ onMounted(async() => {
|
||||
await localGames.fetchGames();
|
||||
});
|
||||
|
||||
async function saveGameInternal(game: BingoGame) {
|
||||
const edit_game = ref(false);
|
||||
|
||||
function editGame(game: BingoGame) {
|
||||
loading.value = true;
|
||||
|
||||
localGames.addGame(game);
|
||||
// await saveGame(game);
|
||||
// local_games.value = await getLocalGames();
|
||||
selected_game.value = game;
|
||||
edit_game.value = true;
|
||||
|
||||
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;
|
||||
}
|
||||
@ -167,7 +252,7 @@ async function saveGameInternal(game: BingoGame) {
|
||||
async function deleteGameInternal(id: string) {
|
||||
loading.value = true;
|
||||
|
||||
localGames.deleteGame(id);
|
||||
await localGames.deleteGame(id);
|
||||
|
||||
loading.value = false;
|
||||
}
|
||||
@ -193,15 +278,8 @@ const filtered_games = computed(() => {
|
||||
return combined_games.value.filter(game => game.name.toLocaleLowerCase().includes(search.value.toLocaleLowerCase()));
|
||||
});
|
||||
|
||||
// On selected game change, open popup
|
||||
watch(selected_game, newGame => {
|
||||
if (newGame)
|
||||
function learnMore(game: BingoGame) {
|
||||
selected_game.value = game;
|
||||
learn_more.value = true;
|
||||
});
|
||||
|
||||
// On popup close, reset selected game
|
||||
watch(learn_more, newValue => {
|
||||
if (!newValue)
|
||||
selected_game.value = undefined;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
@ -9,9 +9,22 @@
|
||||
>
|
||||
<template v-slot:header-goal="prop">
|
||||
<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>
|
||||
<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>
|
||||
</q-badge>
|
||||
</div>
|
||||
@ -19,9 +32,20 @@
|
||||
|
||||
<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"/>
|
||||
<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">
|
||||
<q-badge
|
||||
outline
|
||||
color="orange"
|
||||
class="q-ml-sm"
|
||||
>
|
||||
<div class="text-weight-bold q-my-xs">Goal List</div>
|
||||
</q-badge>
|
||||
</div>
|
||||
@ -29,9 +53,20 @@
|
||||
|
||||
<template v-slot:header-category="prop">
|
||||
<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>
|
||||
<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>
|
||||
</q-badge>
|
||||
</div>
|
||||
@ -40,21 +75,39 @@
|
||||
<!-- 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"></q-btn>
|
||||
<q-btn
|
||||
outline
|
||||
icon="add"
|
||||
label="Goal"
|
||||
color="light-blue"
|
||||
size="sm"
|
||||
></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>
|
||||
<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>
|
||||
<q-btn
|
||||
outline
|
||||
icon="add"
|
||||
label="Category"
|
||||
color="light-blue"
|
||||
size="sm"
|
||||
></q-btn>
|
||||
</div>
|
||||
</template>
|
||||
</q-tree>
|
||||
@ -126,8 +179,6 @@ const selectByTag = (tag: string) => {
|
||||
ticked.value.push(...nodes.value.filter(node => {
|
||||
return getAllGoals(node).map(goal => goal.tags.includes(tag));
|
||||
}));
|
||||
|
||||
console.log(ticked.value);
|
||||
};
|
||||
|
||||
const nodes = computed(() => {
|
||||
|
@ -1,6 +1,10 @@
|
||||
|
||||
<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-input
|
||||
v-model="goal_name"
|
||||
@ -13,6 +17,28 @@
|
||||
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
|
||||
@ -22,6 +48,7 @@
|
||||
use-chips
|
||||
multiple
|
||||
clearable
|
||||
hint="Optional"
|
||||
hide-dropdown-icon
|
||||
input-debounce="0"
|
||||
:options="all_tags"
|
||||
@ -32,14 +59,46 @@
|
||||
<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-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="emitGoal()" :disabled="!canSave()"/>
|
||||
<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">
|
||||
@ -47,13 +106,12 @@ import BingoGoal from '@/js/lib/BingoGoal.ts';
|
||||
import {
|
||||
stringCompare
|
||||
} from '@/js/lib/Util.ts';
|
||||
import type BingoCategory from '@/js/lib/BingoCategory.ts';
|
||||
import type BingoGoalList from '@/js/lib/BingoGoalList.ts';
|
||||
|
||||
const emit = defineEmits([
|
||||
'cancel',
|
||||
'deleteGoal',
|
||||
'emitGoal'
|
||||
'createGoal',
|
||||
'updateGoal',
|
||||
'deleteGoal'
|
||||
]);
|
||||
|
||||
const {
|
||||
@ -61,16 +119,14 @@ const {
|
||||
} = defineProps<{
|
||||
data: {
|
||||
all_tags: string[],
|
||||
reserved_names: string[],
|
||||
goal?: BingoGoal,
|
||||
},
|
||||
}>();
|
||||
|
||||
const goal_name = ref('');
|
||||
const all_tags: Ref<string[]> = ref([ ...data.all_tags ]);
|
||||
const goal_tags: Ref<string[]> = ref([ ]);
|
||||
|
||||
goal_name.value = data.goal?.name ?? '';
|
||||
goal_tags.value = data.goal?.tags ?? [];
|
||||
const markdown_preview = ref(false);
|
||||
|
||||
const delete_label = ref('Delete (5)');
|
||||
const delete_enabled = ref(false);
|
||||
@ -91,8 +147,17 @@ onMounted(() => {
|
||||
}, 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;
|
||||
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) {
|
||||
@ -111,24 +176,29 @@ function addTag(value: string, done: Function) {
|
||||
}
|
||||
|
||||
done(value, 'toggle');
|
||||
|
||||
console.log('All tags', all_tags);
|
||||
console.log('Goal tags', goal_tags);
|
||||
}
|
||||
|
||||
function deleteGoal() {
|
||||
emit('deleteGoal', data.goal);
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
emit('cancel');
|
||||
}
|
||||
|
||||
function emitGoal() {
|
||||
function saveGoal() {
|
||||
const goal = new BingoGoal(goal_name.value);
|
||||
goal.description = goal_description.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>
|
||||
|
||||
|
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;
|
||||
}
|
||||
|
||||
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 {
|
||||
const existingGroup = this.items.find(g => g === group);
|
||||
if (existingGroup) {
|
||||
|
@ -7,6 +7,8 @@ import {
|
||||
export default class BingoGoal {
|
||||
name: string;
|
||||
|
||||
description: string = '';
|
||||
|
||||
tags: string[] = [];
|
||||
|
||||
possible_spaces: number[] = [];
|
||||
|
@ -17,9 +17,16 @@ export default class Parser {
|
||||
return game;
|
||||
}
|
||||
|
||||
static to_json(game: BingoGame): Record<string, any> {
|
||||
static to_json(game: BingoGame): JSON {
|
||||
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;
|
||||
|
||||
console.log(games_array);
|
||||
games.value = games_array.map(game => {
|
||||
const bingo_game = Parser.from_json_raw(game);
|
||||
|
||||
@ -42,7 +41,6 @@ export const useLocalGamesStore = defineStore('localGames', () => {
|
||||
// });
|
||||
|
||||
console.info('--- Finished fetching games!');
|
||||
console.log(games.value);
|
||||
|
||||
console.info('');
|
||||
}
|
||||
@ -62,7 +60,7 @@ export const useLocalGamesStore = defineStore('localGames', () => {
|
||||
return games.value.some(game => game.id === id);
|
||||
}
|
||||
|
||||
function addGame(game: BingoGame) {
|
||||
async function addGame(game: BingoGame) {
|
||||
if (hasGame(game.id)) {
|
||||
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;
|
||||
|
||||
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)) {
|
||||
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 }' ...`);
|
||||
|
||||
games.value = games.value.filter(game => game.id !== id);
|
||||
saveGames();
|
||||
|
||||
await saveGames();
|
||||
}
|
||||
|
||||
return {
|
||||
@ -100,6 +116,7 @@ export const useLocalGamesStore = defineStore('localGames', () => {
|
||||
|
||||
hasGame,
|
||||
addGame,
|
||||
updateGame,
|
||||
deleteGame
|
||||
};
|
||||
}, {
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<!-- <EditorComponent/> -->
|
||||
<EditorComponent/>
|
||||
|
||||
<GameList :game="yakuza" :edit-mode="true"/>
|
||||
<!-- <GameList :game="yakuza" :edit-mode="true"/> -->
|
||||
|
||||
|
||||
<!-- <q-dialog v-model="dialog">
|
||||
@ -12,24 +12,24 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type BingoGame from '@/js/lib/BingoGame.ts';
|
||||
// import type BingoGame from '@/js/lib/BingoGame.ts';
|
||||
|
||||
import {
|
||||
useLocalGamesStore
|
||||
} from '@/stores/local-games.ts';
|
||||
const localGames = useLocalGamesStore();
|
||||
// import {
|
||||
// useLocalGamesStore
|
||||
// } from '@/stores/local-games.ts';
|
||||
// const localGames = useLocalGamesStore();
|
||||
|
||||
const yakuza = ref();
|
||||
onMounted(async() => {
|
||||
await localGames.fetchGames();
|
||||
// const yakuza = ref();
|
||||
// onMounted(async() => {
|
||||
// await localGames.fetchGames();
|
||||
|
||||
yakuza.value = localGames.games[0];
|
||||
console.log(yakuza.value);
|
||||
});
|
||||
// yakuza.value = localGames.games[0];
|
||||
// console.log(yakuza.value);
|
||||
// });
|
||||
|
||||
|
||||
const dialog = ref<boolean>(false);
|
||||
const selected_game = ref<BingoGame | undefined>();
|
||||
// const dialog = ref<boolean>(false);
|
||||
// const selected_game = ref<BingoGame | undefined>();
|
||||
|
||||
|
||||
</script>
|
||||
|
Loading…
Reference in New Issue
Block a user