Started to build out the app. Its got a basic set of features and it should really be in VC

This commit is contained in:
Max G
2019-07-19 20:51:57 +00:00
parent dbc3e5428c
commit 61754fe290
513 changed files with 81139 additions and 0 deletions

View File

@@ -0,0 +1,121 @@
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<h2>Essential Links</h2>
<ul>
<li>
<a
href="https://vuejs.org"
target="_blank"
>
Core Docs
</a>
</li>
<li>
<a
href="https://forum.vuejs.org"
target="_blank"
>
Forum
</a>
</li>
<li>
<a
href="https://chat.vuejs.org"
target="_blank"
>
Community Chat
</a>
</li>
<li>
<a
href="https://twitter.com/vuejs"
target="_blank"
>
Twitter
</a>
</li>
<br>
<li>
<a
href="http://vuejs-templates.github.io/webpack/"
target="_blank"
>
Docs for This Template
</a>
</li>
</ul>
<h2>Ecosystem</h2>
<ul>
<li>
<a
href="http://router.vuejs.org/"
target="_blank"
>
vue-router
</a>
</li>
<li>
<a
href="http://vuex.vuejs.org/"
target="_blank"
>
vuex
</a>
</li>
<li>
<a
href="http://vue-loader.vuejs.org/"
target="_blank"
>
vue-loader
</a>
</li>
<li>
<a
href="https://github.com/vuejs/awesome-vue"
target="_blank"
>
awesome-vue
</a>
</li>
<li>
<a
href="http://marvin.local/solr"
target="_self"
>
solr
</a>
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
return {
msg: 'Welcome to Your mother fucking Vue App'
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
font-weight: normal;
}
ul {
list-style-type: none;
padding: 0;
}
li {
display: inline-block;
margin: 0 10px;
}
a {
color: #42b983;
}
</style>

View File

