1.3.0 release candidate (lacking text manual for advanced search and updated changelog)

This commit is contained in:
Elvanos 2021-03-05 00:51:41 +01:00
parent a6aa75532d
commit 0635be823a
20 changed files with 791 additions and 208 deletions

View file

@ -3,19 +3,22 @@
## 0.1.3
TODO
move doc buttons to the top bar
add delete document dialog to dialog system
ADD TOOLTIPS TO SEACH!!!!
### Bugfixes
- Fixed the "Name" field disappearing upon full deletion of text
- Fixed a bug with single/multi select fields working unintuitively for adding new values (eg: Character personality traits field or Sex field)
- Added an auto-remover of no longer existing relationships filled in within single and mutli-fields
### New features
- Added a safeguard dialog for new project creation in case an opened project exists
- Added a safeguard dialog for project importing in case an opened project exists
- New control bar added for documents and project control
- Added automatic redirecting to the project screen upon importing an existing project or creating a new one (better transition effect will be added later)
- Added a "Advanced search guide" dialog with manual on how to use the advanced search
- Added "About Fantasia Archive" dialog showing current app version (more details will be added in the future)
- New control bar added for documents and project control along with more intelligent button redesign
- A new logo added to the app (better visibility of the logo in small scales and icons)
- Massive overhaul of the search engine used by the Quick opening existing document and single/multi relationship fields (now supports tags, categories, document types, inteligent filtering and inteligent sorting via importance of the found values)
- Added color support to single/multi relationship fields
@ -28,8 +31,10 @@ add delete document dialog to dialog system
- Lightly modified the app color-scheme to offer better readability of contrast
- Changed icon for the button triggering quick-adding of new documents
- Changed the looks of tooltips to go well with the current app looks
- Changed the looks of tooltips, relationship fields and selects to go well with the current app looks
- Adjusted tab-list width to allow for more content to show
- Added a tooptip showing how many of the object in the hierarchical tree are documents and how many are categoriesE
- Hierarchical tree search bar is now attached on the top of the tree and no longer scrolls along with the rest of the content of the tree in order to allow better useability. The search now also expands to full app width on focus via user's interaction. The search icon was moved to right and the field reset icon as moved to the left.
- Modified selected and active indicators for already selected/active items in dropdown lists in order to not clash with the highlighting from the filter results
- Slightly modified the scrollbar visuals to be less intrusive
- Added a light golden tint to the background of the app to go easy on user's eyes before dark mode is added

View file

@ -307,4 +307,8 @@ export default class BaseClass extends Vue {
sleep (ms:number) {
return new Promise(resolve => setTimeout(resolve, ms))
}
stripTags (input: string) {
return (input) ? input.replace(/<[^>]+>/g, "") : input
}
}

View file

