list editing, deleting

This commit is contained in:
Markus Thielen 2023-03-17 18:41:43 +01:00
parent 3bb07e7d47
commit 9a53cfbaa6
Signed by: markus
GPG Key ID: 3D4980D3EC9C8E26
6 changed files with 229 additions and 98 deletions

View File

@ -1,34 +1,20 @@
[resolvers.createTask] [resolvers.getUser]
operation_name = "createTask" operation_name = "getUser"
[resolvers.createTask.resolver] [resolvers.getUser.resolver]
command_type = "SQLInsert" command_type = "SQLSelect"
[resolvers.createTask.resolver.command] [resolvers.getUser.resolver.command]
table = "Task" table = "User"
columns = [] columns = []
where_clauses = [] modify_values = []
aggregate_result = true aggregate_result = true
[[resolvers.createTask.resolver.command.modify_values]] [[resolvers.getUser.resolver.command.where_clauses]]
column = "title" table = "User"
value = "'$title'" column = "username"
condition_str = "= '$username'"
[[resolvers.createTask.resolver.command.modify_values]] index = ""
column = "description"
value = "'$description'"
[[resolvers.createTask.resolver.command.modify_values]]
column = "completed"
value = "$completed"
[[resolvers.createTask.resolver.command.modify_values]]
column = "list_id"
value = "'$list.$id'"
[[resolvers.createTask.resolver.command.modify_values]]
column = "user_username"
value = "'$user.$username'"
[resolvers.updateTask] [resolvers.updateTask]
operation_name = "updateTask" operation_name = "updateTask"
@ -63,38 +49,20 @@ column = "id"
condition_str = "= '$id'" condition_str = "= '$id'"
index = "" index = ""
[resolvers.getUser] [resolvers.deleteList]
operation_name = "getUser" operation_name = "deleteList"
[resolvers.getUser.resolver] [resolvers.deleteList.resolver]
command_type = "SQLSelect"
[resolvers.getUser.resolver.command]
table = "User"
columns = []
modify_values = []
aggregate_result = true
[[resolvers.getUser.resolver.command.where_clauses]]
table = "User"
column = "username"
condition_str = "= '$username'"
index = ""
[resolvers.deleteTask]
operation_name = "deleteTask"
[resolvers.deleteTask.resolver]
command_type = "SQLDelete" command_type = "SQLDelete"
[resolvers.deleteTask.resolver.command] [resolvers.deleteList.resolver.command]
table = "Task" table = "List"
columns = [] columns = []
modify_values = [] modify_values = []
aggregate_result = true aggregate_result = true
[[resolvers.deleteTask.resolver.command.where_clauses]] [[resolvers.deleteList.resolver.command.where_clauses]]
table = "Task" table = "List"
column = "id" column = "id"
condition_str = "= '$id'" condition_str = "= '$id'"
index = "" index = ""
@ -119,6 +87,77 @@ value = "'$username'"
column = "name" column = "name"
value = "'$name'" value = "'$name'"
[resolvers.deleteTask]
operation_name = "deleteTask"
[resolvers.deleteTask.resolver]
command_type = "SQLDelete"
[resolvers.deleteTask.resolver.command]
table = "Task"
columns = []
modify_values = []
aggregate_result = true
[[resolvers.deleteTask.resolver.command.where_clauses]]
table = "Task"
column = "id"
condition_str = "= '$id'"
index = ""
[resolvers.createTask]
operation_name = "createTask"
[resolvers.createTask.resolver]
command_type = "SQLInsert"
[resolvers.createTask.resolver.command]
table = "Task"
columns = []
where_clauses = []
aggregate_result = true
[[resolvers.createTask.resolver.command.modify_values]]
column = "title"
value = "'$title'"
[[resolvers.createTask.resolver.command.modify_values]]
column = "description"
value = "'$description'"
[[resolvers.createTask.resolver.command.modify_values]]
column = "completed"
value = "$completed"
[[resolvers.createTask.resolver.command.modify_values]]
column = "list_id"
value = "'$list.$id'"
[[resolvers.createTask.resolver.command.modify_values]]
column = "user_username"
value = "'$user.$username'"
[resolvers.updateList]
operation_name = "updateList"
[resolvers.updateList.resolver]
command_type = "SQLUpdate"
[resolvers.updateList.resolver.command]
table = "List"
columns = []
aggregate_result = true
[[resolvers.updateList.resolver.command.modify_values]]
column = "title"
value = "'$title'"
[[resolvers.updateList.resolver.command.where_clauses]]
table = "List"
column = "id"
condition_str = "= '$id'"
index = ""
[resolvers.createList] [resolvers.createList]
operation_name = "createList" operation_name = "createList"

View File