@@ -0,0 +1,225 @@
<template>
<div id="InputNotes" class="master-note-edit">
<ckeditor ref="main-edit"
:editor="editor" @ready="onReady" v-model="noteText" :config="editorConfig" v-on:blur="save"></ckeditor>
<div class="ui buttons">
<div class="ui green button">{{statusText}}</div>
<div class="ui button">Delete</div>
<div v-on:click="close" class="ui button">Close</div>
<div class="ui disabled button">{{lastNoteHash}}</div>
<div class="ui disabled button">Last Update: {{updated}}</div>
</div>
<div class="ui segment">
<note-tag-edit :noteId="noteid" :key="'tags-for-note-'+noteid"/>
</div>
<div class="ui segment" v-if="false">
Block formatting
The following block formatting options are available:
<p> Bulleted list Start a line with * or - followed by a space.</p>
<p> Numbered list Start a line with 1. or 1) followed by a space.</p>
<p> Headings Start a line with # or ## or ### followed by a space to create a heading 1, heading 2 or heading 3 (up to heading 6 if options defines more headings).</p>
<p> Block quote Start a line with > followed by a space.</p>
Inline formatting
The following inline formatting options are available:
Bold Type **text** or __text__,
Italic Type *text* or _text_,
Code Type `text`.
</div>
</div>
</template>
<script>
import axios from 'axios'
import DecoupledEditor from '@ckeditor/ckeditor5-build-decoupled-document';
export default {
name: 'InputNotes',
props: [ 'noteid' ],
components:{
'note-tag-edit': require('./NoteTagEdit.vue').default
},
data(){
return {
currentNoteId: 0,
noteText: '',
statusText: 'Save',
lastNoteHash: null,
updated: 'Never',
editDebounce: null,
keyPressesCounter: 0,
editor: DecoupledEditor,
editorConfig: {
startupFocus: 'end',
toolbar: ["alignment", "fontSize", "removeHighlight", "highlight", "bold", "italic", "strikethrough", "underline", "blockQuote", "heading", "link", "numberedList", "bulletedList", "insertTable", "|", "undo", "redo"]
}
}
},
watch: {
noteid:function(newVal, oldVal){
if(newVal == this.currentNoteId){
return
}
if(newVal == oldVal){
return
}
this.currentNoteId = newVal
this.loadNote(this.currentNoteId)
}
},
beforeMount(){
},
beforeDestroy(){
},
mounted: function() {
this.loadNote(this.noteid)
},
methods: {
loadNote(noteId){
let vm = this
//Component is activated with NoteId in place, lookup text with associated ID
if(this.$store.getters.getLoggedIn){
axios.post('/api/notes/get', {'noteId': noteId})
.then(response => {
//Set up local data
vm.currentNoteId = noteId
vm.noteText = response.data.text
vm.updated = response.data.updated
vm.lastNoteHash = vm.hashString(response.data.text)
//Put focus on note, at the end of the note text
vm.$nextTick(() => {
// vm.$refs['custom-input'].focus()
})
})
} else {
console.log('Could not fetch note')
}
},
onReady(editor){
let vm = this
console.log(vm)
// Insert the toolbar before the editable area.
editor.ui.getEditableElement().parentElement.insertBefore(
editor.ui.view.toolbar.element,
editor.ui.getEditableElement()
);
editor.editing.view.focus()
// const editor = this.editor;
const view = editor.editing.view;
const viewDocument = view.document;
//Insert 5 spaces when tab is pressed
viewDocument.on( 'keyup', ( evt, data ) => {
//Each note, save after 5 seconds, focus lost or 30 characters typed.
clearTimeout(vm.editDebounce)
vm.editDebounce = setTimeout(() => {
vm.save()
}, 5000)
//Save after 20 keystrokes
vm.keyPressesCounter = (vm.keyPressesCounter + 1)
if(vm.keyPressesCounter > 30){
vm.save()
}
if( (data.keyCode == 9) && viewDocument.isFocused ){
//Insert 5 spaces to simulate tab
editor.execute( 'input', { text: " " } );
evt.stop(); // Prevent executing the default handler.
data.preventDefault();
view.scrollToTheSelection();
}
} );
},
save(){
console.log('Save, ', this.keyPressesCounter)
this.keyPressesCounter = 0
clearTimeout(this.editDebounce)
//Don't save note if its hash doesn't change
if(this.lastNoteHash == this.hashString(this.noteText)){
return
}
const postData = {
'noteId':this.currentNoteId,
'text': this.noteText
}
let vm = this
axios.post('/api/notes/update', postData).then( response => {
vm.statusText = 'Saved'
//Update last saved note hash
vm.lastNoteHash = vm.hashString(vm.noteText)
setTimeout(() => {
vm.statusText = 'Save'
}, 5000)
})
},
hashString(text){
var hash = 0;
if (text.length == 0) {
return hash;
}
for (let i = 0; i < text.length; i++) {
let char = text.charCodeAt(i);
hash = ((hash<<5)-hash)+char;
hash = hash & hash; // Convert to 32bit integer
}
return hash;
},
close(){
this.$bus.$emit('close_active_note')
}
}
}
</script>
<style type="text/css" scoped>
.master-note-edit {
position: fixed;
left: 10%;
right: 10%;
bottom: 0;
background: green;
height: 98vh;
border-top-right-radius: 4px;
border-top-left-radius: 4px;
box-shadow: 0px 0px 5px 2px rgba(140,140,140,1);
}
</style>

View File

@@ -0,0 +1,77 @@
<template>
<div>
<h3>{{getRudeMessage}}</h3>
<p>Login</p>
<div class="ui segment" v-on:keyup.enter="submit">
<div class="field">
<div class="ui input">
<input v-model="username" type="text" name="email" placeholder="E-mail address" autofocus>
</div>
</div>
<div class="field">
<div class="ui input">
<input v-model="password" type="password" name="password" placeholder="Password">
</div>
</div>
<div v-on:click="submit" class="ui teal submit button">Login</div>
</div>
</div>
</template>
<script>
//ajax calls
import axios from 'axios';
import { mapGetters } from 'vuex'
export default {
name: 'Login',
data () {
return {
message: 'Login stuff',
enabled: false,
username: '',
password: ''
}
},
methods: {
validate(){
console.log('Validate this bitch')
},
submit(){
let vm = this
let data = {
username: this.username,
password: this.password
}
axios.post('/api/user/login', data)
.then(response => {
console.log(response)
if(response.data.success){
const token = response.data.token
vm.$store.commit('setLoginToken', token)
//Redirect user to notes section after login
this.$router.push('/notes')
}
})
.catch(error => {
console.log('There was an error with log in request')
})
}
},
computed: {
...mapGetters([
'getRudeMessage'
])
}
}
</script>

View File