@ -20,6 +20,18 @@
@trigger-dialog-close="deleteObjectDialogClose"
/>
<!-- Advanced search guide dialog -->
<advancedSearchGuideDialog
:dialog-trigger="advancedSearchGuideDialogTrigger"
@trigger-dialog-close="advancedSearchGuideDialogClose"
/>
<!-- Keybind dialog -->
<keybindCheatsheetDialog
:dialog-trigger="keybindsDialogTrigger"
@trigger-dialog-close="keybindsDialogClose"
/>
<q-page-sticky position="top-right" class="documentControl">
<div class="documentControl__blocker"></div>
@ -28,6 +40,38 @@
<div class="documentControl__left">
<q-btn
icon="mdi-keyboard-settings"
color="primary"
outline
@click="keybindsDialogAssignUID"
>
<q-tooltip
:delay="500"
anchor="bottom middle"
self="top middle"
>
Open keybinds cheatsheet
</q-tooltip>
</q-btn>
<q-btn
icon="mdi-file-question"
color="primary"
outline
@click="advancedSearchGuideAssignUID"
>
<q-tooltip
:delay="500"
anchor="bottom middle"
self="top middle"
>
Open advanced search guide
</q-tooltip>
</q-btn>
<q-separator vertical inset color="accent" />
<q-btn
icon="mdi-package-variant-closed"
color="primary"
@ -35,7 +79,11 @@
:disable="!projectExists"
@click="commenceExport"
>
<q-tooltip>
<q-tooltip
:delay="500"
anchor="bottom middle"
self="top middle"
>
Export current project
</q-tooltip>
</q-btn>
@ -48,7 +96,11 @@
outline
@click="existingObjectAssignUID"
>
<q-tooltip>
<q-tooltip
:delay="500"
anchor="bottom middle"
self="top middle"
>
Quick-search an existing document
</q-tooltip>
</q-btn>
@ -59,7 +111,11 @@
outline
@click="newObjectAssignUID"
>
<q-tooltip>
<q-tooltip
:delay="500"
anchor="bottom middle"
self="top middle"
>
Quick-add a new document
</q-tooltip>
</q-btn>
@ -67,14 +123,73 @@
</div>
<div class="documentControl__right">
<q-btn
icon="mdi-file-document-edit"
color="primary"
outline
@click="toggleEditMode"
v-if="currentyEditable && SGET_allOpenedDocuments.docs.length > 0"
>
<q-tooltip
:delay="500"
anchor="bottom middle"
self="top middle"
>
Edit current document
</q-tooltip>
</q-btn>
<q-btn
icon="mdi-content-save"
color="primary"
outline
@click="saveCurrentDocument"
v-if="!currentyEditable && SGET_allOpenedDocuments.docs.length > 0"
>
<q-tooltip
:delay="500"
anchor="bottom left"
self="top middle"
>
Save current document
</q-tooltip>
</q-btn>
<q-btn
icon="mdi-file-tree"
color="primary"
outline
@click="addNewUnderParent"
v-if="!currentlyNew && SGET_allOpenedDocuments.docs.length > 0"
>
<q-tooltip
:delay="500"
max-width="500px"
anchor="bottom left"
self="top middle"
>
Add new document with the current one as parent
</q-tooltip>
</q-btn>
<q-separator vertical inset color="accent"
v-if="!currentlyNew && SGET_allOpenedDocuments.docs.length > 0"
/>
<q-btn
icon="mdi-text-box-remove-outline"
color="secondary"
outline
@click="deleteObjectAssignUID"
:disable="SGET_allOpenedDocuments.docs.length < 1"
v-if="!currentlyNew && SGET_allOpenedDocuments.docs.length > 0"
>
<q-tooltip>
<q-tooltip
:delay="500"
anchor="bottom left"
self="top middle"
>
Delete current document
</q-tooltip>
</q-btn>
@ -95,6 +210,12 @@ import BaseClass from "src/BaseClass"
import newDocumentDialog from "src/components/dialogs/NewDocument.vue"
import existingDocumentDialog from "src/components/dialogs/ExistingDocument.vue"
import deleteDocumentCheckDialog from "src/components/dialogs/DeleteDocumentCheck.vue"
import advancedSearchGuideDialog from "src/components/dialogs/AdvancedSearchGuide.vue"
import keybindCheatsheetDialog from "src/components/dialogs/KeybindCheatsheet.vue"
import { I_OpenedDocument } from "src/interfaces/I_OpenedDocument"
import { extend } from "quasar"
import { saveDocument } from "src/scripts/databaseManager/documentManager"
import { retrieveCurrentProjectName, exportProject } from "src/scripts/projectManagement/projectManagent"
@ -102,7 +223,9 @@ import { retrieveCurrentProjectName, exportProject } from "src/scripts/projectMa
components: {
newDocumentDialog,
existingDocumentDialog,
deleteDocumentCheckDialog
deleteDocumentCheckDialog,
advancedSearchGuideDialog,
keybindCheatsheetDialog
}
})
export default class DocumentControl extends BaseClass {
@ -137,6 +260,42 @@ export default class DocumentControl extends BaseClass {
if (this.determineKeyBind("deleteDocument")) {
this.deleteObjectAssignUID()
}
// Edit document - CTRL + E
if (this.determineKeyBind("editDocument") && this.currentyEditable) {
this.toggleEditMode()
}
// Save document
if (this.determineKeyBind("saveDocument") && !this.currentyEditable) {
this.saveCurrentDocument().catch(e => console.log(e))
}
}
/****************************************************************/
// Advanced search guide dialog
/****************************************************************/
advancedSearchGuideDialogTrigger: string | false = false
advancedSearchGuideDialogClose () {
this.advancedSearchGuideDialogTrigger = false
}
advancedSearchGuideAssignUID () {
this.advancedSearchGuideDialogTrigger = this.generateUID()
}
/****************************************************************/
// Keybings cheatsheet dialog
/****************************************************************/
keybindsDialogTrigger: string | false = false
keybindsDialogClose () {
this.keybindsDialogTrigger = false
}
keybindsDialogAssignUID () {
this.keybindsDialogTrigger = this.generateUID()
}
/****************************************************************/
@ -189,6 +348,98 @@ export default class DocumentControl extends BaseClass {
const projectName = await retrieveCurrentProjectName()
this.exportProject(projectName)
}
/****************************************************************/
// Add new document under parent
/****************************************************************/
addNewUnderParent () {
const currentDoc = this.findRequestedOrActiveDocument()
if (currentDoc) {
const routeObject = {
_id: currentDoc.type,
parent: currentDoc._id
}
// @ts-ignore
this.addNewObjectRoute(routeObject)
}
}
/****************************************************************/
// Toggle edit mode & Save document
/****************************************************************/
toggleEditMode () {
const currentDoc = this.findRequestedOrActiveDocument()
if (currentDoc && !currentDoc.editMode) {
const dataCopy: I_OpenedDocument = extend(true, {}, currentDoc)
dataCopy.editMode = true
const dataPass = { doc: dataCopy, treeAction: false }
this.SSET_updateOpenedDocument(dataPass)
}
}
async saveCurrentDocument () {
const currentDoc = this.findRequestedOrActiveDocument()
const allDocuments = this.SGET_allOpenedDocuments
const docCopy: I_OpenedDocument[] = extend(true, [], allDocuments.docs)
if (currentDoc) {
// @ts-ignore
const savedDocument: {
documentCopy: I_OpenedDocument,
allOpenedDocuments: I_OpenedDocument[]
} = await saveDocument(currentDoc, docCopy)
// Update the opened document
const dataPass = { doc: savedDocument.documentCopy, treeAction: true }
this.SSET_updateOpenedDocument(dataPass)
// Update all others
for (const doc of savedDocument.allOpenedDocuments) {
// Update the opened document
const dataPass = { doc: doc, treeAction: true }
this.SSET_updateOpenedDocument(dataPass)
}
}
}
@Watch("$route", { immediate: true, deep: true })
onUrlChange () {
this.checkEditability()
this.checkNew()
}
@Watch("SGET_allOpenedDocuments", { deep: true })
onDocChange () {
this.checkEditability()
this.checkNew()
}
checkEditability () {
const currentDocument = this.findRequestedOrActiveDocument()
if (currentDocument && !currentDocument.editMode) {
this.currentyEditable = true
}
else {
this.currentyEditable = false
}
}
checkNew () {
const currentDocument = this.findRequestedOrActiveDocument()
if (currentDocument && currentDocument.isNew) {
this.currentlyNew = true
}
else {
this.currentlyNew = false
}
}
currentyEditable = false
currentlyNew = false
}
</script>
@ -211,7 +462,7 @@ export default class DocumentControl extends BaseClass {
&__wrapper {
width: calc(100vw - 375px);
padding: 10px 15px;
padding: 8.5px 15px;
display: flex;
justify-content: space-between;
}

View file

@ -1,14 +1,17 @@
<template>
<span>
<q-page-sticky position="top-left" class="treeSearchWrapper">
<q-input ref="treeFilter" dark filled v-model="treeFilter" label="Filter document tree...">
<template v-slot:prepend>
<template v-slot:append>
<q-icon name="mdi-text-search" />
</template>
<template v-slot:append>
<q-icon v-if="treeFilter !== ''" name="clear" class="cursor-pointer" @click="resetTreeFilter" />
<template v-slot:prepend>
<q-icon v-if="treeFilter !== ''" name="clear" class="cursor-pointer text-secondary" @click="resetTreeFilter" />
</template>
</q-input>
</q-page-sticky>
<q-tree
class="objectTree q-pa-sm"
@ -36,7 +39,14 @@
<span
class="text-primary text-weight-medium q-ml-xs"
v-if="prop.node.isRoot">
({{prop.node.documentCount}})
({{prop.node.allCount}})
<q-tooltip
:delay="1000"
>
Document count: <span class="text-bold text-gunmetal-bright">{{prop.node.documentCount}}</span>
<br>
Category count: <span class="text-bold text-gunmetal-bright">{{prop.node.categoryCount}}</span>
</q-tooltip>
</span>
<q-badge
class="treeBadge"
@ -63,7 +73,7 @@
color="dark"
class="z-1 q-ml-sm treeButton treeButton--edit"
icon="mdi-pencil"
size="8px"
size="10px"
@click.stop.prevent="openExistingDocumentRoute(prop.node)"
>
<q-tooltip
@ -81,7 +91,7 @@
color="dark"
class="z-1 q-ml-sm treeButton treeButton--add"
icon="mdi-plus"
size="8px"
size="10px"
@click.stop.prevent="processNodeNewDocumentButton(prop.node)"
>
<q-tooltip
@ -380,6 +390,7 @@ export default class ObjectTree extends BaseClass {
return {
label: doc.extraFields.find(e => e.id === "name")?.value,
icon: (isCategory) ? "fas fa-folder-open" : doc.icon,
isCategory: !!(isCategory),
sticker: doc.extraFields.find(e => e.id === "order")?.value,
parentDoc: (parentDocID) ? parentDocID._id : false,
handler: this.openExistingDocumentRoute,
@ -396,7 +407,10 @@ export default class ObjectTree extends BaseClass {
} as I_ShortenedDocument
})
const documentCount = allDocumentsRows.length
const documentCount = allDocumentsRows.filter(e => !e.isCategory).length
const categoryCount = allDocumentsRows.filter(e => e.isCategory).length
const allCount = allDocumentsRows.length
const listCopy: I_ShortenedDocument[] = extend(true, [], allDocumentsRows)
allTreeDocuments = [...allTreeDocuments, ...listCopy]
@ -411,7 +425,9 @@ export default class ObjectTree extends BaseClass {
handler: this.addNewObjectRoute,
specialLabel: blueprint.nameSingular.toLowerCase(),
isRoot: true,
allCount: allCount,
documentCount: documentCount,
categoryCount: categoryCount,
children: [
...hierarchicalTreeContent,
{
@ -459,12 +475,18 @@ export default class ObjectTree extends BaseClass {
})
.sort((a, b) => a.label.localeCompare(b.label))
const documentCount = tagDocs.filter(e => !e.isCategory).length
const categoryCount = tagDocs.filter(e => e.isCategory).length
const allCount = tagDocs.length
const tagObject = {
label: `${tag}`,
icon: "mdi-tag",
_id: `tag-${tag}`,
key: `tag-${tag}`,
documentCount: tagDocs.length,
allCount: allCount,
documentCount: documentCount,
categoryCount: categoryCount,
isRoot: true,
isTag: true,
children: tagDocs
@ -632,12 +654,19 @@ export default class ObjectTree extends BaseClass {
</script>
<style lang="scss">
.objectTree {
padding-top: 60px;
.q-tree__arrow {
margin-right: 0;
padding: 4px 4px 4px 0;
}
.q-tree__node {
padding: 0 0 0 22px;
}
.q-tree__node-header {
padding: 0;
}
@ -675,6 +704,27 @@ export default class ObjectTree extends BaseClass {
}
}
.treeSearchWrapper {
top: -40px;
left: -375px;
width: 375px;
z-index: 555;
background-color: $dark;
> div {
width: 100%;
}
label {
background-color: $dark;
&.q-field--focused {
width: 100vw;
max-width: inherit;
}
}
}
.treeButton {
&--add {
.q-icon {

View file

@ -28,6 +28,12 @@
@trigger-dialog-close="newProjectDialogClose"
/>
<!-- About app dialog -->
<aboutAppDialog
:dialog-trigger="aboutAppDialogTrigger"
@trigger-dialog-close="aboutAppDialogClose"
/>
<q-btn-group
flat
class="AppControl__buttons"
@ -156,6 +162,17 @@
<q-item-section>Show keybind cheatsheet</q-item-section>
</q-item>
<q-item
@click="aboutAppDialogAssignUID"
v-close-popup
clickable
active
active-class="bg-gunmetal-light text-cultured"
class="noHigh"
>
<q-item-section>About Fantasia Archive</q-item-section>
</q-item>
<q-separator dark />
<q-item
@ -188,6 +205,7 @@ import projectCloseCheckDialog from "src/components/dialogs/ProjectCloseCheck.vu
import keybindCheatsheetDialog from "src/components/dialogs/KeybindCheatsheet.vue"
import importProjectCheckDialog from "src/components/dialogs/ImportProjectCheck.vue"
import newProjectCheckDialog from "src/components/dialogs/NewProjectCheck.vue"
import aboutAppDialog from "src/components/dialogs/AboutApp.vue"
import { retrieveCurrentProjectName, exportProject } from "src/scripts/projectManagement/projectManagent"
@ -197,7 +215,8 @@ import { toggleDevTools } from "src/scripts/utilities/devTools"
projectCloseCheckDialog,
keybindCheatsheetDialog,
importProjectCheckDialog,
newProjectCheckDialog
newProjectCheckDialog,
aboutAppDialog
}
})
export default class AppControl extends BaseClass {
@ -280,6 +299,19 @@ export default class AppControl extends BaseClass {
keybindsDialogAssignUID () {
this.keybindsDialogTrigger = this.generateUID()
}
/****************************************************************/
// About app dialog
/****************************************************************/
aboutAppDialogTrigger: string | false = false
aboutAppDialogClose () {
this.aboutAppDialogTrigger = false
}
aboutAppDialogAssignUID () {
this.aboutAppDialogTrigger = this.generateUID()
}
}
</script>

View file

@ -47,6 +47,7 @@
<q-btn
round
dense
flat
class="z-max q-ml-auto"
:class="{'q-mr-sm': document.hasEdits}"
size="xs"

View file

@ -0,0 +1,62 @@
<template>
<q-dialog
v-model="dialogModel"
@hide="triggerDialogClose"
>
<q-card
class="aboutDialog"
dark
>
<q-card-section>
<h6 class="text-center q-my-sm">About Fantasia Archive</h6>
</q-card-section>
<q-card-section>
<div>
Currently running Fantasia Archive version: <span class="text-bold text-primary">{{appVersion}}</span>
</div>
</q-card-section>
<q-card-actions align="around" class="q-mb-lg q-mt-md">
<q-btn flat label="Close" color="accent" v-close-popup />
</q-card-actions>
</q-card>
</q-dialog>
</template>
<script lang="ts">
import { Component, Watch } from "vue-property-decorator"
import DialogBase from "src/components/dialogs/_DialogBase"
@Component({
components: { }
})
export default class AboutApp extends DialogBase {
@Watch("dialogTrigger")
openDialog (val: string|false) {
if (val) {
if (this.SGET_getDialogsState) {
return
}
this.SSET_setDialogState(true)
this.dialogModel = true
}
}
appVersion = process.env.npm_package_version
}
</script>
<style lang="scss">
.aboutDialog {
text-align: center;
width: 500px;
h6 {
display: block;
}
}
</style>

View file

@ -0,0 +1,60 @@
<template>
<q-dialog
v-model="dialogModel"
@hide="triggerDialogClose"
>
<q-card
class="aboutDialog"
dark
>
<q-card-section>
<h6 class="text-center q-my-sm">How to use advanced search</h6>
</q-card-section>
<q-card-section>
<div>
{{$t('tooltips.advancedSearch')}}
</div>
</q-card-section>
<q-card-actions align="around" class="q-mb-lg q-mt-md">
<q-btn flat label="Close" color="accent" v-close-popup />
</q-card-actions>
</q-card>
</q-dialog>
</template>
<script lang="ts">
import { Component, Watch } from "vue-property-decorator"
import DialogBase from "src/components/dialogs/_DialogBase"
@Component({
components: { }
})
export default class AdvancedSearchGuide extends DialogBase {
@Watch("dialogTrigger")
openDialog (val: string|false) {
if (val) {
if (this.SGET_getDialogsState) {
return
}
this.SSET_setDialogState(true)
this.dialogModel = true
}
}
}
</script>
<style lang="scss">
.aboutDialog {
text-align: center;
width: 500px;
h6 {
display: block;
}
}
</style>

View file

@ -74,7 +74,7 @@
<q-tooltip
:delay="300"
>
Add a new document belonging under {{ opt.label }}
Add a new document belonging under {{ stripTags(opt.label) }}
</q-tooltip>
</q-btn>
</q-item>

View file

@ -10,12 +10,12 @@
</q-icon>
<q-icon v-if="isOneWayRelationship" name="mdi-arrow-right-bold" size="16px" class="q-ml-md">
<q-tooltip :delay="500">
This is a one-way relationship. <br> Editing this value <b>will not</b> have effect on the connected document/s.
This is a one-way relationship. <br> Editing this value <span class="text-gunmetal-bright">will not</span> have effect on the connected document/s.
</q-tooltip>
</q-icon>
<q-icon v-if="!isOneWayRelationship" name="mdi-arrow-left-right-bold" size="16px" class="q-ml-md">
<q-tooltip :delay="500">
This is a two-way relationship. <br> Editing this value <b>will</b> also effect the connected document/s.
This is a two-way relationship. <br> Editing this value <span class="text-gunmetal-bright">will</span> also effect the connected document/s.
</q-tooltip>
</q-icon>
@ -32,7 +32,7 @@
class="text-primary"
@click="openExistingDocumentRoute(single)">
<q-item-section>
{{single.label}}
{{stripTags(single.label)}}
<span class="inline-block q-ml-xs text-italic connectionNote">
{{retrieveNoteText(single._id)}}
</span>
@ -57,8 +57,21 @@
input-debounce="0"
v-model="localInput"
@filter="filterSelect"
@input="signalInput"
@input="signalInput(false)"
>
<template v-slot:selected-item="scope">
<q-chip
removable
dense
@remove="scope.removeAtIndex(scope.index)"
:tabindex="scope.tabindex"
color="accent"
text-color="dark"
class="text-bold"
>
{{ stripTags(scope.opt.label) }}
</q-chip>
</template>
<template v-slot:option="{ itemProps, itemEvents, opt }">
<q-item
v-bind="itemProps"
@ -100,14 +113,14 @@
:key="index"
>
<td>
{{localInput[index].label}}
{{stripTags(localInput[index].label)}}
</td>
<td>
<q-input
label="Note"
v-model="singleNote.value"
dense
@keyup="signalInput"
@keyup="signalInput(false)"
outlined
>
</q-input>
@ -154,7 +167,6 @@ export default class Field_SingleRelationship extends BaseClass {
@Prop() readonly editMode!: boolean
changedInput = false
localInput = [] as unknown as I_FieldRelationship[]
@Watch("inputDataValue", { deep: true, immediate: true })
@ -281,11 +293,14 @@ export default class Field_SingleRelationship extends BaseClass {
this.localInput = (Array.isArray(this.localInput)) ? this.localInput : []
let nonExistValue = false
this.localInput.forEach((s, index) => {
if (s._id) {
if (!allObjectsWithoutCurrent.find(e => e._id === s._id)) {
// @ts-ignore
this.localInput.splice(index, 1)
nonExistValue = true
}
else {
const matchedFieldContent = allObjectsWithoutCurrent.find(e => e._id === s._id)
@ -297,6 +312,10 @@ export default class Field_SingleRelationship extends BaseClass {
}
})
if (nonExistValue) {
this.signalInput(true)
}
const allObjectsWithoutCategories: I_FieldRelationship[] = allObjectsWithoutCurrent.filter((obj) => !obj.isCategory)
this.extraInput = allObjectsWithoutCategories
@ -312,16 +331,13 @@ export default class Field_SingleRelationship extends BaseClass {
}
@Emit()
signalInput () {
this.changedInput = true
signalInput (skipSave?: boolean) {
this.checkNotes()
this.inputNotes = this.inputNotes.filter(single => this.localInput.find(e => single.pairedId === e._id))
return {
value: this.localInput,
addedValues: this.inputNotes
addedValues: this.inputNotes,
skipSave: (skipSave)
}
}
}

View file

@ -44,6 +44,20 @@
@input="signalInput"
@keydown="signalInput"
>
<template v-slot:selected-item="scope">
<q-chip
removable
dense
@remove="scope.removeAtIndex(scope.index)"
:tabindex="scope.tabindex"
color="accent"
text-color="dark"
class="text-bold"
>
{{ stripTags(scope.opt) }}
</q-chip>
</template>
</q-select>
<div class="separatorWrapper">

View file

@ -10,12 +10,12 @@
</q-icon>
<q-icon v-if="isOneWayRelationship" name="mdi-arrow-right-bold" size="16px" class="q-ml-md">
<q-tooltip :delay="500">
This is a one-way relationship. <br> Editing this value <b>will not</b> have effect on the connected document/s.
This is a one-way relationship. <br> Editing this value <span class="text-gunmetal-bright">will not</span> have effect on the connected document/s.
</q-tooltip>
</q-icon>
<q-icon v-if="!isOneWayRelationship" name="mdi-arrow-left-right-bold" size="16px" class="q-ml-md">
<q-tooltip :delay="500">
This is a two-way relationship. <br> Editing this value <b>will</b> also effect the connected document/s.
This is a two-way relationship. <br> Editing this value <span class="text-gunmetal-bright">will</span> also effect the connected document/s.
</q-tooltip>
</q-icon>
</div>
@ -29,7 +29,7 @@
class="text-primary"
@click="openExistingDocumentRoute(localInput)">
<q-item-section>
{{localInput.label}}
{{stripTags(localInput.label)}}
<span class="inline-block q-ml-xs text-italic connectionNote">
{{retrieveNoteText()}}
</span>
@ -45,15 +45,30 @@
dark
style="flex-grow: 1;"
dense
:ref="`singleRelationshipField${this.inputDataBluePrint.id}`"
:ref="`singleRelationshipField${inputDataBluePrint.id}`"
:options="filteredInput"
use-input
outlined
input-debounce="0"
v-model="localInput"
@filter="filterSelect"
@input="signalInput"
@input="signalInput(false)"
>
<template v-slot:selected-item="scope">
<q-chip
v-if="scope.opt.label && scope.opt.label.length > 0"
removable
dense
@remove="scope.removeAtIndex(scope.index)"
:tabindex="scope.tabindex"
color="accent"
text-color="dark"
class="text-bold"
>
{{ stripTags(scope.opt.label) }}
</q-chip>
</template>
<template v-slot:option="{ itemProps, itemEvents, opt }">
<q-item
v-bind="itemProps"
@ -92,14 +107,14 @@
<table class="q-mt-sm" v-if="localInput && inputFieldID !== 'parentDoc'">
<tr>
<td>
{{localInput.label}}
{{stripTags(localInput.label)}}
</td>
<td>
<q-input
label="Note"
v-model="inputNote.value"
dense
@keyup="signalInput"
@keyup="signalInput(false)"
outlined
>
</q-input>
@ -144,7 +159,6 @@ export default class Field_SingleRelationship extends BaseClass {
@Prop() readonly editMode!: boolean
changedInput = false
localInput = "" as unknown as I_FieldRelationship
inputNote: { pairedId: string; value: string; } = {
@ -282,6 +296,7 @@ export default class Field_SingleRelationship extends BaseClass {
if (!objectsWithoutCurrent.find(e => e._id === this.localInput._id)) {
// @ts-ignore
this.localInput = ""
this.signalInput(true)
}
else {
const matchedFieldContent = objectsWithoutCurrent.find(e => e._id === this.localInput._id)
@ -295,13 +310,15 @@ export default class Field_SingleRelationship extends BaseClass {
}
}
debounceTimerCheck = false
@Emit()
signalInput () {
this.changedInput = true
signalInput (skipSave?: boolean) {
this.inputNote = (this.localInput !== null) ? this.inputNote : { pairedId: "", value: "" }
return {
value: this.localInput,
addedValues: this.inputNote
addedValues: this.inputNote,
skipSave: (skipSave)
}
}
}

View file

@ -39,6 +39,21 @@
@input="signalInput"
@keydown="signalInput"
>
<template v-slot:selected-item="scope">
<q-chip
v-if="scope.opt && scope.opt.length > 0"
removable
dense
@remove="scope.removeAtIndex(scope.index)"
:tabindex="scope.tabindex"
color="accent"
text-color="dark"
class="text-bold"
>
{{ scope.opt }}
</q-chip>
</template>
</q-select>
<div class="separatorWrapper">

View file

@ -15,7 +15,7 @@
>
<q-chip
v-for="(input,index) in localInput" :key="index"
color="primary" text-color="white" class="text-bold">
color="accent" text-color="dark" class="text-bold">
{{input}}
</q-chip>
</div>
@ -41,6 +41,19 @@
@input="signalInput"
@keydown="signalInput"
>
<template v-slot:selected-item="scope">
<q-chip
removable
dense
@remove="scope.removeAtIndex(scope.index)"
:tabindex="scope.tabindex"
color="accent"
text-color="dark"
class="text-bold"
>
{{ stripTags(scope.opt) }}
</q-chip>
</template>
</q-select>
<div class="separatorWrapper">

View file

@ -75,11 +75,36 @@ body {
}
.q-field__input,
.q-icon,
.q-field__native span {
color: #000 !important;
}
}
.tagSelect,
.singleSelect,
.multiSelect,
.singleRelashionshipSelect,
.multiRelashionshipSelect {
.q-chip {
height: inherit;
&--dense {
padding: 0.2em 0.5em;
}
&__content {
white-space: normal;
flex-wrap: wrap;
}
&__icon--remove {
margin-right: -0.2em;
margin-left: 0.2em;
}
}
}
.q-menu .q-item {
transition: none !important;
}
@ -142,5 +167,4 @@ body .q-tooltip {
color: $dark;
font-size: 13px;
font-weight: 600;
max-width: 800px;
}

View file

@ -13,7 +13,7 @@
// Tip: Use the "Theme Builder" on Quasar's documentation website.
$primary : #d7ac47;
$secondary : #e3173a;
$secondary : #f72e43;
$accent : #dde4e4;
$dark : #18303a;
@ -28,7 +28,7 @@ $customColors: (
'gunmetal-light': lighten(#18303a, 7.5),
'gunmetal-bright': lighten(#18303a, 30),
'gunmetal-shine': lighten(#18303a, 60),
'ruby-red': #e3173a,
'ruby-red': #f72e43,
'satin-sheen-gold': #d7ac47,
'gainsboro': #dde4e4,
'cultured': #f5f5f5

View file

@ -3,5 +3,9 @@
export default {
failed: "Action failed",
success: "Action was successful"
success: "Action was successful",
tooltips: {
advancedSearch: `stuff
more stuff`
}
}

View file

@ -4,30 +4,6 @@
>
<div class="row justify-start q-col-gutter-x-xl">
<div class="col-12 flex justify-end q-mb-lg q-mt-md">
<q-btn
color="primary"
:label="`Add a new ${bluePrintData.nameSingular} belonging under ${retrieveFieldValue(currentData, 'name')}`"
@click="addNewUnderParent"
class="q-mr-xl"
v-if="!currentData.isNew"
/>
<q-btn
color="primary"
:label="`Save ${bluePrintData.nameSingular}`"
@click="saveDocument"
class="q-mr-xl"
v-if="editMode"
/>
<q-btn
color="primary"
:label="`Edit ${bluePrintData.nameSingular}`"
@click="toggleEditMode"
class="q-mr-xl"
v-if="!editMode"
/>
</div>
<div
:class="`col-${field.sizing} q-mb-md`"
v-for="field in bluePrintData.extraFields"
@ -170,11 +146,10 @@ import BaseClass from "src/BaseClass"
import { I_Blueprint, I_ExtraFields } from "src/interfaces/I_Blueprint"
import { I_OpenedDocument } from "src/interfaces/I_OpenedDocument"
import PouchDB from "pouchdb"
// import { cleanDatabases } from "src/scripts/databaseManager/cleaner"
import { single_changeRelationshipToAnotherObject, many_changeRelationshipToAnotherObject } from "src/scripts/databaseManager/relationshipManager"
import { extend } from "quasar"
import { saveDocument } from "src/scripts/databaseManager/documentManager"
import Field_Break from "src/components/fields/Field_Break.vue"
import Field_Text from "src/components/fields/Field_Text.vue"
import Field_Number from "src/components/fields/Field_Number.vue"
@ -210,7 +185,24 @@ export default class PageDocumentDisplay extends BaseClass {
* Watches on changes of the route in order to load proper blueprint and object data
*/
@Watch("$route", { immediate: true, deep: true })
async onUrlChange () {
onUrlChange () {
this.reloadLocalContent().catch(e => console.log(e))
}
/**
* Watches on changes of the opened documents in order to load proper blueprint and object data
*/
@Watch("SGET_allOpenedDocuments", { deep: true })
async onDocChange () {
await this.sleep(300)
const matchingDoc = this.findRequestedOrActiveDocument()
if (matchingDoc && matchingDoc._id === this.currentData._id && !matchingDoc.hasEdits) {
this.reloadLocalContent().catch(e => console.log(e))
}
}
async reloadLocalContent () {
// Determine the type and retrieve the right blueprint
this.bluePrintData = this.retrieveDocumentBlueprint()
@ -251,7 +243,7 @@ export default class PageDocumentDisplay extends BaseClass {
this.SSET_addOpenedDocument(dataPass)
}
reactToFieldUpdate (inputData: string, field: I_ExtraFields) {
async reactToFieldUpdate (inputData: string, field: I_ExtraFields) {
// FIELD - Text
if (field.type === "text") {
this.currentData.hasEdits = true
@ -356,6 +348,14 @@ export default class PageDocumentDisplay extends BaseClass {
const dataCopy: I_OpenedDocument = extend(true, {}, this.currentData)
const dataPass = { doc: dataCopy, treeAction: false }
// @ts-ignore
if (inputData.skipSave) {
this.currentData.extraFields[indexToUpdate].value.skipSave = false
await this.triggerSaveDocument()
return
}
this.SSET_updateOpenedDocument(dataPass)
}
@ -368,6 +368,14 @@ export default class PageDocumentDisplay extends BaseClass {
const dataCopy: I_OpenedDocument = extend(true, {}, this.currentData)
const dataPass = { doc: dataCopy, treeAction: false }
// @ts-ignore
if (inputData.skipSave) {
this.currentData.extraFields[indexToUpdate].value.skipSave = false
await this.triggerSaveDocument()
return
}
this.SSET_updateOpenedDocument(dataPass)
}
@ -396,129 +404,40 @@ export default class PageDocumentDisplay extends BaseClass {
}
}
toggleEditMode () {
this.editMode = true
this.currentData.editMode = true
const dataCopy: I_OpenedDocument = extend(true, {}, this.currentData)
const dataPass = { doc: dataCopy, treeAction: false }
this.SSET_updateOpenedDocument(dataPass)
}
async triggerSaveDocument () {
const currentDoc = this.currentData
async saveDocument () {
this.editMode = false
this.currentData.isNew = false
this.currentData.hasEdits = false
this.currentData.editMode = false
const allDocuments = this.SGET_allOpenedDocuments
const CurrentObjectDB = new PouchDB(this.$route.params.type)
const docCopy: I_OpenedDocument[] = extend(true, [], allDocuments.docs)
let currentDocument = false as unknown as I_OpenedDocument
try {
currentDocument = await CurrentObjectDB.get(this.$route.params.id)
if (currentDoc) {
// @ts-ignore
const savedDocument: {
documentCopy: I_OpenedDocument,
allOpenedDocuments: I_OpenedDocument[]
} = await saveDocument(currentDoc, docCopy)
// Update the opened document
const dataPass = { doc: savedDocument.documentCopy, treeAction: true }
this.SSET_updateOpenedDocument(dataPass)
// Update all others
for (const doc of savedDocument.allOpenedDocuments) {
// Update the opened document
const dataPass = { doc: doc, treeAction: true }
this.SSET_updateOpenedDocument(dataPass)
}
this.editMode = false
this.currentData.isNew = false
this.currentData.hasEdits = false
this.currentData.editMode = false
}
catch (error) {}
let documentCopy = {} as unknown as I_OpenedDocument
if (currentDocument) {
documentCopy = extend(true, {}, this.currentData)
documentCopy._rev = currentDocument?._rev
}
else {
documentCopy = extend(true, {}, this.currentData)
}
// Handle relatinship fields
const single_relationshipTypes = ["singleToSingleRelationship", "singleToManyRelationship"]
const single_allRelationshipFields = documentCopy.extraFields.filter(field => {
const currentField = this.bluePrintData.extraFields.find(e => e.id === field.id) as unknown as I_ExtraFields
return (currentField && single_relationshipTypes.includes(currentField.type))
})
const many_relationshipTypes = ["manyToSingleRelationship", "manyToManyRelationship"]
const many_allRelationshipFields = documentCopy.extraFields.filter(field => {
const currentField = this.bluePrintData.extraFields.find(e => e.id === field.id) as unknown as I_ExtraFields
return (currentField && many_relationshipTypes.includes(currentField.type))
})
// Update single fields
for (const field of single_allRelationshipFields) {
const single_updatedDocuments: I_OpenedDocument[] = await single_changeRelationshipToAnotherObject(field, documentCopy, currentDocument)
const pairedFieldID = this.bluePrintData.extraFields.find(e => e.id === field.id)?.relationshipSettings?.connectedField
const filteredDocuments = single_updatedDocuments.filter(doc => {
return this.SGET_allOpenedDocuments.docs.find(subDoc => {
return subDoc._id === doc._id
})
})
// Update the particular field in each currently opened document
filteredDocuments.forEach(doc => {
const toUpdateIndex = doc.extraFields.findIndex(e => e.id === pairedFieldID)
if (toUpdateIndex) {
const docCopy: I_OpenedDocument = extend(true, {}, this.SGET_openedDocument(doc._id))
docCopy.extraFields[toUpdateIndex] = doc.extraFields[toUpdateIndex]
// Tree action here due to how parentDoc field works
const dataPass = { doc: docCopy, treeAction: true }
this.SSET_updateOpenedDocument(dataPass)
}
})
}
// Update many fields
for (const field of many_allRelationshipFields) {
const many_updatedDocuments: I_OpenedDocument[] = await many_changeRelationshipToAnotherObject(field, documentCopy, currentDocument)
const pairedFieldID = this.bluePrintData.extraFields.find(e => e.id === field.id)?.relationshipSettings?.connectedField
const filteredDocuments = many_updatedDocuments.filter(doc => {
return this.SGET_allOpenedDocuments.docs.find(subDoc => {
return subDoc._id === doc._id
})
})
// Update the particular field in each currently opened document
filteredDocuments.forEach(doc => {
const toUpdateIndex = doc.extraFields.findIndex(e => e.id === pairedFieldID)
if (toUpdateIndex) {
const docCopy: I_OpenedDocument = extend(true, {}, this.SGET_openedDocument(doc._id))
docCopy.extraFields[toUpdateIndex] = doc.extraFields[toUpdateIndex]
const dataPass = { doc: docCopy, treeAction: false }
this.SSET_updateOpenedDocument(dataPass)
}
})
}
// Save the document
await CurrentObjectDB.put(documentCopy)
// Update the opened document
const dataPass = { doc: documentCopy, treeAction: true }
this.SSET_updateOpenedDocument(dataPass)
}
editMode = false
/**
* React to keypresses
*/
@Watch("SGET_getCurrentKeyBindData", { deep: true })
processKeyPush () {
// Save document
if (this.determineKeyBind("saveDocument") && this.editMode) {
this.saveDocument().catch(e => {
console.log(e)
})
}
// Edit document - CTRL + E
if (this.determineKeyBind("editDocument") && !this.editMode) {
this.toggleEditMode()
}
}
currentData = false as unknown as I_OpenedDocument
/**
@ -628,15 +547,6 @@ export default class PageDocumentDisplay extends BaseClass {
const ignoredList = ["breakBasic", "name", "documentColor", "parentDoc", "order", "categorySwitch"]
return (!isCategory || ignoredList.includes(currentFieldID))
}
addNewUnderParent () {
const routeObject = {
_id: this.currentData.type,
parent: this.currentData._id
}
// @ts-ignore
this.addNewObjectRoute(routeObject)
}
}
</script>

View file

@ -0,0 +1,103 @@
import { single_changeRelationshipToAnotherObject, many_changeRelationshipToAnotherObject } from "src/scripts/databaseManager/relationshipManager"
import { I_OpenedDocument } from "src/interfaces/I_OpenedDocument"
import { I_ExtraFields } from "src/interfaces/I_Blueprint"
import { extend } from "quasar"
import PouchDB from "pouchdb"
/**
* Saves the given project and handles all the needed procedures
*/
export const saveDocument = async (document: I_OpenedDocument, allOpenedDocuments: I_OpenedDocument[]) => {
const BlueprintsDB = new PouchDB("blueprints")
const currentBlueprint: {extraFields: I_ExtraFields[]} = await BlueprintsDB.get(document.type)
const CurrentObjectDB = new PouchDB(document.type)
let currentDocument = false as unknown as I_OpenedDocument
try {
currentDocument = await CurrentObjectDB.get(document._id)
}
catch (error) {}
let documentCopy = {} as unknown as I_OpenedDocument
if (currentDocument) {
documentCopy = extend(true, {}, document)
documentCopy._rev = currentDocument?._rev
}
else {
documentCopy = extend(true, {}, document)
}
allOpenedDocuments = allOpenedDocuments.filter(doc => doc._id !== document._id)
// Handle relatinship fields
const single_relationshipTypes = ["singleToSingleRelationship", "singleToManyRelationship"]
const single_allRelationshipFields = documentCopy.extraFields.filter(field => {
const currentField = currentBlueprint.extraFields.find(e => e.id === field.id) as unknown as I_ExtraFields
return (currentField && single_relationshipTypes.includes(currentField.type))
})
const many_relationshipTypes = ["manyToSingleRelationship", "manyToManyRelationship"]
const many_allRelationshipFields = documentCopy.extraFields.filter(field => {
const currentField = currentBlueprint.extraFields.find(e => e.id === field.id) as unknown as I_ExtraFields
return (currentField && many_relationshipTypes.includes(currentField.type))
})
// Update single fields
for (const field of single_allRelationshipFields) {
const single_updatedDocuments: I_OpenedDocument[] = await single_changeRelationshipToAnotherObject(field, documentCopy, currentDocument)
const pairedFieldID = currentBlueprint.extraFields.find(e => e.id === field.id)?.relationshipSettings?.connectedField
const filteredDocuments = single_updatedDocuments.filter(doc => {
return allOpenedDocuments.find(subDoc => {
return subDoc._id === doc._id
})
})
// Update the particular field in each currently opened document
filteredDocuments.forEach(doc => {
const toUpdateIndex = doc.extraFields.findIndex(e => e.id === pairedFieldID)
if (toUpdateIndex) {
const matchingDoc = allOpenedDocuments.find(subDoc => subDoc._id === doc._id)
if (matchingDoc) {
matchingDoc.extraFields[toUpdateIndex] = doc.extraFields[toUpdateIndex]
}
}
})
}
// Update many fields
for (const field of many_allRelationshipFields) {
const many_updatedDocuments: I_OpenedDocument[] = await many_changeRelationshipToAnotherObject(field, documentCopy, currentDocument)
const pairedFieldID = currentBlueprint.extraFields.find(e => e.id === field.id)?.relationshipSettings?.connectedField
const filteredDocuments = many_updatedDocuments.filter(doc => {
return allOpenedDocuments.find(subDoc => {
return subDoc._id === doc._id
})
})
// Update the particular field in each currently opened document
filteredDocuments.forEach(doc => {
const toUpdateIndex = doc.extraFields.findIndex(e => e.id === pairedFieldID)
if (toUpdateIndex) {
const matchingDoc = allOpenedDocuments.find(subDoc => subDoc._id === doc._id)
if (matchingDoc) {
matchingDoc.extraFields[toUpdateIndex] = doc.extraFields[toUpdateIndex]
}
}
})
}
documentCopy.isNew = false
documentCopy.hasEdits = false
documentCopy.editMode = false
// Save the document
await CurrentObjectDB.put(documentCopy)
return { documentCopy, allOpenedDocuments }
}

View file

@ -1,11 +1,13 @@
import { I_OpenedDocument } from "./../../interfaces/I_OpenedDocument"
export interface OpenDocumentsStateInterface {
documents: {
timestamp: string,
treeAction: boolean,
docs: I_OpenedDocument[]
}
documents: InnerOpenDocumentsStateInterface
}
export interface InnerOpenDocumentsStateInterface {
timestamp: string,
treeAction: boolean,
docs: I_OpenedDocument[]
}
function state (): OpenDocumentsStateInterface {