瀏覽代碼

feat: added basic autosuggesting

Kristian Vos 4 年之前
父節點
當前提交
53d9283d60

+ 65 - 2
backend/logic/io.js

@@ -11,6 +11,9 @@ const express = require("express");
 const http = require("http");
 const socketio = require('socket.io');
 
+let temporaryAutosuggestCache = {};
+let temporaryAutosuggestMap = {};
+
 module.exports = class extends coreClass {
 	constructor(name, moduleManager) {
 		super(name, moduleManager);
@@ -28,7 +31,47 @@ module.exports = class extends coreClass {
 
 			this.mongo = this.moduleManager.modules["mongo"];
 
+			this.mongo.models.accountSchema.find({}, null, { sort: "-version", limit: 1 }, (err, res) => {
+				if (!err) {
+					res[0].fields.forEach(field => {
+						field.fieldTypes.forEach(fieldType => {
+							if (fieldType.type === "text" && fieldType.autosuggestGroup) {
+								temporaryAutosuggestMap[`${field.fieldId}.${fieldType.fieldTypeId}`] = fieldType.autosuggestGroup;
+								temporaryAutosuggestCache[fieldType.autosuggestGroup] = [];
+							}
+						});
+					});
+
+					this.mongo.models.account.find({}, (err, accounts) => {
+						if (!err) {
+							accounts.forEach(account => {
+								Object.keys(temporaryAutosuggestMap).forEach(key => {
+									let autosuggestGroup = temporaryAutosuggestMap[key];
+									let fieldId = key.split(".")[0];
+									let fieldTypeId = key.split(".")[1];
+									account.fields[fieldId].forEach(field => {
+										if (temporaryAutosuggestCache[autosuggestGroup].indexOf(field[fieldTypeId]) === -1)
+											temporaryAutosuggestCache[autosuggestGroup].push(field[fieldTypeId]);
+									});
+								});
+							});
+
+							console.log(temporaryAutosuggestCache);
+							console.log(temporaryAutosuggestMap);
+						}
+					});
+				}
+			});
+
+			
+
 			this.handlers = {
+				"getAutosuggest": cb => {
+					cb({
+						autosuggest: temporaryAutosuggestCache
+					});
+				},
+
 				"getAccounts": cb => {
 					this.mongo.models.account.find({}, (err, accounts) => {
 						if (err)
@@ -66,11 +109,21 @@ module.exports = class extends coreClass {
 								status: "failure",
 								err: err
 							});
-						else
+						else {
+							Object.keys(temporaryAutosuggestMap).forEach(key => {
+								let autosuggestGroup = temporaryAutosuggestMap[key];
+								let fieldId = key.split(".")[0];
+								let fieldTypeId = key.split(".")[1];
+								account.fields[fieldId].forEach(field => {
+									if (temporaryAutosuggestCache[autosuggestGroup].indexOf(field[fieldTypeId]) === -1)
+										temporaryAutosuggestCache[autosuggestGroup].push(field[fieldTypeId]);
+								});
+							});
 							console.log("Added account!");
 							return cb({
 								status: "success"
 							});
+						}
 					});
 				},
 
@@ -81,11 +134,21 @@ module.exports = class extends coreClass {
 								status: "failure",
 								err: err
 							});
-						else
+						else {
+							Object.keys(temporaryAutosuggestMap).forEach(key => {
+								let autosuggestGroup = temporaryAutosuggestMap[key];
+								let fieldId = key.split(".")[0];
+								let fieldTypeId = key.split(".")[1];
+								account.fields[fieldId].forEach(field => {
+									if (temporaryAutosuggestCache[autosuggestGroup].indexOf(field[fieldTypeId]) === -1)
+										temporaryAutosuggestCache[autosuggestGroup].push(field[fieldTypeId]);
+								});
+							});
 							console.log("Editted account!");
 							return cb({
 								status: "success"
 							});
+						}
 					});
 				},
 

+ 7 - 4
backend/schemas/accountSchemaV3.js → backend/schemas/accountSchemaV4.js

@@ -1,7 +1,7 @@
 module.exports = {
 	name: "Account",
 	description: "Account schema",
-	version: 3,
+	version: 4,
 	fields: [
 		{
 			name: "Name",
@@ -23,7 +23,8 @@ module.exports = {
 				{
 					type: "text",
 					fill: true,
-					fieldTypeId: "domain"
+					fieldTypeId: "domain",
+					autosuggestGroup: "domain"
 				}
 			],
 			minEntries: 0,
@@ -79,7 +80,8 @@ module.exports = {
 				{
 					type: "text",
 					fieldTypeId: "email",
-					fill: true
+					fill: true,
+					autosuggestGroup: "email"
 				}
 			],
 			minEntries: 0,
@@ -268,7 +270,8 @@ module.exports = {
 				{
 					type: "text",
 					fieldTypeId: "recoveryEmail",
-					fill: true
+					fill: true,
+					autosuggestGroup: "email"
 				}
 			],
 			minEntries: 0,

+ 7 - 1
frontend/vue/components/AccountForm.vue

@@ -6,6 +6,7 @@
 			:minEntries="field.minEntries"
 			:maxEntries="field.maxEntries"
 			:initialEntries="account.fields[field.fieldId]"
+			:autosuggest="autosuggest"
 			:key="field.fieldId"
 			:ref="field.fieldId"
 			:fieldTypes="field.fieldTypes"/>
@@ -25,7 +26,8 @@ export default {
 	data: function() {
 		return {
 			fields: [],
-			account: {}
+			account: {},
+			autosuggest: {}
 		};
 	},
 	methods: {
@@ -70,6 +72,10 @@ export default {
 					this.account = this.initialAccount;
 				}
 			});
+
+			socket.emit("getAutosuggest", res => {
+				this.autosuggest = res.autosuggest;
+			});
 		});
 	}
 };