@@ -0,0 +1,74 @@
<template>
<div>
<div class="ui icon large label" v-for="tag in tags">
{{ucWords(tag.text)}} <i class="delete icon" v-on:click="removeTag(tag.id)"></i>
</div>
<input v-model="newTagInput" v-on:keyup.enter="addTag" placeholder="Add Tag" />
<div class="ui divider"></div>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'NoteTagEdit',
props: [ 'noteId' ],
data () {
return {
tags: null,
newTagInput: ''
}
},
beforeMount(){
},
mounted() {
this.getTags()
},
methods: {
getTags(){
//Get Note Tags -> /api/tags/get
let vm = this
axios.post('/api/tags/get', {'noteId': this.noteId})
.then(response => {
//Set up local data
vm.tags = response.data
})
},
addTag(event){
//post to -> /api/addtagtonote
let postData = {
'tagText':this.newTagInput,
'noteId':this.noteId
}
let vm = this
axios.post('/api/tags/addtonote', postData)
.then(response => {
vm.newTagInput = ''
vm.getTags()
})
},
removeTag(tagId){
let postData = {
'tagId':tagId,
'noteId':this.noteId
}
let vm = this
axios.post('/api/tags/removefromnote', postData)
.then(response => {
vm.getTags()
})
},
ucWords(str){
return (str + '')
.replace(/^(.)|\s+(.)/g, function ($1) {
return $1.toUpperCase()
})
}
}
}
</script>

View File

@@ -0,0 +1,47 @@
<template>
<div id="NotesPage">
<div class="ui segment">
<search-bar />
</div>
<input-notes v-if="activeNoteId != null" :noteid="activeNoteId"></input-notes>
</div>
</template>
<script>
//ajax calls
import axios from 'axios';
// import { mapGetters } from 'vuex'
export default {
name: 'Notes',
components:{
'input-notes': require('./InputNotes.vue').default,
'search-bar': require('./SearchBar.vue').default
},
data () {
return {
notes: null,
activeNoteId: null
}
},
beforeMount(){
this.$bus.$on('close_active_note', () => {
this.activeNoteId = null
})
this.$bus.$on('open_note', openNoteId => {
this.activeNoteId = openNoteId
})
},
mounted: function() {
//this.getLatest()
},
methods: {
openNote(id){
this.activeNoteId = id
}
}
}
</script>

View File

@@ -0,0 +1,124 @@
<template>
<div>
<div class="ui grid">
<!-- search menu -->
<div class="ui row">
<div @click="createNote" class="ui green button">
<i class="plus icon"></i>
New Note
</div>
<input v-model="searchTerm" @keyup="searchKeyUp" @:keyup.enter="search" placeholder="Search Notes" />
<div class="ui button" @click="reset">Reset</div>
</div>
<div class="ui row">
<div class="ui two wide column">
<div class="ui clickable basic fluid large label" v-for="tag in commonTags" @click="toggleTagFilter(tag.id)"
:class="{ 'green':(searchTags.includes(tag.id)) }">
{{ucWords(tag.text)}} <div class="detail">{{tag.usages}}</div>
</div>
</div>
<div class="ui fourteen wide column">
<div v-if="notes !== null">
<div class="ui vertical segment clickable" v-for="note in notes" v-on:click="openNote(note.id)" v-html="note.text">
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'SearchBar',
data () {
return {
initComponent: true,
commonTags: [],
searchTerm: '',
searchTags: [],
notes: null,
searchDebounce: null
}
},
beforeMount(){
},
mounted() {
this.search()
},
methods: {
toggleTagFilter(tagId){
if(this.searchTags.includes(tagId)){
this.searchTags.splice( this.searchTags.indexOf(tagId) , 1);
} else {
this.searchTags.push(tagId)
}
this.search()
},
search(){
let postData = {
searchQuery: this.searchTerm,
searchTags: this.searchTags
}
//Perform search
let vm = this
axios.post('/api/notes/search', postData).
then(response => {
console.log('Notes and Tags')
console.log(response.data)
vm.commonTags = response.data.tags
vm.notes = response.data.notes
})
},
searchKeyUp(){
let vm = this
clearTimeout(vm.searchDebounce)
vm.searchDebounce = setTimeout(() => {
vm.search()
}, 300)
},
createNote(event){
const title = ''
let vm = this
axios.post('/api/notes/create', {title})
.then(response => {
if(response.data && response.data.id){
vm.openNote(response.data.id)
}
})
},
openNote(noteId){
//Emit open note event
this.$bus.$emit('open_note', noteId)
},
ucWords(str){
return (str + '')
.replace(/^(.)|\s+(.)/g, function ($1) {
return $1.toUpperCase()
})
},
reset(){
this.searchTerm = ''
this.searchTags = []
this.search()
}
}
}
</script>
<style type="text/css" scoped>
.detail {
float: right;
}
</style>