Basic goal editing / creation / deletion
Doesn't save yet
This commit is contained in:
parent
cc9282e661
commit
fd4adedcb3
4
components.d.ts
vendored
4
components.d.ts
vendored
@ -7,8 +7,12 @@ export {}
|
|||||||
|
|
||||||
declare module 'vue' {
|
declare module 'vue' {
|
||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
|
EditGoal: typeof import('./src/composables/EditGoal.vue')['default']
|
||||||
|
EditGoalDialog: typeof import('./src/composables/EditGoalDialog.vue')['default']
|
||||||
EditorComponent: typeof import('./src/components/EditorComponent.vue')['default']
|
EditorComponent: typeof import('./src/components/EditorComponent.vue')['default']
|
||||||
|
GameList: typeof import('./src/composables/GameList.vue')['default']
|
||||||
GeneratorComponent: typeof import('./src/components/GeneratorComponent.vue')['default']
|
GeneratorComponent: typeof import('./src/components/GeneratorComponent.vue')['default']
|
||||||
|
GoalEditorDialog: typeof import('./src/components/GoalEditorDialog.vue')['default']
|
||||||
MarkdownRenderer: typeof import('./src/components/MarkdownRenderer.vue')['default']
|
MarkdownRenderer: typeof import('./src/components/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']
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@quasar/extras": "^1.16.8",
|
"@quasar/extras": "^1.16.8",
|
||||||
|
"@sindresorhus/string-hash": "^2.0.0",
|
||||||
"@types/markdown-it": "^13.0.6",
|
"@types/markdown-it": "^13.0.6",
|
||||||
"@vue/runtime-core": "^3.3.8",
|
"@vue/runtime-core": "^3.3.8",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
|
15
pnpm-lock.yaml
generated
15
pnpm-lock.yaml
generated
@ -8,6 +8,9 @@ dependencies:
|
|||||||
'@quasar/extras':
|
'@quasar/extras':
|
||||||
specifier: ^1.16.8
|
specifier: ^1.16.8
|
||||||
version: 1.16.8
|
version: 1.16.8
|
||||||
|
'@sindresorhus/string-hash':
|
||||||
|
specifier: ^2.0.0
|
||||||
|
version: 2.0.0
|
||||||
'@types/markdown-it':
|
'@types/markdown-it':
|
||||||
specifier: ^13.0.6
|
specifier: ^13.0.6
|
||||||
version: 13.0.6
|
version: 13.0.6
|
||||||
@ -791,6 +794,18 @@ packages:
|
|||||||
resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
|
resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/@sindresorhus/fnv1a@3.1.0:
|
||||||
|
resolution: {integrity: sha512-KV321z5m/0nuAg83W1dPLy85HpHDk7Sdi4fJbwvacWsEhAh+rZUW4ZfGcXmUIvjZg4ss2bcwNlRhJ7GBEUG08w==}
|
||||||
|
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@sindresorhus/string-hash@2.0.0:
|
||||||
|
resolution: {integrity: sha512-eNmMOd5DZkiu9LxIeHdh1XvDbcpFXV4HdBqg9hlg8YNKDvE6qmHiJ+Vy+rFrzXofRYmtheNv4A3ESad8unxwwA==}
|
||||||
|
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||||
|
dependencies:
|
||||||
|
'@sindresorhus/fnv1a': 3.1.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@stylistic/eslint-plugin-js@1.2.0:
|
/@stylistic/eslint-plugin-js@1.2.0:
|
||||||
resolution: {integrity: sha512-1Zi/AlQzOzTlTegupd3vrUYHd02ilvk7x5O9ZRFjYGtUcwHVk+WTEKk/3Nmr8yuvzEiXqUNFJ8bv8b4rLYCPRQ==}
|
resolution: {integrity: sha512-1Zi/AlQzOzTlTegupd3vrUYHd02ilvk7x5O9ZRFjYGtUcwHVk+WTEKk/3Nmr8yuvzEiXqUNFJ8bv8b4rLYCPRQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -205,4 +205,3 @@ watch(learn_more, newValue => {
|
|||||||
selected_game.value = undefined;
|
selected_game.value = undefined;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@/js/lib/LocalGames
|
|
||||||
|
0
src/components/GoalEditorDialog.vue
Normal file
0
src/components/GoalEditorDialog.vue
Normal file
134
src/composables/EditGoalDialog.vue
Normal file
134
src/composables/EditGoalDialog.vue
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
|
||||||
|
<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-select
|
||||||
|
label="Select Tags"
|
||||||
|
square
|
||||||
|
filled
|
||||||
|
v-model="goal_tags"
|
||||||
|
use-input
|
||||||
|
use-chips
|
||||||
|
multiple
|
||||||
|
clearable
|
||||||
|
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="emitGoal()" :disabled="!canSave()"/>
|
||||||
|
</q-card-actions>
|
||||||
|
</q-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
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'
|
||||||
|
]);
|
||||||
|
|
||||||
|
const {
|
||||||
|
data
|
||||||
|
} = defineProps<{
|
||||||
|
data: {
|
||||||
|
all_tags: 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 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);
|
||||||
|
});
|
||||||
|
|
||||||
|
function canSave() {
|
||||||
|
return goal_name.value?.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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');
|
||||||
|
|
||||||
|
console.log('All tags', all_tags);
|
||||||
|
console.log('Goal tags', goal_tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteGoal() {
|
||||||
|
emit('deleteGoal', data.goal);
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancel() {
|
||||||
|
emit('cancel');
|
||||||
|
}
|
||||||
|
|
||||||
|
function emitGoal() {
|
||||||
|
const goal = new BingoGoal(goal_name.value);
|
||||||
|
goal.tags = goal_tags.value;
|
||||||
|
|
||||||
|
emit('emitGoal', goal);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
308
src/composables/GameList.vue
Normal file
308
src/composables/GameList.vue
Normal file
@ -0,0 +1,308 @@
|
|||||||
|
<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>
|
||||||
|
|
@ -3,12 +3,14 @@ import BingoCategory from '@/js/lib/BingoCategory.ts';
|
|||||||
import BingoGoal from '@/js/lib/BingoGoal.ts';
|
import BingoGoal from '@/js/lib/BingoGoal.ts';
|
||||||
import BingoGoalList from '@/js/lib/BingoGoalList.ts';
|
import BingoGoalList from '@/js/lib/BingoGoalList.ts';
|
||||||
import SuccessResponse from '@/js/lib/SuccessResponse.ts';
|
import SuccessResponse from '@/js/lib/SuccessResponse.ts';
|
||||||
import Util from '@/js/lib/Util.ts';
|
import {
|
||||||
|
stringCompare
|
||||||
|
} from '@/js/lib/Util.ts';
|
||||||
import {
|
import {
|
||||||
Transform, plainToInstance, type TransformFnParams, Exclude
|
Transform, plainToInstance, type TransformFnParams, Exclude
|
||||||
} from 'class-transformer';
|
} from 'class-transformer';
|
||||||
|
|
||||||
type BingoCategoryOrGoalList = BingoCategory | BingoGoalList;
|
export type BingoCategoryOrGoalList = BingoCategory | BingoGoalList;
|
||||||
|
|
||||||
export default class BingoGame {
|
export default class BingoGame {
|
||||||
id: string;
|
id: string;
|
||||||
@ -86,7 +88,7 @@ export default class BingoGame {
|
|||||||
|
|
||||||
removeItemByName(name: string): SuccessResponse {
|
removeItemByName(name: string): SuccessResponse {
|
||||||
const group = this.items.find(
|
const group = this.items.find(
|
||||||
g => Util.stringCompare(g.name, name)
|
g => stringCompare(g.name, name)
|
||||||
);
|
);
|
||||||
if (!group) {
|
if (!group) {
|
||||||
return SuccessResponse.error('Category not found');
|
return SuccessResponse.error('Category not found');
|
||||||
@ -95,6 +97,26 @@ export default class BingoGame {
|
|||||||
return this.removeItem(group);
|
return this.removeItem(group);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAllTags(): string[] {
|
||||||
|
const tags: string[] = [];
|
||||||
|
|
||||||
|
this.items.map(group => {
|
||||||
|
const goal_lists = group instanceof BingoGoalList ? [ group ] : group.goal_lists;
|
||||||
|
|
||||||
|
goal_lists.map(subgroup => {
|
||||||
|
subgroup.goals.map(goal => {
|
||||||
|
goal.tags.map(tag => {
|
||||||
|
if (!tags.includes(tag)) {
|
||||||
|
tags.push(tag);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return tags;
|
||||||
|
}
|
||||||
|
|
||||||
getGoalsByTags(...tags: string[]): BingoGoal[] {
|
getGoalsByTags(...tags: string[]): BingoGoal[] {
|
||||||
const goals: BingoGoal[] = [];
|
const goals: BingoGoal[] = [];
|
||||||
|
|
||||||
@ -105,7 +127,7 @@ export default class BingoGame {
|
|||||||
subgroup.goals.map(goal => {
|
subgroup.goals.map(goal => {
|
||||||
if (goal.tags.some(
|
if (goal.tags.some(
|
||||||
tag => tags.some(
|
tag => tags.some(
|
||||||
inputTag => Util.stringCompare(tag, inputTag)
|
inputTag => stringCompare(tag, inputTag)
|
||||||
)
|
)
|
||||||
)) {
|
)) {
|
||||||
goals.push(goal);
|
goals.push(goal);
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
|
||||||
import SuccessResponse from '@/js/lib/SuccessResponse.js';
|
import SuccessResponse from '@/js/lib/SuccessResponse.js';
|
||||||
import Util from './Util';
|
import {
|
||||||
|
stringCompare
|
||||||
|
} from '@/js/lib/Util.ts';
|
||||||
|
|
||||||
export default class BingoGoal {
|
export default class BingoGoal {
|
||||||
name: string;
|
name: string;
|
||||||
@ -15,7 +17,7 @@ export default class BingoGoal {
|
|||||||
|
|
||||||
addTag(tag: string): SuccessResponse {
|
addTag(tag: string): SuccessResponse {
|
||||||
if (this.tags.some(
|
if (this.tags.some(
|
||||||
existingTag => Util.stringCompare(existingTag, tag)
|
existingTag => stringCompare(existingTag, tag)
|
||||||
)) {
|
)) {
|
||||||
return SuccessResponse.error('Tag already exists');
|
return SuccessResponse.error('Tag already exists');
|
||||||
}
|
}
|
||||||
@ -28,13 +30,13 @@ export default class BingoGoal {
|
|||||||
|
|
||||||
removeTag(tag: string): SuccessResponse {
|
removeTag(tag: string): SuccessResponse {
|
||||||
if (!this.tags.some(
|
if (!this.tags.some(
|
||||||
existingTag => Util.stringCompare(existingTag, tag)
|
existingTag => stringCompare(existingTag, tag)
|
||||||
)) {
|
)) {
|
||||||
return SuccessResponse.error('Tag doesn\'t exist');
|
return SuccessResponse.error('Tag doesn\'t exist');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.tags = this.tags.filter(
|
this.tags = this.tags.filter(
|
||||||
existingTag => !Util.stringCompare(existingTag, tag)
|
existingTag => !stringCompare(existingTag, tag)
|
||||||
);
|
);
|
||||||
|
|
||||||
return SuccessResponse.success();
|
return SuccessResponse.success();
|
||||||
|
@ -1,7 +1,33 @@
|
|||||||
export default class Util {
|
const colors = [
|
||||||
static stringCompare(str1: string, str2: string): boolean {
|
'red',
|
||||||
return str1.localeCompare(str2, undefined, {
|
'pink',
|
||||||
sensitivity: 'accent'
|
'purple',
|
||||||
}) === 0;
|
'deep-purple',
|
||||||
}
|
'indigo',
|
||||||
|
'blue',
|
||||||
|
'light-blue',
|
||||||
|
'cyan',
|
||||||
|
'teal',
|
||||||
|
'green',
|
||||||
|
'light-green',
|
||||||
|
'lime',
|
||||||
|
'yellow',
|
||||||
|
'amber',
|
||||||
|
'orange',
|
||||||
|
'deep-orange',
|
||||||
|
'brown',
|
||||||
|
'grey',
|
||||||
|
'blue-grey'
|
||||||
|
];
|
||||||
|
|
||||||
|
import stringHash from '@sindresorhus/string-hash';
|
||||||
|
|
||||||
|
export function stringCompare(str1: string, str2: string): boolean {
|
||||||
|
return str1.localeCompare(str2, undefined, {
|
||||||
|
sensitivity: 'accent'
|
||||||
|
}) === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getColorForString(input: string): string {
|
||||||
|
return colors[stringHash(input) % colors.length];
|
||||||
}
|
}
|
||||||
|
@ -42,9 +42,9 @@ export const useLocalGamesStore = defineStore('localGames', () => {
|
|||||||
// });
|
// });
|
||||||
|
|
||||||
console.info('--- Finished fetching games!');
|
console.info('--- Finished fetching games!');
|
||||||
console.info('');
|
|
||||||
|
|
||||||
console.log(games.value);
|
console.log(games.value);
|
||||||
|
|
||||||
|
console.info('');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveGames() {
|
async function saveGames() {
|
||||||
|
@ -1,3 +1,35 @@
|
|||||||
<template>
|
<template>
|
||||||
<EditorComponent/>
|
<!-- <EditorComponent/> -->
|
||||||
|
|
||||||
|
<GameList :game="yakuza" :edit-mode="true"/>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- <q-dialog v-model="dialog">
|
||||||
|
<q-card style="min-width: 700px; max-width: 80vw; min-height: 10vh; max-height: 80vh;">
|
||||||
|
<GoalEditorDialog :tags="tags"/>
|
||||||
|
</q-card>
|
||||||
|
</q-dialog> -->
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type BingoGame from '@/js/lib/BingoGame.ts';
|
||||||
|
|
||||||
|
import {
|
||||||
|
useLocalGamesStore
|
||||||
|
} from '@/stores/local-games.ts';
|
||||||
|
const localGames = useLocalGamesStore();
|
||||||
|
|
||||||
|
const yakuza = ref();
|
||||||
|
onMounted(async() => {
|
||||||
|
await localGames.fetchGames();
|
||||||
|
|
||||||
|
yakuza.value = localGames.games[0];
|
||||||
|
console.log(yakuza.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const dialog = ref<boolean>(false);
|
||||||
|
const selected_game = ref<BingoGame | undefined>();
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
@ -22,7 +22,11 @@ export default defineConfig({
|
|||||||
plugins: [
|
plugins: [
|
||||||
VueRouter(),
|
VueRouter(),
|
||||||
Components({
|
Components({
|
||||||
dts: true
|
dts: true,
|
||||||
|
dirs: [
|
||||||
|
'src/components',
|
||||||
|
'src/composables'
|
||||||
|
]
|
||||||
}),
|
}),
|
||||||
AutoImport({
|
AutoImport({
|
||||||
include: [
|
include: [
|
||||||
|
Loading…
Reference in New Issue
Block a user