+ 1 - 1
frontend/vue/components/AccountsList.vue

@@ -1,7 +1,7 @@
 <template>
 	<div class="accounts-list">
 		<div class="account" v-for="(account, accountIndex) in accounts">
-			Account
+			{{ account.fields.name[0].name }}
 			<router-link
 				:to="`edit/${account._id}`"
 			>

+ 55 - 6
frontend/vue/components/Field.vue

@@ -4,7 +4,7 @@
 		<button v-if="entries.length === 0" @click="addEntry()" type="button">+</button>
 		<div class="control-row" v-for="(entry, entryIndex) in entries">
 			<div class="control-col" v-for="(fieldType, fieldIndex) in fieldTypes" :class="{ 'fill-remaining': fieldType.fill }">
-				<input name="name" type="text" v-if="fieldType.type === 'text'" v-model="entry[fieldType.fieldTypeId]"/>
+				<input name="name" type="text" v-if="fieldType.type === 'text'" v-model="entry[fieldType.fieldTypeId]" v-on:blur="blurInput(entryIndex, fieldType.fieldTypeId)" v-on:focus="focusInput(entryIndex, fieldType.fieldTypeId)" v-on:keydown="keydownInput(entryIndex, fieldType.fieldTypeId)" />
 				<select name="name" v-if="fieldType.type === 'select'" class="fill-remaining" v-model="entry[fieldType.fieldTypeId]">
 					<option v-for="option in fieldType.options" :value="option.value">{{option.text}}</option>
 				</select>
@@ -13,8 +13,15 @@
 				<button v-if="fieldType.extraButtons" v-for="buttonInfo in fieldType.extraButtons" type="button" :class="[buttonInfo.style]">{{buttonInfo.icon}}</button>
 				<button v-if="entryIndex + 1 === entries.length && entryIndex + 1 < maxEntries && fieldIndex + 1 === fieldTypes.length" @click="addEntry()" type="button">+</button>
 				<button v-if="entries.length > minEntries && fieldIndex + 1 === fieldTypes.length" @click="removeEntry(entryIndex)" type="button">-</button>
+
+				<div v-if="fieldType.autosuggestGroup && (focusedInput === `${entryIndex}.${fieldType.fieldTypeId}` || autosuggestHover === `${entryIndex}.${fieldType.fieldTypeId}`)" class="autosuggest-container" @mouseover="focusAutosuggestContainer(entryIndex, fieldType.fieldTypeId)" @mouseleave="blurAutosuggestContainer()">
+					<div v-for="autosuggestItem in filter(autosuggest[fieldType.autosuggestGroup], entry[fieldType.fieldTypeId])" class="autosuggest-item" @click="selectAutosuggest(entryIndex, fieldType.fieldTypeId, autosuggestItem)">
+						{{ autosuggestItem }}
+					</div>
+				</div>
 			</div>
 		</div>
+		
 	</div>
 </template>
 
@@ -26,18 +33,18 @@ function Field() {
 export default {
 	data: function() {
 		return {
-			entries: [...this.initialEntries]
+			entries: [...this.initialEntries],
+			focusedInput: "",
+			activeAutosuggestHover: ""
 		};
-	},
-	methods: {
-		
 	},
 	props: {
 		name: String,
 		fieldTypes: Array,
 		minEntries: Number,
 		maxEntries: Number,
-		initialEntries: Array
+		initialEntries: Array,
+		autosuggest: Object
 	},
 	mounted() {
 		
@@ -56,6 +63,27 @@ export default {
 		},
 		toggleCheckbox(entryIndex, fieldTypeId) {
 			this.entries[entryIndex][fieldTypeId] = !this.entries[entryIndex][fieldTypeId];
+		},
+		selectAutosuggest(entryIndex, fieldTypeId, autosuggestItem) {
+			this.entries[entryIndex][fieldTypeId] = autosuggestItem;
+		},
+		blurInput(entryIndex, fieldTypeId) {
+			this.focusedInput = "";
+		},
+		focusInput(entryIndex, fieldTypeId) {
+			this.focusedInput = `${entryIndex}.${fieldTypeId}`;
+		},
+		keydownInput(entryIndex, fieldTypeId) {
+
+		},
+		focusAutosuggestContainer(entryIndex, fieldTypeId) {
+			this.autosuggestHover = `${entryIndex}.${fieldTypeId}`;
+		},
+		blurAutosuggestContainer() {
+			this.autosuggestHover = "";
+		},
+		filter(autosuggest, value) {
+			return autosuggest.filter(autosuggestItem => autosuggestItem.toLowerCase().startsWith(value.toLowerCase())).sort();
 		}
 	}
 };
@@ -83,6 +111,7 @@ export default {
 
 	.control-col {
 		display: flex;
+		position: relative;
 		button:last-of-type {
 			border-radius: 0 5px 5px 0;
 		}
@@ -92,6 +121,26 @@ export default {
 			border-top-right-radius: 0;
 			border-bottom-right-radius: 0;
 		}
+
+		.autosuggest-container {
+			position: absolute;
+			top: 31px;
+			width: 100%;
+			border-radius: 5px 5px 5px 5px;
+			border: solid .5px #464646;
+			z-index: 10;
+
+			.autosuggest-item {
+				padding: 8px;
+				cursor: pointer;
+				background-color: white;
+				
+
+				&:hover, &:focus {
+					background-color: lightgray;
+				}
+			}
+		}
 	}
 
 	input, select {