WIP CreatePage with BoardView implementation

This commit is contained in:
Baer 2024-08-25 22:23:37 +02:00
parent efd8ac2ab9
commit d2a0551ddd
13 changed files with 197 additions and 148 deletions

View File

@ -46,6 +46,7 @@ $body-secondary-bg: $light-accented;
$dropdown-link-hover-bg: $dark-accented;
$modal-fade-transform: scale(.75);
$breadcrumb-divider: quote(">");
// 3. Include remainder of required Bootstrap stylesheets (including any separate color mode stylesheets)
@import "bootstrap/scss/variables";

View File

@ -0,0 +1,25 @@
<script setup lang="ts">
import type { BoardEntry } from '@/models/board/BoardEntry';
const props = defineProps<{
boardEntry: BoardEntry,
}>();
</script>
<template>
<div class="mx-3 h-100 d-flex flex-column">
<div class="row">
<div class="col text-center p-3">
<h2>
{{ boardEntry.name }}
</h2>
</div>
</div>
<div class="row h-100">
<div class="col">
Entry
</div>
</div>
</div>
</template>

View File

@ -20,10 +20,12 @@ function boardEntrySelected(cIndex: number, bEIndex: number){
emit("boardEntrySelected", cIndex, bEIndex);
}
</script>
<template>
<div class="mx-3 h-100">
<div class="mx-3 h-100 d-flex flex-column">
<div class="row">
<div class="col text-center p-3">
<h2>
@ -33,17 +35,22 @@ function boardEntrySelected(cIndex: number, bEIndex: number){
</div>
<div class="row h-100">
<template v-for="(category, categoryIndex) in props.board.categories " :key="category.name">
<div class="col">
<div class="d-flex flex-column g-1 h-100">
<button class="flex-fill board-card-max-height card bg-primary w-100" @click="categorySelected(categoryIndex)">
<div class="col pb-2">
<div class="d-flex flex-column h-100">
<button class="flex-fill board-card-max-height card bg-primary w-100 my-1" @click="categorySelected(categoryIndex)" :title="board.categories[categoryIndex].description">
<div class="card-body d-flex align-items-center justify-content-center">
{{ category.name }}
</div>
</button>
<template v-for=" (boardEntry, boardEntryIndex) in category.boardEntries " :key="boardEntry.name">
<button class="flex-fill board-card-max-height card bg-body-secondary w-100" @click="boardEntrySelected(categoryIndex, boardEntryIndex)">
<button class="flex-fill board-card-max-height card bg-body-secondary w-100 my-1" @click="boardEntrySelected(categoryIndex, boardEntryIndex)">
<div class="card-body d-flex align-items-center justify-content-center">
{{ boardEntry.name }}
<template v-if="board.pointsAreTitle">
{{ boardEntry.points }}
</template>
<template v-else>
{{ boardEntry.name }}
</template>
</div>
</button>
</template>

View File

@ -36,13 +36,19 @@ function editBoardEntry(cIndex: number, bEIndex: number){
<EditCategoryPanel v-model="board" :categoryIndex="props.categoryIndex" v-else-if="props.categoryIndex !== null && props.boardEntryIndex === null" @editBoard="editBoard" @editBoardEntry="editBoardEntry"/>
<EditBoardEntryPanel v-model="board" :categoryIndex="props.categoryIndex" :boardEntryIndex="props.boardEntryIndex" v-else-if="props.categoryIndex !== null && props.boardEntryIndex !== null" @editCategory="editCategory" @editBoard="editBoard" />
</div>
<div class="border-top border-2 border-primary p-2 d-flex justify-content-evenly align-items-center g-3">
<button class="btn btn-primary">
Save
</button>
<button class="btn btn-danger">
Abort and Exit
</button>
<div class="border-top border-2 border-primary p-2">
<div class="d-flex align-items-center">
<div class="flex-grow-1 me-1">
<button class="btn btn-primary w-100">
Save
</button>
</div>
<div class="flex-grow-1 ms-1">
<button class="btn btn-danger w-100">
Exit
</button>
</div>
</div>
</div>
</div>
</template>

View File

@ -1,129 +1,114 @@
<script setup lang="ts">
import type { Board } from '@/models/board/Board';
import { Category } from '@/models/board/Category';
import { ref } from 'vue';
import { computed, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { VueDraggable } from 'vue-draggable-plus';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
import { BoardEntry } from '@/models/board/BoardEntry';
import { Answer } from '@/models/board/Answer';
const { t } = useI18n();
const emit = defineEmits<{
categorySelected: [index: number],
}>()
const board = defineModel<Board>( { required: true } );
const newCategoryName = ref( '' );
function addCategory() {
if( board.value.categories.length >= 12 ) {
const props = defineProps<{
categoryIndex: number,
boardEntryIndex: number,
}>();
const emit = defineEmits<{
editBoard: [],
editCategory: [categoryIndex: number],
}>()
const boardEntry = computed( () => {
return board.value.categories[props.categoryIndex].boardEntries[props.boardEntryIndex];
})
const newBoardEntryName = ref( '' );
function addBoardEntry() {
if( board.value.categories[props.categoryIndex].boardEntries.length >= 10 ) {
return;
}
const newCategory = new Category( newCategoryName.value, '', board.value );
board.value.categories.push( newCategory );
const answer = new Answer('', undefined);
const newBoardEntry = new BoardEntry( newBoardEntryName.value, 0, board.value.categories[props.categoryIndex], answer, [] );
board.value.categories[props.categoryIndex].boardEntries.push( newBoardEntry );
}
function deleteCategory( index: number ) {
board.value.categories.splice( index, 1 );
function deleteBoardEntry( index: number ) {
board.value.categories[props.categoryIndex].boardEntries.splice( index, 1 );
}
function moveCategoryUp( index: number ) {
if( index === 0 || board.value.categories.length <= 1 ) {
function moveBoardEntryUp( index: number ) {
if( index === 0 || board.value.categories[props.categoryIndex].boardEntries.length <= 1 ) {
return;
}
const tmp = board.value.categories[index];
board.value.categories[index] = board.value.categories[index - 1];
board.value.categories[index - 1] = tmp;
const tmp = board.value.categories[props.categoryIndex].boardEntries[index];
board.value.categories[props.categoryIndex].boardEntries[index] = board.value.categories[props.categoryIndex].boardEntries[index - 1];
board.value.categories[props.categoryIndex].boardEntries[index - 1] = tmp;
}
function moveCategoryDown( index: number ) {
if( index === board.value.categories.length - 1 || board.value.categories.length <= 1 ) {
function moveBoardEntryDown( index: number ) {
if( index === board.value.categories[props.categoryIndex].boardEntries.length - 1 || board.value.categories[props.categoryIndex].boardEntries.length <= 1 ) {
return;
}
const tmp = board.value.categories[index];
board.value.categories[index] = board.value.categories[index + 1];
board.value.categories[index + 1] = tmp;
const tmp = board.value.categories[props.categoryIndex].boardEntries[index];
board.value.categories[props.categoryIndex].boardEntries[index] = board.value.categories[props.categoryIndex].boardEntries[index + 1];
board.value.categories[props.categoryIndex].boardEntries[index + 1] = tmp;
}
function openCategory( index: number ) {
emit("categorySelected", index);
function openBoard() {
emit("editBoard");
}
function openCategory(categoryIndex: number) {
emit("editCategory", categoryIndex);
}
</script>
<template>
<div class="d-flex flex-column h-100">
<div class="flex-grow-1 overflow-y-auto p-2">
<div class="row me-0">
<div class="flex-grow-1 px-2">
<div class="row">
<div class="col">
<h2 class="border-bottom border-3 border-primary">{{ t( "board.infos" ) }}</h2>
<label for="board-name">{{ t( 'board.name' ) }}</label>
<input type="text" id="board-name" class="form-control" v-model="board.name" :placeholder="t( 'board.name' )">
</div>
</div>
<div class="row me-0 mt-3">
<div class="col">
<h4>{{ t( "board.category.label", 2 ) }}</h4>
<template v-if=" board.categories.length === 0 ">
{{ t( "board.category.label", 0 ) }}
</template>
<template v-else>
<VueDraggable v-model="board.categories" item-key="id" :animation="150" ghost-class="draggable-ghost"
tag="ul" class="list-group" handle=".drag-handle">
<template v-for="( category, categoryIndex) of board.categories" :key="category.id">
<li class="list-group-item bg-body-secondary p-0">
<div class="d-flex justify-content-between w-100">
<div class="flex-grow-1 d-flex align-items-center g-3 justify-content-start">
<div
class="drag-handle h-100 d-flex justify-content-center align-items-center cursor-move border-end border-1 bg-primary bg-opacity-10">
<FontAwesomeIcon :icon="['fas', 'grip-lines']" class="px-2" />
</div>
<input type="text" :id="`category-name-${categoryIndex}`" v-model="category.name"
class="form-control rounded-0 h-100">
</div>
<div class="d-flex">
<button class="btn btn-primary rounded-0" @click="openCategory( categoryIndex )">
<FontAwesomeIcon :icon="['fas', 'edit']" size="sm" />
</button>
<div class="d-flex flex-column justify-content-center align-items-center h-100" role="group"
aria-label="Vertical button group">
<button class="btn btn-primary rounded-0 py-0 lh-sm" @click="moveCategoryUp( categoryIndex )"
:disabled="categoryIndex === 0">
<FontAwesomeIcon :icon="['fas', 'angle-up']" size="xs" />
</button>
<button class="btn btn-primary rounded-0 py-0 lh-sm" @click="moveCategoryDown( categoryIndex )"
:disabled="categoryIndex === board.categories.length - 1">
<FontAwesomeIcon :icon="['fas', 'angle-down']" size="xs" />
</button>
</div>
<button class="btn btn-danger rounded-start-0 rounded-0"
:class="[{ 'rounded-bottom-end': categoryIndex === board.categories.length - 1 }, { 'rounded-top-end': categoryIndex === 0 }]"
@click="deleteCategory( categoryIndex )">
<FontAwesomeIcon :icon="['fas', 'trash']" size="sm" />
</button>
</div>
</div>
</li>
</template>
</VueDraggable>
</template>
<label class="mt-2" for="new-category-name">{{ t( "board.category.add" ) }}</label>
<div class="input-group mb-3">
<input type="text" id="new-category-name" class="form-control" v-model="newCategoryName"
:placeholder="t( 'board.category.name' )" @keyup.enter="addCategory">
<button class="btn btn-primary" @click="addCategory">
<FontAwesomeIcon :icon="['fas', 'plus']" />
<div class="d-flex align-items-center mb-2 overflow-x-auto pb-2">
<button class="btn btn-sm btn-outline-primary" :title="t('board.category.back')" @click="openCategory(props.categoryIndex)">
<FontAwesomeIcon :icon="['fas', 'angle-left']"/>
</button>
<nav class="flex-grow-1 ms-2" aria-label="breadcrumb">
<ol class="breadcrumb mb-0 flex-nowrap">
<li class="breadcrumb-item text-truncate">
<a href="#" @click="openBoard">
{{ board.name }}
</a>
</li>
<li class="breadcrumb-item text-truncate">
<a href="#" @click="openCategory( props.categoryIndex )">
{{ board.categories[props.categoryIndex].name }}
</a>
</li>
<li class="breadcrumb-item active text-truncate" aria-current="page">
{{ boardEntry.name }}
</li>
</ol>
</nav>
</div>
</div>
</div>
</div>
<div class="border-top border-2 border-primary p-2 d-flex justify-content-evenly align-items-center g-3">
<button class="btn btn-primary">
Save
</button>
<button class="btn btn-danger">
Abort and Exit
</button>
<div class="row">
<div class="col">
<div class="d-flex border-bottom border-3 border-primary align-items-center mb-2">
<h2 class="flex-grow-1 ms-2">{{ t( "board.boardentry.infos" ) }}</h2>
</div>
<label for="category-name">{{ t( 'board.boardentry.name' ) }}</label>
<input type="text" id="category-name" class="form-control mb-2" v-model="boardEntry.name" :placeholder="t( 'board.boardentry.name' )">
<label for="board-entry-points">{{ t( 'board.boardentry.points' ) }}</label>
<input type="text" id="board-entry-points" class="form-control" v-model="boardEntry.points" :placeholder="t( 'board.boardentry.points' )">
</div>
</div>
<div class="row mt-3">
<div class="col">
Questions
</div>
</div>
</div>
</div>
</template>

View File

@ -52,15 +52,19 @@ function openCategory( index: number ) {
<template>
<div class="d-flex flex-column h-100">
<div class="flex-grow-1 overflow-y-auto p-2">
<div class="row me-0">
<div class="flex-grow-1 p-2">
<div class="row">
<div class="col">
<h2 class="border-bottom border-3 border-primary">{{ t( "board.infos" ) }}</h2>
<label for="board-name">{{ t( 'board.name' ) }}</label>
<input type="text" id="board-name" class="form-control" v-model="board.name" :placeholder="t( 'board.name' )">
<div class="form-check mt-2">
<input class="form-check-input" type="checkbox" id="points-for-title" v-model="board.pointsAreTitle">
<label for="points-for-title" class="form-check-label">{{ t( 'board.options.pointsForTitle' ) }}</label>
</div>
</div>
</div>
<div class="row me-0 mt-3">
<div class="row mt-3">
<div class="col">
<h4>{{ t( "board.category.label", 2 ) }}</h4>
<template v-if=" board.categories.length === 0 ">
@ -117,14 +121,6 @@ function openCategory( index: number ) {
</div>
</div>
</div>
<div class="border-top border-2 border-primary p-2 d-flex justify-content-evenly align-items-center g-3">
<button class="btn btn-primary">
Save
</button>
<button class="btn btn-danger">
Abort and Exit
</button>
</div>
</div>
</template>

View File

@ -16,22 +16,23 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
boardEntrySelected: [categoryIndex: number, boardEntryIndex: number],
editBoardEntry: [categoryIndex: number, boardEntryIndex: number],
editBoard: [],
}>()
const newBoardEntryName = ref( '' );
function addBoardEntry() {
if( board.value.categories.length >= 12 ) {
if( board.value.categories[props.categoryIndex].boardEntries.length >= 10 ) {
return;
}
const answer = new Answer('', undefined);
const newBoardEntry = new BoardEntry( newBoardEntryName.value, board.value.categories[props.categoryIndex], answer, [] );
const newBoardEntry = new BoardEntry( newBoardEntryName.value, 0, board.value.categories[props.categoryIndex], answer, [] );
board.value.categories[props.categoryIndex].boardEntries.push( newBoardEntry );
}
function deleteCategory( index: number ) {
board.value.categories.splice( index, 1 );
function deleteBoardEntry( index: number ) {
board.value.categories[props.categoryIndex].boardEntries.splice( index, 1 );
}
function moveBoardEntryUp( index: number ) {
@ -53,25 +54,38 @@ function moveBoardEntryDown( index: number ) {
}
function openBoardEntry( index: number ) {
emit("boardEntrySelected", props.categoryIndex, index);
emit("editBoardEntry", props.categoryIndex, index);
}
function openBoard() {
emit("editBoard");
}
</script>
<template>
<div class="d-flex flex-column h-100">
<div class="flex-grow-1 overflow-y-auto p-2">
<div class="row me-0">
<div class="flex-grow-1 p-2">
<div class="row">
<div class="col">
<h2 class="border-bottom border-3 border-primary">{{ t( "board.category.infos" ) }}</h2>
<label for="board-name">{{ t( 'board.category.name' ) }}</label>
<input type="text" id="board-name" class="form-control" v-model="board.name" :placeholder="t( 'board.name' )">
<div class="d-flex border-bottom border-3 border-primary align-items-center mb-2">
<button class="btn btn-sm btn-outline-primary mb-1" :title="t('board.category.back')" @click="openBoard">
<FontAwesomeIcon :icon="['fas', 'angle-left']"/>
</button>
<h2 class="flex-grow-1 ms-2">{{ t( "board.category.infos" ) }}</h2>
</div>
<label for="category-name">{{ t( 'board.category.name' ) }}</label>
<input type="text" id="category-name" class="form-control mb-2" v-model="board.categories[props.categoryIndex].name" :placeholder="t( 'board.category.name' )">
<label for="category-description">{{ t( 'board.category.description' ) }}</label>
<textarea id="category-description" class="form-control" v-model="board.categories[props.categoryIndex].description" :placeholder="t( 'board.category.description' )">
</textarea>
</div>
</div>
<div class="row me-0 mt-3">
<div class="row mt-3">
<div class="col">
<h4>{{ t( "board.boardentry.label", 2 ) }}</h4>
<template v-if=" board.categories[props.categoryIndex].boardEntries.length === 0 ">
{{ t( "board.boardentry.label", 0 ) }}
<p>
{{ t( "board.boardentry.label", 0 ) }}
</p>
</template>
<template v-else>
<VueDraggable v-model="board.categories[props.categoryIndex].boardEntries" item-key="id" :animation="150" ghost-class="draggable-ghost"
@ -103,8 +117,8 @@ function openBoardEntry( index: number ) {
</button>
</div>
<button class="btn btn-danger rounded-start-0 rounded-0"
:class="[{ 'rounded-bottom-end': categoryIndex === board.categories[props.categoryIndex].boardEntries.length - 1 }, { 'rounded-top-end': categoryIndex === 0 }]"
@click="deleteCategory( categoryIndex )">
:class="[{ 'rounded-bottom-end': boardEntryIndex === board.categories[props.categoryIndex].boardEntries.length - 1 }, { 'rounded-top-end': boardEntryIndex === 0 }]"
@click="deleteBoardEntry( boardEntryIndex )">
<FontAwesomeIcon :icon="['fas', 'trash']" size="sm" />
</button>
</div>
@ -124,14 +138,6 @@ function openBoardEntry( index: number ) {
</div>
</div>
</div>
<div class="border-top border-2 border-primary p-2 d-flex justify-content-evenly align-items-center g-3">
<button class="btn btn-primary">
Save
</button>
<button class="btn btn-danger">
Abort and Exit
</button>
</div>
</div>
</template>

View File

@ -9,6 +9,9 @@ import type NavBar from '@/components/blocks/NavBar.vue';
import BoardView from '@/components/blocks/BoardView.vue';
import CreatePanel from '@/components/blocks/CreatePanel.vue';
import { Category } from '@/models/board/Category';
import BoardEntryView from '../blocks/BoardEntryView.vue';
import { BoardEntry } from '@/models/board/BoardEntry';
import { Answer } from '@/models/board/Answer';
const { t } = useI18n();
@ -22,6 +25,9 @@ const restHeight = computed( () => {
const board = ref<Board>(new Board("New Board"));
const board1 = ref<Board>(new Board("New Board", [new Category("Test1", "", board.value as Board),new Category("Test2", "", board.value as Board)]));
const answer = new Answer('', undefined);
const newBoardEntry = new BoardEntry( "Test Entry 1", 0, board1.value.categories[0] as Category, answer, [] );
board1.value.categories[0].boardEntries.push( newBoardEntry );
const categoryIndex = ref<number | null>(null);
const boardEntryIndex = ref<number | null>(null);
@ -45,10 +51,11 @@ function showBoard(){
<template>
<div :style="restHeight">
<div class="row h-100">
<div class="col-9 overflow-y-auto pe-0">
<BoardView :board="(board1 as Board)" />
<div class="col-9 pe-0">
<BoardEntryView v-if="categoryIndex !== null && boardEntryIndex !== null" :boardEntry="(board1.categories[categoryIndex].boardEntries[boardEntryIndex] as BoardEntry)" />
<BoardView v-else :board="(board1 as Board)" @categorySelected="showCategory" @boardEntrySelected="showBoardEntry" />
</div>
<div class="col-3 px-0 h-100 overflow-auto border-start border-3 border-primary">
<div class="col-3 ps-0 h-100 overflow-auto border-start border-2 border-primary">
<CreatePanel v-model="(board1 as Board)" :categoryIndex="categoryIndex" :boardEntryIndex="boardEntryIndex" @editBoard="showBoard" @editCategory="showCategory" @editBoardEntry="showBoardEntry"/>
</div>
</div>

View File

@ -69,13 +69,26 @@
"textCode": "Wanna create a board and host a game yourself?"
},
"board": {
"label": "Board",
"name": "Board Name",
"infos": "Board Infos",
"options": {
"pointsForTitle": "Show points amount instead of entry name"
},
"category": {
"infos": "Category Infos",
"label": "No Category | Category | Categories | {count} Categories",
"name": "Category name",
"description": "Category description",
"add": "Add Category"
"name": "Category Name",
"description": "Category Description",
"add": "Add Category",
"back": "Back to Board"
},
"boardentry": {
"infos":"Entry Infos",
"points": "Point | Points | {count} Points",
"label": "No Entries | Entry | Entries | {count} Entries",
"add": "Add Entry",
"name": "Entry Name"
}
},
"theme": {

View File

@ -9,7 +9,7 @@ import '@/assets/scss/customized_bootstrap.scss';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
import { library } from '@fortawesome/fontawesome-svg-core';
import { faSun, faMoon, faCircleHalfStroke, faEdit, faPlay, faSpinner, faLanguage, faGlobe, faPlus, faTrash, faGripLines, faAngleUp, faAngleDown } from '@fortawesome/free-solid-svg-icons';
import { faSun, faMoon, faCircleHalfStroke, faEdit, faPlay, faSpinner, faLanguage, faGlobe, faPlus, faTrash, faGripLines, faAngleUp, faAngleDown, faAngleLeft } from '@fortawesome/free-solid-svg-icons';
import enMessages from './locales/en.json';
import deMessages from './locales/de.json';
@ -38,6 +38,7 @@ library.add(
faGripLines,
faAngleUp,
faAngleDown,
faAngleLeft,
)
const app = createApp( App );

View File

@ -5,6 +5,7 @@ export class Board{
constructor(
public name: string,
public categories: Array<Category> = [],
public pointsAreTitle: boolean = false,
public owner: User | undefined = undefined,
private id: number | undefined = undefined,
){}

View File

@ -5,6 +5,7 @@ import type { Question } from './Question';
export class BoardEntry{
constructor(
public name: string,
public points: number,
public category: Category,
public answer: Answer,
public questions: Array<Question>,

View File

@ -18,5 +18,5 @@ export default defineConfig({
build:{
outDir: '../resources/static',
emptyOutDir: true,
}
},
});