@ -54,6 +54,13 @@ type Mutation {
user: User! # username needs to be specified as it's non-nullable user: User! # username needs to be specified as it's non-nullable
): List @bb_resolver(_type: insert, _object: List, _fields: { title: "$title", user: "$user" }) ): List @bb_resolver(_type: insert, _object: List, _fields: { title: "$title", user: "$user" })
updateList(
id: ID!,
title: String!
): List @bb_resolver(_type: update, _object: List, _filter: { id: { _eq: "$id" } }, _fields: { title: "$title" })
deleteList(id: ID!): List @bb_resolver(_type: delete, _object: List, _filter: { id: { _eq: "$id" } })
createTask( createTask(
title: String!, title: String!,
description: String, description: String,

View File

@ -1,8 +1,8 @@
<script setup> <script setup>
import { RouterLink, RouterView } from 'vue-router' import { RouterLink, RouterView } from 'vue-router'
import HelloWorld from './components/Welcome.vue'
import { store } from './store.js' import { store } from './store.js'
import {clearSession} from "./store.js"; import {clearSession} from "./store.js";
import {Modal} from "bootstrap";
/** /**
* Logout the user. * Logout the user.
@ -36,11 +36,14 @@ function dismissError() {
<div v-if="store.session.token" class="collapse navbar-collapse" id="navbarSupportedContent"> <div v-if="store.session.token" class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0"> <ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item"> <li class="nav-item">
<RouterLink to="/about" class="nav-link" active-class="active">About</RouterLink> <RouterLink to="/" class="nav-link" active-class="active">Tasks</RouterLink>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<RouterLink to="/lists" class="nav-link" active-class="active">Lists</RouterLink> <RouterLink to="/lists" class="nav-link" active-class="active">Lists</RouterLink>
</li> </li>
<li class="nav-item">
<RouterLink to="/about" class="nav-link" active-class="active">About</RouterLink>
</li>
</ul> </ul>
<ul class="navbar-nav mb-2 mb-lg-0"> <ul class="navbar-nav mb-2 mb-lg-0">
<li class="navbar-text">Hello, {{ store.session.username }}!</li> <li class="navbar-text">Hello, {{ store.session.username }}!</li>
@ -60,6 +63,24 @@ function dismissError() {
<button type="button" class="btn-close" @click="dismissError()" aria-label="Close"></button> <button type="button" class="btn-close" @click="dismissError()" aria-label="Close"></button>
</div> </div>
<!-- message modal -->
<div class="modal" tabindex="-1" id="msgbox">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Modal title</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Modal body text goes here.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<RouterView /> <RouterView />
</main> </main>

View File

@ -12,7 +12,7 @@
</h1> </h1>
<div class="toolbar"> <div class="toolbar">
<button type="button" @click="addItem()" class="btn btn-primary btn-large">New List</button> <button type="button" @click="addList()" class="btn btn-primary btn-large">New List</button>
<div class="ms-auto"> <div class="ms-auto">
<small>{{ gStore.lists.length }} lists found.</small> <small>{{ gStore.lists.length }} lists found.</small>
</div> </div>
@ -30,7 +30,8 @@
<i class="bi bi-trash"></i> <i class="bi bi-trash"></i>
</button> </button>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><a class="dropdown-item" href="#">Delete this list</a></li> <li><a class="dropdown-item" href="#"
@click="deleteList(list)">Delete this list</a></li>
</ul> </ul>
</div> </div>
<span class="item-counter"> <span class="item-counter">
@ -42,7 +43,7 @@
<div class="alert alert-info" v-if="gStore.lists.length === 0"> <div class="alert alert-info" v-if="gStore.lists.length === 0">
<p>There are no lists, yet.</p> <p>There are no lists, yet.</p>
<button type="button" @click="addItem()" class="btn btn-primary btn-large">Create your first list</button> <button type="button" @click="addList()" class="btn btn-primary btn-large">Create your first list</button>
</div> </div>
</transition-group> </transition-group>
</div> </div>
@ -53,7 +54,10 @@
<script> <script>
import {gqlQuery} from "../util/net"; import {gqlQuery} from "../util/net";
import {store} from "../store"; import {showError, store} from "../store";
import {showModal} from "../util/misc";
const NEW_LIST_ID = "new-list-id";
export default { export default {
name: "Lists", name: "Lists",
@ -62,19 +66,87 @@ export default {
/** /**
* Save a list to the database. * Save a list to the database.
* @param list * @param list - the list object to save.
*/ */
saveList(list) saveList(list)
{ {
/* Prepare request; it is different if the list is new */
let gql = "";
if (list.id === NEW_LIST_ID) {
/* create new list */
gql = `mutation {
createList(
title: "${list.title}"
user: {
username: "${store.session.username}"
}
) {
id
}
}`;
} else {
/* save existing list */
gql = `mutation {
updateList(
id: "${list.id}",
title: "${list.title}"
) {
id
}
}`;
}
/* send query */
gqlQuery(
gql
).then(data => {
/* Save the list's id in case it was just created */
list.id = data.List.id;
}).catch(e => {
const errMsg = `Failed to save list: ${e}`;
console.error(errMsg);
showError(errMsg);
});
},
/**
* Delete a list.
* @param list - the list object to delete
*/
deleteList(list) {
if (list.id !== NEW_LIST_ID) {
/* Make sure the list is not in use */
if (store.tasks.findIndex(task => task.list.id === list.id) !== -1) {
showModal("Delete List", "The list cannot be deleted as it is in use.");
return;
}
/* List must be also deleted from the server */
gqlQuery(`mutation {
deleteList(id: "${list.id}") {
id
}
}`);
}
/* delete list from the store. */
const idx = store.lists.findIndex(item => item.id === list.id);
if (idx !== -1) {
store.lists.splice(idx, 1);
}
}, },
/** /**
* Add a new list. * Add a new list.
*/ */
addItem() addList()
{ {
/* push it to the array of lists in the store; it will be saved
* when the user leaves the title field.
*/
store.lists.push({
title: "Enter list title",
id: NEW_LIST_ID
});
}, },
/** /**

View File

@ -12,24 +12,6 @@
ToDo List ToDo List
</h1> </h1>
<!-- message modal -->
<div class="modal" tabindex="-1" id="msgbox">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Modal title</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Modal body text goes here.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<div id="filter-form" class="toolbar"> <div id="filter-form" class="toolbar">
<label class="switch"> <label class="switch">
@ -37,7 +19,7 @@
Show completed Show completed
</label> </label>
<button type="button" @click="addItem()" class="ms-auto btn btn-primary btn-large">Add Item</button> <button type="button" @click="addTask()" class="ms-auto btn btn-primary btn-large">Add Item</button>
<div class="ms-auto"> <div class="ms-auto">
<div class="d-flex"> <div class="d-flex">
@ -69,7 +51,7 @@
<i class="bi bi-trash"></i> <i class="bi bi-trash"></i>
</button> </button>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><a class="dropdown-item" href="#" @click="deleteTask(task)">Delete this item</a></li> <li><a class="dropdown-item" href="" @click="deleteTask(task)">Delete this item</a></li>
</ul> </ul>
</div> </div>
</div> </div>
@ -81,7 +63,7 @@
</p> </p>
<p v-if="currentList !== 0">You can add items or select another list.</p> <p v-if="currentList !== 0">You can add items or select another list.</p>
<hr> <hr>
<button type="button" class="btn btn-primary btn-sm" @click="addItem()">Create Todo Item</button> <button type="button" class="btn btn-primary btn-sm" @click="addTask()">Create Todo Item</button>
</div> </div>
</transition-group> </transition-group>
@ -93,7 +75,7 @@
<script> <script>
import {gqlQuery} from "../util/net"; import {gqlQuery} from "../util/net";
import {showError, store} from "../store"; import {showError, store} from "../store";
import { Modal } from 'bootstrap'; import {showModal} from "../util/misc";
const NEW_TASK_ID = "new-task-id"; const NEW_TASK_ID = "new-task-id";
@ -158,7 +140,7 @@ export default {
gqlQuery( gqlQuery(
request request
).then(data => { ).then(data => {
/* save the task's id */ /* Save the task's id in case it was just created */
task.id = data.Task.id; task.id = data.Task.id;
}).catch(e => { }).catch(e => {
const errMsg = `Failed to save task: ${e}`; const errMsg = `Failed to save task: ${e}`;
@ -190,23 +172,12 @@ export default {
}, },
/** /**
* Show a message nox (Bootstrap modal) * Add a task to the current list.
*/ */
showModal(title, text) { addTask() {
const el = document.getElementById('msgbox');
el.querySelector(".modal-title").innerHTML = title;
el.querySelector(".modal-body").innerHTML = text;
const modal = new Modal(el);
modal.show();
},
/**
* Add an item to the current list.
*/
addItem() {
if (!this.currentList) { if (!this.currentList) {
this.showModal("No list selected", showModal("No list selected",
'Please select a specific list (not "All") before adding an item.'); 'Please select a specific list (not "All") before adding an item.');
return; return;
} }

21
src/util/misc.js Normal file
View File

@ -0,0 +1,21 @@
/**
* Misc utility functions.
*
* Part of the basebox sample Todo app.
* https://basebox.tech
*/
import {Modal} from "bootstrap";
/**
* Show a message box (Bootstrap modal).
*
* The markup for the modal is defined in App.vue.
*/
export function showModal(title, text) {
const el = document.getElementById('msgbox');
el.querySelector(".modal-title").innerHTML = title;
el.querySelector(".modal-body").innerHTML = text;
const modal = new Modal(el);
modal.show();
}