WIP
This commit is contained in:
parent
c7dd690ced
commit
44b8dc0c2f
@ -13,55 +13,58 @@ import CommunityIcon from './icons/IconCommunity.vue'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AboutItem>
|
||||
<template #icon>
|
||||
<DocumentationIcon />
|
||||
</template>
|
||||
<template #heading>About</template>
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-8 offset-md-2 col-lg-6 offset-lg-3">
|
||||
<AboutItem>
|
||||
<template #icon>
|
||||
<DocumentationIcon />
|
||||
</template>
|
||||
<template #heading>About</template>
|
||||
|
||||
<p>This is a very simple demo app for <a href="https://basebox.tech">basebox</a>,
|
||||
the medical grade backend that helps you get your project done with ease.</p>
|
||||
</AboutItem>
|
||||
<p>This is a very simple demo app for <a href="https://basebox.tech">basebox</a>,
|
||||
the medical grade backend that helps you get your project done with ease.</p>
|
||||
</AboutItem>
|
||||
|
||||
<AboutItem>
|
||||
<template #icon>
|
||||
<ToolingIcon />
|
||||
</template>
|
||||
<template #heading>Tools and Languages</template>
|
||||
<AboutItem>
|
||||
<template #icon>
|
||||
<ToolingIcon />
|
||||
</template>
|
||||
<template #heading>Tools and Languages</template>
|
||||
|
||||
<p>We built basebox in 100% <a href="https://rust-lang.org">Rust</a>, but you can use about
|
||||
any tool, language or framework to use basebox as your backend. As long as it can send
|
||||
<a href="https://graphql.org">GraphQL</a> requests and understands JSON, it can build on basebox!</p>
|
||||
</AboutItem>
|
||||
<p>We built basebox in 100% <a href="https://rust-lang.org">Rust</a>, but you can use about
|
||||
any tool, language or framework to use basebox as your backend. As long as it can send
|
||||
<a href="https://graphql.org">GraphQL</a> requests and understands JSON, it can build on basebox!</p>
|
||||
</AboutItem>
|
||||
|
||||
<AboutItem>
|
||||
<template #icon>
|
||||
<CommunityIcon />
|
||||
</template>
|
||||
<template #heading>Feedback</template>
|
||||
<p><b>We are so eager to hear from you!</b></p>
|
||||
<ul>
|
||||
<li>Are you missing something?</li>
|
||||
<li>Any suggestion?</li>
|
||||
<li>Any question?</li>
|
||||
</ul>
|
||||
<p>We'd be REALLY happy to <a href="https://basebox.tech/open-a-aticket">hear from you</a>!</p>
|
||||
</AboutItem>
|
||||
<AboutItem>
|
||||
<template #icon>
|
||||
<CommunityIcon />
|
||||
</template>
|
||||
<template #heading>Feedback</template>
|
||||
<p><b>We are so eager to hear from you!</b></p>
|
||||
<ul>
|
||||
<li>Are you missing something?</li>
|
||||
<li>Any suggestion?</li>
|
||||
<li>Any question?</li>
|
||||
</ul>
|
||||
<p>We'd be REALLY happy to <a href="https://basebox.tech/open-a-aticket">hear from you</a>!</p>
|
||||
</AboutItem>
|
||||
|
||||
<AboutItem>
|
||||
<template #icon>
|
||||
<QuestionIcon />
|
||||
</template>
|
||||
<template #heading>Support</template>
|
||||
<AboutItem>
|
||||
<template #icon>
|
||||
<QuestionIcon />
|
||||
</template>
|
||||
<template #heading>Support</template>
|
||||
|
||||
If you need help, check out our <a href="https://basebox.tech">website</a>.<br>
|
||||
Some support options:
|
||||
<ul>
|
||||
<li>See our <a href="https://basebox.tech/faq">FAQs</a></li>
|
||||
<li>Read the <a href="https://docs.basebox.tech">documentation</a></li>
|
||||
<li>Open a <a href="https://basebox.tech/open-a-ticket">ticket</a></li>
|
||||
</ul>
|
||||
|
||||
</AboutItem>
|
||||
If you need help, check out our <a href="https://basebox.tech">website</a>.<br>
|
||||
Some support options:
|
||||
<ul>
|
||||
<li>See our <a href="https://basebox.tech/faq">FAQs</a></li>
|
||||
<li>Read the <a href="https://docs.basebox.tech">documentation</a></li>
|
||||
<li>Open a <a href="https://basebox.tech/open-a-ticket">ticket</a></li>
|
||||
</ul>
|
||||
|
||||
</AboutItem>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -12,6 +12,24 @@
|
||||
ToDo List
|
||||
</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">
|
||||
|
||||
<label class="switch">
|
||||
@ -26,7 +44,7 @@
|
||||
<label class="col-form-label" for="list-selector">Select List: </label>
|
||||
<div class="ms-2">
|
||||
<select id="list-selector" class="form-select" v-model="currentList">
|
||||
<option :value="0">All</option>
|
||||
<option :value="0" selected>All</option>
|
||||
<option v-for="item in gStore.lists" :key="item.id" :value="item.id">{{ item.title }}</option>
|
||||
</select>
|
||||
</div>
|
||||
@ -58,7 +76,12 @@
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info" v-if="filteredItems.length === 0">
|
||||
|
||||
<p><i class="bi bi-info-circle"></i>
|
||||
Sorry, there are no todo items{{ currentList !== 0 ? " in this list" : ""}}.
|
||||
</p>
|
||||
<p v-if="currentList !== 0">You can add items or select another list.</p>
|
||||
<hr>
|
||||
<button type="button" class="btn btn-primary btn-sm" @click="addItem()">Create Todo Item</button>
|
||||
</div>
|
||||
|
||||
</transition-group>
|
||||
@ -69,7 +92,10 @@
|
||||
|
||||
<script>
|
||||
import {gqlQuery} from "../util/net";
|
||||
import {store} from "../store";
|
||||
import {showError, store} from "../store";
|
||||
import { Modal } from 'bootstrap';
|
||||
|
||||
const NEW_TASK_ID = "new-task-id";
|
||||
|
||||
export default {
|
||||
name: "Todo",
|
||||
@ -81,7 +107,7 @@ export default {
|
||||
/** whether to show completed items */
|
||||
showCompleted: true,
|
||||
/* the id of the list currently being shown */
|
||||
currentList: 1,
|
||||
currentList: 0,
|
||||
}
|
||||
},
|
||||
|
||||
@ -92,23 +118,77 @@ export default {
|
||||
* @param task the task to save; this is an object as received from the broker.
|
||||
*/
|
||||
saveTask(task) {
|
||||
console.info(`Saving task '${task.title}' with id ${task.id}`);
|
||||
let request = "";
|
||||
if (task.id !== NEW_TASK_ID) {
|
||||
/* save changes to existing task */
|
||||
request = `mutation {
|
||||
updateTask(
|
||||
id: "${task.id}",
|
||||
title: "${task.title}",
|
||||
description: "${task.description}",
|
||||
completed: ${task.completed ? "true" : "false"},
|
||||
list: {
|
||||
id: "${task.list.id}"
|
||||
},
|
||||
) {
|
||||
id
|
||||
}
|
||||
}`;
|
||||
} else {
|
||||
/* create new task */
|
||||
request = `mutation {
|
||||
createTask(
|
||||
title: "${task.title}",
|
||||
description: "${task.description}",
|
||||
completed: ${task.completed ? "true" : "false"},
|
||||
list: {
|
||||
id: "${task.list.id}"
|
||||
},
|
||||
user: {
|
||||
username: "${store.session.username}"
|
||||
}
|
||||
) {
|
||||
id
|
||||
}
|
||||
}`;
|
||||
}
|
||||
|
||||
/* send query */
|
||||
gqlQuery(
|
||||
request
|
||||
).then(data => {
|
||||
task.id = data.id;
|
||||
}).catch(e => {
|
||||
const errMsg = `Failed to save task: ${e}`;
|
||||
console.error(errMsg);
|
||||
showError(errMsg);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Show a message nox (Bootstrap modal)
|
||||
*/
|
||||
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();
|
||||
},
|
||||
|
||||
/**
|
||||
* Add an item to the current list.
|
||||
*/
|
||||
addItem() {
|
||||
if (this.currentList === 0) {
|
||||
window.alert("Please select a list at the top right before adding an item.");
|
||||
if (!this.currentList) {
|
||||
this.showModal("No list selected",
|
||||
'Please select a specific list (not "All") before adding an item.');
|
||||
return;
|
||||
}
|
||||
|
||||
/* fake ID of new item */
|
||||
const newItemId = store.tasks.length + 1;
|
||||
|
||||
store.tasks.push({
|
||||
id: newItemId,
|
||||
id: NEW_TASK_ID,
|
||||
completed: false,
|
||||
title: "Enter task here",
|
||||
description: "",
|
||||
@ -194,41 +274,6 @@ export default {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* mounted lifecycle hook.
|
||||
*/
|
||||
mounted() {
|
||||
if (!store.session) {
|
||||
console.error("TodoRoot component must not be loaded if user is not logged in.");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Load user info and his/her lists and todos */
|
||||
gqlQuery(`query {
|
||||
getUser(username: "${store.session.userName}") {
|
||||
name
|
||||
lists {
|
||||
id
|
||||
title
|
||||
}
|
||||
tasks {
|
||||
id
|
||||
title
|
||||
description
|
||||
completed
|
||||
list {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}`).then(rspJson => {
|
||||
|
||||
}).catch(err => {
|
||||
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
* https://basebox.tech
|
||||
*/
|
||||
|
||||
import {showError, store} from "../store";
|
||||
import {loggedIn, showError, store} from "../store";
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import HomeView from '../views/HomeView.vue'
|
||||
import { objectToQueryString } from "../util/net";
|
||||
@ -41,7 +41,7 @@ router.beforeEach(async (to, from) => {
|
||||
// Handle OAuth callback request.
|
||||
// This request is coming in after the user entered her/his credentials at the IdP
|
||||
// login form; the IdP (e.g. Keycloak) redirects the browser to the URL of this route.
|
||||
if (to.path === "/oauth-callback") {
|
||||
if (to.path.startsWith("/oauth-callback")) {
|
||||
let queryString = objectToQueryString(to.query);
|
||||
console.info(`Got /oauth-callback with query string '${queryString}`);
|
||||
|
||||
@ -58,6 +58,11 @@ router.beforeEach(async (to, from) => {
|
||||
return {name: 'home'};
|
||||
}
|
||||
|
||||
/* if no user is logged in, we redirect to the home page */
|
||||
if (!loggedIn() && to.path.length > 1 && to.path !== "/about") {
|
||||
return {name: 'home'};
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
export default router
|
||||
|
135
src/store.js
135
src/store.js
@ -22,32 +22,9 @@ export const store = reactive({
|
||||
fatalError: "",
|
||||
|
||||
/* array of todo lists currently known */
|
||||
lists: [
|
||||
{ id: 1, title: "Default"},
|
||||
{ id: 2, title: "House" },
|
||||
{ id: 3, title: "Car" },
|
||||
{ id: 4, title: "Work" },
|
||||
],
|
||||
lists: [],
|
||||
/* known tasks */
|
||||
tasks: [
|
||||
{ id: 1, completed: false, title: "Go to dentist", description: "Last time is way too long ago...", list: { id: 1 } },
|
||||
{ id: 2, completed: true, title: "Change engine oil", description: "Use the good one", list: { id: 3 } },
|
||||
{ id: 3, completed: false, title: "Clean windows", description: "Regualar stuff", list: { id: 1 } },
|
||||
{ id: 4, completed: false, title: "Oil stove hinges", description: "They are stiff and screechy", list: { id: 2 } },
|
||||
{ id: 5, completed: false, title: "Oil stove hinges", description: "They are stiff and screechy", list: { id: 2 } },
|
||||
{ id: 6, completed: false, title: "Oil stove hinges", description: "They are stiff and screechy", list: { id: 2 } },
|
||||
{ id: 7, completed: false, title: "Oil stove hinges", description: "They are stiff and screechy", list: { id: 2 } },
|
||||
{ id: 8, completed: false, title: "Oil stove hinges", description: "They are stiff and screechy", list: { id: 2 } },
|
||||
{ id: 9, completed: false, title: "Oil stove hinges", description: "They are stiff and screechy", list: { id: 2 } },
|
||||
{ id: 10, completed: false, title: "Oil stove hinges", description: "They are stiff and screechy", list: { id: 2 } },
|
||||
{ id: 11, completed: false, title: "Oil stove hinges", description: "They are stiff and screechy", list: { id: 2 } },
|
||||
{ id: 12, completed: false, title: "Oil stove hinges", description: "They are stiff and screechy", list: { id: 2 } },
|
||||
{ id: 13, completed: false, title: "Oil stove hinges", description: "They are stiff and screechy", list: { id: 2 } },
|
||||
{ id: 14, completed: false, title: "Oil stove hinges", description: "They are stiff and screechy", list: { id: 2 } },
|
||||
{ id: 15, completed: false, title: "Oil stove hinges", description: "They are stiff and screechy", list: { id: 2 } },
|
||||
{ id: 16, completed: false, title: "Oil stove hinges", description: "They are stiff and screechy", list: { id: 2 } },
|
||||
{ id: 17, completed: false, title: "Oil stove hinges", description: "They are stiff and screechy", list: { id: 2 } },
|
||||
],
|
||||
tasks: [],
|
||||
|
||||
});
|
||||
|
||||
@ -101,43 +78,95 @@ export async function storeInit(session) {
|
||||
* We cannot run these requests in parallel, since the user record must exist in the database
|
||||
* before we can create the default list.
|
||||
*/
|
||||
const tasks = [];
|
||||
tasks.push(
|
||||
gqlQuery(`mutation {
|
||||
try {
|
||||
console.info("NOTE: If next request fails, it is probably because the user already exists. In this case, the error is ignored.");
|
||||
await gqlQuery(`mutation {
|
||||
createUser(
|
||||
username: "${session.username}",
|
||||
name: "${session.first_name} ${session.last_name}"
|
||||
) {
|
||||
username
|
||||
}
|
||||
}`));
|
||||
tasks.push(gqlQuery(`mutation {
|
||||
createList(
|
||||
title: "Default",
|
||||
user: { username: "${session.username}" }
|
||||
) {
|
||||
title
|
||||
}`);
|
||||
} catch(e) {
|
||||
const errStr = e.toString();
|
||||
if (errStr.indexOf("already exists") === -1) {
|
||||
/* this is an error */
|
||||
const e = `Failed to create user: ${errStr}`;
|
||||
console.error(e);
|
||||
showError(e);
|
||||
return;
|
||||
}
|
||||
}`));
|
||||
}
|
||||
|
||||
const allErrors = [];
|
||||
|
||||
for (const task of tasks) {
|
||||
try {
|
||||
await task;
|
||||
} catch (e) {
|
||||
const errStr = e.toString();
|
||||
if (errStr.indexOf("already exists") === -1) {
|
||||
/* this is an error */
|
||||
const e = `Init task failed: ${errStr}`;
|
||||
console.error(e);
|
||||
allErrors.push(e);
|
||||
/* Load user info, lists and todos. */
|
||||
gqlQuery(`query {
|
||||
getUser(username: "${store.session.username}") {
|
||||
name
|
||||
lists {
|
||||
id
|
||||
title
|
||||
}
|
||||
tasks {
|
||||
id
|
||||
title
|
||||
description
|
||||
completed
|
||||
list {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}`).then(data => {
|
||||
store.lists = data.User.lists ? data.User.lists : [];
|
||||
store.tasks = data.User.tasks ? data.User.tasks : [];
|
||||
/* create default list if necessary */
|
||||
if (store.lists.length === 0) {
|
||||
gqlQuery(`mutation {
|
||||
createList(
|
||||
title: "Default",
|
||||
user: { username: "${store.session.username}" }
|
||||
) {
|
||||
title
|
||||
}
|
||||
}`);
|
||||
}
|
||||
|
||||
if (allErrors.length) {
|
||||
showError(allErrors.join("<br><br>"));
|
||||
}
|
||||
/* fix for basebox bug: task.list is returned as an array of List objects, where we expect just a single object */
|
||||
for (const task of store.tasks) {
|
||||
if (Array.isArray(task.list)) {
|
||||
task.list = task.list[0];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}).catch(err => {
|
||||
const e = `Failed to load user/data: ${err.toString()}`;
|
||||
console.error(e);
|
||||
showError(e);
|
||||
});
|
||||
|
||||
|
||||
/* Check if we already have a list named "Default". */
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
const testData = {
|
||||
lists: [
|
||||
{ id: 1, title: "Default"},
|
||||
{ id: 2, title: "House" },
|
||||
{ id: 3, title: "Car" },
|
||||
{ id: 4, title: "Work" },
|
||||
],
|
||||
/* known tasks */
|
||||
tasks: [
|
||||
{ id: 1, completed: false, title: "Go to dentist", description: "Last time is way too long ago...", list: { id: 1 } },
|
||||
{ id: 2, completed: true, title: "Change engine oil", description: "Use the good one", list: { id: 3 } },
|
||||
{ id: 3, completed: false, title: "Clean windows", description: "Regualar stuff", list: { id: 1 } },
|
||||
{ id: 4, completed: false, title: "Oil stove hinges", description: "They are stiff and screechy", list: { id: 2 } },
|
||||
{ id: 5, completed: false, title: "Do more benchmarking", description: "Find out how fast this thing really is", list: { id: 4 } },
|
||||
],
|
||||
|
||||
};
|
Loading…
Reference in New Issue
Block a user