list editing, deleting
This commit is contained in:
parent
3bb07e7d47
commit
9a53cfbaa6
@ -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"
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
25
src/App.vue
25
src/App.vue
@ -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>
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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
21
src/util/misc.js
Normal 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();
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user