frontend updating and rendering
This commit is contained in:
parent
f1f3c3dd5b
commit
6ac7ba8fd0
116
entry/entry.go
116
entry/entry.go
|
@ -1,6 +1,9 @@
|
||||||
package entry
|
package entry
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.chrissexton.org/cws/cabinet/db"
|
"code.chrissexton.org/cws/cabinet/db"
|
||||||
|
@ -22,10 +25,10 @@ type Entry struct {
|
||||||
func PrepareTable(tx *sqlx.Tx) error {
|
func PrepareTable(tx *sqlx.Tx) error {
|
||||||
q := `create table if not exists entries (
|
q := `create table if not exists entries (
|
||||||
id integer primary key,
|
id integer primary key,
|
||||||
slug text unique,
|
slug text unique not null,
|
||||||
content text,
|
content text not null,
|
||||||
created datetime,
|
created datetime not null,
|
||||||
updated datetime,
|
updated datetime not null,
|
||||||
author_id integer
|
author_id integer
|
||||||
)`
|
)`
|
||||||
_, err := tx.Exec(q)
|
_, err := tx.Exec(q)
|
||||||
|
@ -35,9 +38,10 @@ func PrepareTable(tx *sqlx.Tx) error {
|
||||||
}
|
}
|
||||||
q = `create table if not exists tags (
|
q = `create table if not exists tags (
|
||||||
id integer primary key,
|
id integer primary key,
|
||||||
name text,
|
name text not null,
|
||||||
entry_id integer,
|
entry_id integer,
|
||||||
foreign key(entry_id) references entries(id)
|
foreign key(entry_id) references entries(id),
|
||||||
|
constraint unique_name_id unique (name, entry_id)
|
||||||
)`
|
)`
|
||||||
_, err = tx.Exec(q)
|
_, err = tx.Exec(q)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -74,12 +78,20 @@ func GetByID(db *db.Database, id int64) (Entry, error) {
|
||||||
return e, e.populateTags()
|
return e, e.populateTags()
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAll(db *db.Database) ([]*Entry, error) {
|
func Search(db *db.Database, query string) ([]*Entry, error) {
|
||||||
entries := []*Entry{}
|
entries := []*Entry{}
|
||||||
q := `select * from entries`
|
if query != "" {
|
||||||
err := db.Select(&entries, q)
|
q := `select * from entries where content like '%?%'`
|
||||||
if err != nil {
|
err := db.Select(&entries, q, query)
|
||||||
return nil, err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
q := `select * from entries`
|
||||||
|
err := db.Select(&entries, q)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
for _, e := range entries {
|
for _, e := range entries {
|
||||||
e.db = db
|
e.db = db
|
||||||
|
@ -117,13 +129,91 @@ func (e *Entry) populateTags() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *Entry) addTag(tag string) error {
|
||||||
|
q := `insert into tags (name,entry_id) values (?,?)`
|
||||||
|
_, err := e.db.Exec(q, tag, e.ID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Entry) removeTag(tag string) error {
|
||||||
|
q := `delete from tags where name=? and entry_id=?`
|
||||||
|
_, err := e.db.Exec(q, tag, e.ID)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Entry) UniqueSlug() string {
|
||||||
|
candidate := strings.Split(e.Content, "\n")[0]
|
||||||
|
candidateNumber := 0
|
||||||
|
|
||||||
|
r := regexp.MustCompile(`[^a-zA-Z0-9 -]`)
|
||||||
|
candidate = r.ReplaceAllString(candidate, "")
|
||||||
|
candidate = strings.TrimSpace(candidate)
|
||||||
|
candidate = strings.ReplaceAll(candidate, " ", "-")
|
||||||
|
if len(candidate) == 0 {
|
||||||
|
candidate = "untitled"
|
||||||
|
}
|
||||||
|
candidate = strings.ToLower(candidate)
|
||||||
|
|
||||||
|
q := `select slug from entries where slug like ?`
|
||||||
|
slugs := []string{}
|
||||||
|
if err := e.db.Select(&slugs, q, candidate+"%"); err != nil {
|
||||||
|
log.Debug().Err(err).Msgf("Could not get candidate slugs: %s", err)
|
||||||
|
return candidate
|
||||||
|
}
|
||||||
|
|
||||||
|
contains := func(s string, ss []string) bool {
|
||||||
|
for _, e := range ss {
|
||||||
|
if s == e {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpCandidate := candidate
|
||||||
|
for contains(tmpCandidate, slugs) {
|
||||||
|
candidateNumber++
|
||||||
|
tmpCandidate = fmt.Sprintf("%s-%d", candidate, candidateNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tmpCandidate
|
||||||
|
}
|
||||||
|
|
||||||
func (e *Entry) Update() error {
|
func (e *Entry) Update() error {
|
||||||
if e.ID == -1 {
|
if e.ID == -1 {
|
||||||
return e.Create()
|
return e.Create()
|
||||||
}
|
}
|
||||||
|
old, err := GetByID(e.db, e.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
e.Slug = e.UniqueSlug()
|
||||||
|
|
||||||
q := `update entries set slug=?, content=?, updated=?, author_id=? where id=?`
|
q := `update entries set slug=?, content=?, updated=?, author_id=? where id=?`
|
||||||
_, err := e.db.Exec(q, e.Slug, e.Content, e.Updated.Unix(), e.AuthorID, e.ID)
|
if _, err = e.db.Exec(q, e.Slug, e.Content, e.Updated.Unix(), e.AuthorID, e.ID); err != nil {
|
||||||
return err
|
return err
|
||||||
|
}
|
||||||
|
for _, t := range e.Tags {
|
||||||
|
if !contains(old.Tags, t) {
|
||||||
|
e.addTag(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, t := range old.Tags {
|
||||||
|
if !contains(e.Tags, t) {
|
||||||
|
e.removeTag(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func contains(items []string, entry string) bool {
|
||||||
|
for _, e := range items {
|
||||||
|
if e == entry {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Entry) Create() error {
|
func (e *Entry) Create() error {
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
"lint": "vue-cli-service lint"
|
"lint": "vue-cli-service lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"asciidoctor": "^2.0.3",
|
||||||
|
"axios": "^0.19.0",
|
||||||
"bootstrap": "^4.3.1",
|
"bootstrap": "^4.3.1",
|
||||||
"bootstrap-vue": "^2.0.4",
|
"bootstrap-vue": "^2.0.4",
|
||||||
"brace": "latest",
|
"brace": "latest",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="app">
|
<div id="app">
|
||||||
<b-navbar type="dark" variant="dark">
|
<b-navbar type="dark" variant="dark">
|
||||||
<b-navbar-brand>Cabinet</b-navbar-brand>
|
<b-navbar-brand>🗄 Cabinet</b-navbar-brand>
|
||||||
<b-navbar-nav>
|
<b-navbar-nav>
|
||||||
<b-nav-item to="/">Home</b-nav-item>
|
<b-nav-item to="/">Home</b-nav-item>
|
||||||
<b-nav-item to="/console">Console</b-nav-item>
|
<b-nav-item to="/console">Console</b-nav-item>
|
||||||
|
@ -44,4 +44,4 @@
|
||||||
Error
|
Error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<editor v-model="text" @init="editorInit" lang="asciidoc" theme="github" width="100%" height="500" />
|
<editor ref="myEditor" v-model="text" @init="editorInit" lang="asciidoc" theme="github" width="100%" height="500" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -20,7 +20,13 @@
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
content: function(newValue) {
|
content: function(newValue) {
|
||||||
|
console.log('text update\n'+newValue)
|
||||||
|
let editor = this.$refs.myEditor.editor
|
||||||
|
editor.renderer.updateFull()
|
||||||
this.text = newValue
|
this.text = newValue
|
||||||
|
},
|
||||||
|
text: function() {
|
||||||
|
this.$emit('change', this.text)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
@ -3,20 +3,13 @@
|
||||||
<b-container fluid>
|
<b-container fluid>
|
||||||
<b-row>
|
<b-row>
|
||||||
<b-col>
|
<b-col>
|
||||||
<Editor :content="content" />
|
<Editor :content="content" @change="updateContent"/>
|
||||||
</b-col>
|
</b-col>
|
||||||
</b-row>
|
</b-row>
|
||||||
<b-row>
|
<b-row>
|
||||||
|
<b-col cols="1"><label style="padding-top: 0.5em" for="tagList">Tags</label></b-col>
|
||||||
<b-col cols="10">
|
<b-col cols="10">
|
||||||
<label for="tagList">Tags</label>
|
<TagList id="tagList" :tags="tags" @change="tagUpdate"/>
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
<b-row>
|
|
||||||
<b-col cols="10">
|
|
||||||
<TagList id="tagList" :tags="tags" />
|
|
||||||
</b-col>
|
|
||||||
<b-col cols="1">
|
|
||||||
<b-button variant="primary">Save</b-button>
|
|
||||||
</b-col>
|
</b-col>
|
||||||
</b-row>
|
</b-row>
|
||||||
</b-container>
|
</b-container>
|
||||||
|
@ -24,8 +17,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Editor from "./Editor";
|
import Editor from "./Editor"
|
||||||
import TagList from "./TagList";
|
import TagList from "./TagList"
|
||||||
|
import _ from 'lodash'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "MainEditor",
|
name: "MainEditor",
|
||||||
props: {
|
props: {
|
||||||
|
@ -35,23 +30,18 @@
|
||||||
if (this.$props.slug) {
|
if (this.$props.slug) {
|
||||||
this.getFile(this.$props.slug)
|
this.getFile(this.$props.slug)
|
||||||
}
|
}
|
||||||
},
|
this.save = _.debounce(this.save, 2000)
|
||||||
data: function () {
|
|
||||||
return {
|
|
||||||
file: ''
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
tags: function() {
|
tags: function () {
|
||||||
if (this.file) {
|
if (this.$store.state.file) {
|
||||||
return this.file.tags
|
return this.$store.state.file.Tags
|
||||||
}
|
}
|
||||||
return []
|
return []
|
||||||
},
|
},
|
||||||
content: function () {
|
content: function () {
|
||||||
console.log('file:' + this.file)
|
if (this.$store.state.file) {
|
||||||
if (this.file) {
|
return this.$store.state.file.Content
|
||||||
return this.file.contents
|
|
||||||
}
|
}
|
||||||
return "= Main Editor"
|
return "= Main Editor"
|
||||||
}
|
}
|
||||||
|
@ -62,10 +52,35 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getFile: function(slug) {
|
getFile: function (slug) {
|
||||||
this.$store.dispatch('getFile', slug)
|
this.$store.dispatch('getFile', slug)
|
||||||
.then(file => {
|
},
|
||||||
this.file = file
|
tagUpdate: function(newTags) {
|
||||||
|
if (JSON.stringify(newTags) === JSON.stringify(this.file.Tags))
|
||||||
|
return
|
||||||
|
this.file.Tags = newTags
|
||||||
|
this.$emit('markDirty', true)
|
||||||
|
this.save()
|
||||||
|
},
|
||||||
|
updateContent: function (newContent) {
|
||||||
|
if (this.$store.state.file.Content === newContent)
|
||||||
|
return
|
||||||
|
this.$store.state.file.Content = newContent
|
||||||
|
this.$emit('markDirty', true)
|
||||||
|
this.save()
|
||||||
|
},
|
||||||
|
save: function () {
|
||||||
|
this.$store.state.file.Content = this.content
|
||||||
|
console.log("Saving file: " + this.file.Slug)
|
||||||
|
this.$store.dispatch('saveFile', this.file)
|
||||||
|
.then(res => {
|
||||||
|
this.$emit('markDirty', false)
|
||||||
|
this.$store.dispatch('updateSearch')
|
||||||
|
if (res.data.Slug != this.$route.params.slug)
|
||||||
|
this.$router.replace({params: { slug: res.data.Slug }})
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.log('err:' + err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -40,14 +40,14 @@
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
tags: function() {
|
tags: function() {
|
||||||
if (this.file) {
|
if (this.$store.state.file) {
|
||||||
return this.file.tags
|
return this.$store.state.file.Tags
|
||||||
}
|
}
|
||||||
return []
|
return []
|
||||||
},
|
},
|
||||||
content: function () {
|
content: function () {
|
||||||
if (this.file) {
|
if (this.$store.state.file) {
|
||||||
return this.file.contents
|
return this.$store.state.file.Content
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
@ -60,9 +60,6 @@
|
||||||
methods: {
|
methods: {
|
||||||
getFile: function(slug) {
|
getFile: function(slug) {
|
||||||
this.$store.dispatch('getFile', slug)
|
this.$store.dispatch('getFile', slug)
|
||||||
.then(file => {
|
|
||||||
this.file = file
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {TagList, Viewer}
|
components: {TagList, Viewer}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
<template>
|
<template>
|
||||||
<b-container fluid>
|
<b-container fluid>
|
||||||
<b-row>
|
<b-row>
|
||||||
<b-input placeholder="Search" @update="runQuery" v-model="query" />
|
<b-input placeholder="Search" @update="runQuery" v-model="queryText" />
|
||||||
</b-row>
|
</b-row>
|
||||||
<b-row v-for="item in results" v-bind:key="item.id">
|
<b-row v-for="item in results" v-bind:key="item.ID">
|
||||||
<b-col>
|
<b-col>
|
||||||
<b-link
|
<b-link
|
||||||
:to="{ name: target, params: { slug: item.slug } }"
|
:to="{ name: target, params: { slug: item.Slug } }"
|
||||||
>{{item.title}}</b-link>
|
>{{item.Slug}}</b-link>
|
||||||
</b-col>
|
</b-col>
|
||||||
</b-row>
|
</b-row>
|
||||||
</b-container>
|
</b-container>
|
||||||
|
@ -19,35 +19,34 @@
|
||||||
name: "SearchResults",
|
name: "SearchResults",
|
||||||
data: function() {
|
data: function() {
|
||||||
return {
|
return {
|
||||||
results: []
|
queryText: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
target: String,
|
target: String,
|
||||||
query: String
|
query: String
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
query: function(newValue) {
|
||||||
|
this.queryText = newValue
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
results: function() {
|
||||||
|
console.log("results:"+this.$store.state.searchResults)
|
||||||
|
return this.$store.state.searchResults
|
||||||
|
}
|
||||||
|
},
|
||||||
created() {
|
created() {
|
||||||
this.getResults()
|
this.getResults()
|
||||||
this.runQuery = _.debounce(this.runQuery, 1000)
|
this.runQuery = _.debounce(this.runQuery, 1000)
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
getResults: function () {
|
getResults: function () {
|
||||||
this.$store.dispatch('getSearchResults')
|
this.$store.dispatch('getSearchResults', null)
|
||||||
.catch(res => {
|
|
||||||
this.$store.commit('addError', res.message);
|
|
||||||
})
|
|
||||||
.then(res => {
|
|
||||||
this.results = res
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
runQuery: function() {
|
runQuery: function() {
|
||||||
this.$store.dispatch('getSearchResults', this.query)
|
this.$store.dispatch('getSearchResults', this.query)
|
||||||
.catch(res => {
|
|
||||||
this.$store.commit('addError', res.message);
|
|
||||||
})
|
|
||||||
.then(res => {
|
|
||||||
this.results = res
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<b-input type="text" placeholder="tags" :value="tagString" :hidden="readOnly"/>
|
<b-input type="text" placeholder="tags" :value="tagString" @update="changed" :hidden="readOnly"/>
|
||||||
<div :hidden="!readOnly">
|
<div :hidden="!readOnly">
|
||||||
<b-badge pill v-for="(tag, idx) in tags" v-bind:key="idx" class="tag">
|
<b-badge pill v-for="(tag, idx) in tags" v-bind:key="idx" class="tag">
|
||||||
{{tag}}
|
{{tag}}
|
||||||
|
@ -12,20 +12,36 @@
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: "TagList",
|
name: "TagList",
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
internalTags: []
|
||||||
|
}
|
||||||
|
},
|
||||||
props: {
|
props: {
|
||||||
tags: Array,
|
tags: Array,
|
||||||
readOnly: Boolean
|
readOnly: Boolean
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
tags: function (newValue) {
|
||||||
|
this.internalTags = newValue
|
||||||
|
}
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
tagString: function() {
|
tagString: function () {
|
||||||
return this.tags.join(' ')
|
return this.internalTags.join(',')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
changed: function (value) {
|
||||||
|
this.internalTags = value.split(',')
|
||||||
|
this.$emit('change', this.internalTags)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.tag {
|
.tag {
|
||||||
margin-right: 0.5em;
|
margin-right: 0.5em;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
|
@ -1,16 +1,26 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div v-html="adoc">
|
||||||
<pre>
|
|
||||||
{{content}}
|
|
||||||
</pre>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
let asciidoctor = require('asciidoctor')()
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Viewer",
|
name: "Viewer",
|
||||||
props: {
|
props: {
|
||||||
content: String
|
content: String
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
adoc: function () {
|
||||||
|
if (!this.content)
|
||||||
|
return ''
|
||||||
|
let doc = asciidoctor.convert(this.content, {
|
||||||
|
'safe': 'safe',
|
||||||
|
'attributes': {'showtitle': true, 'icons': 'font'}
|
||||||
|
})
|
||||||
|
return doc
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -6,11 +6,6 @@ import Console from '../views/Console.vue'
|
||||||
Vue.use(VueRouter)
|
Vue.use(VueRouter)
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
|
||||||
path: '/',
|
|
||||||
name: 'home',
|
|
||||||
component: Home
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: '/view/:slug',
|
path: '/view/:slug',
|
||||||
name: 'home-slug',
|
name: 'home-slug',
|
||||||
|
@ -43,6 +38,11 @@ const routes = [
|
||||||
// this generates a separate chunk (about.[hash].js) for this route
|
// this generates a separate chunk (about.[hash].js) for this route
|
||||||
// which is lazy-loaded when the route is visited.
|
// which is lazy-loaded when the route is visited.
|
||||||
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
|
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
name: 'home',
|
||||||
|
component: Home
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -1,25 +1,29 @@
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import Vuex from 'vuex'
|
import Vuex from 'vuex'
|
||||||
import _ from 'lodash'
|
// import _ from 'lodash'
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
Vue.use(Vuex)
|
Vue.use(Vuex)
|
||||||
|
|
||||||
function getRandomInt(min, max) {
|
// function getRandomInt(min, max) {
|
||||||
min = Math.ceil(min)
|
// min = Math.ceil(min)
|
||||||
max = Math.floor(max)
|
// max = Math.floor(max)
|
||||||
return Math.floor(Math.random() * (max - min + 1)) + min
|
// return Math.floor(Math.random() * (max - min + 1)) + min
|
||||||
}
|
// }
|
||||||
|
|
||||||
var files = {
|
// var files = {
|
||||||
'test-1': {id: 0, title: 'test 1', contents: '= test 1', slug: 'test-1', tags: ['a']},
|
// 'test-1': {id: 0, title: 'test 1', contents: '= test 1', slug: 'test-1', tags: ['a']},
|
||||||
'test-2': {id: 1, title: 'test 2', contents: '= test 2', slug: 'test-2', tags: ['b']},
|
// 'test-2': {id: 1, title: 'test 2', contents: '= test 2', slug: 'test-2', tags: ['b']},
|
||||||
'test-3': {id: 2, title: 'test 3', contents: '= test 3', slug: 'test-3', tags: ['a','b']},
|
// 'test-3': {id: 2, title: 'test 3', contents: '= test 3', slug: 'test-3', tags: ['a','b']},
|
||||||
'test-4': {id: 3, title: 'test 4', contents: '= test 4', slug: 'test-4', tags: ['c']}
|
// 'test-4': {id: 3, title: 'test 4', contents: '= test 4', slug: 'test-4', tags: ['c']}
|
||||||
}
|
// }
|
||||||
|
|
||||||
export default new Vuex.Store({
|
export default new Vuex.Store({
|
||||||
state: {
|
state: {
|
||||||
errs: []
|
errs: [],
|
||||||
|
searchResults: [],
|
||||||
|
query: null,
|
||||||
|
file: null
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
clearError(state) {
|
clearError(state) {
|
||||||
|
@ -27,25 +31,49 @@ export default new Vuex.Store({
|
||||||
},
|
},
|
||||||
addError(state, err) {
|
addError(state, err) {
|
||||||
state.errs.push(err)
|
state.errs.push(err)
|
||||||
|
},
|
||||||
|
setResults(state, results) {
|
||||||
|
state.searchResults = results
|
||||||
|
},
|
||||||
|
setQuery(state, query) {
|
||||||
|
state.query = query
|
||||||
|
},
|
||||||
|
setFile(state, file) {
|
||||||
|
state.file = file
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
getFile: function(_, slug) {
|
getFile: function({ commit }, slug) {
|
||||||
console.log('getFile('+slug+')')
|
if (slug)
|
||||||
return new Promise(function (resolve) {
|
return axios.get('/v1/entries/'+slug)
|
||||||
setTimeout(() => resolve(files[slug]), getRandomInt(0, 1000))
|
.catch(err => commit('addError', err))
|
||||||
})
|
.then(res => {
|
||||||
|
commit('setFile', res.data)
|
||||||
|
})
|
||||||
},
|
},
|
||||||
getSearchResults: function (state, query) {
|
getSearchResults: function ({dispatch, commit}, query) {
|
||||||
console.log('getSearchResults: '+query)
|
commit('setQuery', query)
|
||||||
let values = Object.values(files)
|
dispatch('updateSearch')
|
||||||
if (query)
|
},
|
||||||
values = _.filter(values, (o) => {
|
updateSearch: function ({commit, state}) {
|
||||||
return o.title.indexOf(query) != -1
|
let query = state.query
|
||||||
|
if (query) {
|
||||||
|
axios.get('/v1/entries?query='+query)
|
||||||
|
.catch(err => state.addError(err))
|
||||||
|
.then(res =>{
|
||||||
|
console.log("getSearchResults:"+res.data)
|
||||||
|
commit('setResults', res.data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
axios.get('/v1/entries')
|
||||||
|
.catch(err => state.addError(err))
|
||||||
|
.then(res =>{
|
||||||
|
console.log("getSearchResults:"+res.data)
|
||||||
|
commit('setResults', res.data)
|
||||||
})
|
})
|
||||||
return new Promise(function (resolve) {
|
},
|
||||||
setTimeout(() => resolve(values, getRandomInt(0, 1000)))
|
saveFile: function(state, file) {
|
||||||
})
|
return axios.put('/v1/entries/'+file.Slug, file)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
modules: {
|
modules: {
|
||||||
|
|
|
@ -1,22 +1,31 @@
|
||||||
<template>
|
<template>
|
||||||
<b-container fluid>
|
<b-container fluid>
|
||||||
<b-row>
|
|
||||||
<b-col>
|
|
||||||
<h1>Console</h1>
|
|
||||||
</b-col>
|
|
||||||
</b-row>
|
|
||||||
<b-row>
|
<b-row>
|
||||||
<b-col md="5">
|
<b-col md="5">
|
||||||
<h2>Scratchpad</h2>
|
<h2>Scratchpad</h2>
|
||||||
<ScratchPad />
|
<ScratchPad />
|
||||||
</b-col>
|
</b-col>
|
||||||
<b-col md="5">
|
<b-col md="5">
|
||||||
<h2>Editor</h2>
|
|
||||||
<MainEditor :slug="$route.params.slug" />
|
<div>
|
||||||
|
<b-tabs content-class="mt-3">
|
||||||
|
<b-tab active>
|
||||||
|
<template v-slot:title>
|
||||||
|
Editor <span v-bind:class="{ dirty: isDirty, clean: !isDirty }">•</span>
|
||||||
|
</template>
|
||||||
|
<MainEditor :slug="$route.params.slug" @markDirty="markDirty"/>
|
||||||
|
</b-tab>
|
||||||
|
<b-tab title="Viewer">
|
||||||
|
<MainView :slug="$route.params.slug" />
|
||||||
|
</b-tab>
|
||||||
|
</b-tabs>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</b-col>
|
</b-col>
|
||||||
<b-col md="2">
|
<b-col md="2">
|
||||||
<h2>Search Results</h2>
|
<h2>Search Results</h2>
|
||||||
<SearchResults target="console-slug" />
|
<SearchResults target="console-slug" :query="query"/>
|
||||||
</b-col>
|
</b-col>
|
||||||
</b-row>
|
</b-row>
|
||||||
</b-container>
|
</b-container>
|
||||||
|
@ -25,6 +34,7 @@
|
||||||
<script>
|
<script>
|
||||||
// @ is an alias to /src
|
// @ is an alias to /src
|
||||||
import MainEditor from "../components/MainEditor";
|
import MainEditor from "../components/MainEditor";
|
||||||
|
import MainView from "../components/MainView";
|
||||||
import ScratchPad from "../components/ScratchPad";
|
import ScratchPad from "../components/ScratchPad";
|
||||||
import SearchResults from "../components/Search";
|
import SearchResults from "../components/Search";
|
||||||
|
|
||||||
|
@ -33,7 +43,54 @@ export default {
|
||||||
components: {
|
components: {
|
||||||
SearchResults,
|
SearchResults,
|
||||||
ScratchPad,
|
ScratchPad,
|
||||||
MainEditor
|
MainEditor,
|
||||||
|
MainView
|
||||||
|
},
|
||||||
|
data: function() {
|
||||||
|
return {
|
||||||
|
isDirty: false,
|
||||||
|
query: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
provide: {
|
||||||
|
update: function() {}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
markDirty: function(dirty) {
|
||||||
|
console.log('markDirty:'+dirty)
|
||||||
|
this.isDirty = dirty
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeRouteEnter (to, from, next) {
|
||||||
|
// called before the route that renders this component is confirmed.
|
||||||
|
// does NOT have access to `this` component instance,
|
||||||
|
// because it has not been created yet when this guard is called!
|
||||||
|
console.log('beforeRouteEnter'+to+from)
|
||||||
|
next()
|
||||||
|
},
|
||||||
|
beforeRouteUpdate (to, from, next) {
|
||||||
|
// called when the route that renders this component has changed,
|
||||||
|
// but this component is reused in the new route.
|
||||||
|
// For example, for a route with dynamic params `/foo/:id`, when we
|
||||||
|
// navigate between `/foo/1` and `/foo/2`, the same `Foo` component instance
|
||||||
|
// will be reused, and this hook will be called when that happens.
|
||||||
|
// has access to `this` component instance.
|
||||||
|
if (this.isDirty) {
|
||||||
|
const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
|
||||||
|
if (answer) {
|
||||||
|
next()
|
||||||
|
} else {
|
||||||
|
next(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next()
|
||||||
|
},
|
||||||
|
beforeRouteLeave (to, from, next) {
|
||||||
|
// called when the route that renders this component is about to
|
||||||
|
// be navigated away from.
|
||||||
|
// has access to `this` component instance.
|
||||||
|
console.log('beforeRouteLeave'+to+from)
|
||||||
|
next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -42,4 +99,10 @@ export default {
|
||||||
h2 {
|
h2 {
|
||||||
font-size: large;
|
font-size: large;
|
||||||
}
|
}
|
||||||
|
.dirty {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
.clean {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -2,6 +2,21 @@
|
||||||
# yarn lockfile v1
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
"@asciidoctor/cli@2.0.1":
|
||||||
|
version "2.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@asciidoctor/cli/-/cli-2.0.1.tgz#5e0154ff06da21aedb1840bff5d49ca6ee7d520d"
|
||||||
|
integrity sha512-XNqnHPMVTRMDlKk6EEiA61P0WImHqOecoky1p8HIek+0u1jbKbiDku8WraC2EaNy8IWyEjfrdnrDYGvogBVqzA==
|
||||||
|
dependencies:
|
||||||
|
yargs "13.2.2"
|
||||||
|
|
||||||
|
"@asciidoctor/core@2.0.3":
|
||||||
|
version "2.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@asciidoctor/core/-/core-2.0.3.tgz#95630424eb2e346acc51dede1e6599159fbd5d44"
|
||||||
|
integrity sha512-iN0zV/tsL36nTltJyk5IdcRqDW34HHiwb9PrgSoTGDCIEML6C3HYnJOQi+jKq7A//Gt1nL1SHohaCHkyw3swEg==
|
||||||
|
dependencies:
|
||||||
|
asciidoctor-opal-runtime "0.3.0"
|
||||||
|
unxhr "1.0.1"
|
||||||
|
|
||||||
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.5.5":
|
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.5.5":
|
||||||
version "7.5.5"
|
version "7.5.5"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d"
|
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d"
|
||||||
|
@ -1385,6 +1400,22 @@ array-unique@^0.3.2:
|
||||||
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
|
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
|
||||||
integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
|
integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
|
||||||
|
|
||||||
|
asciidoctor-opal-runtime@0.3.0:
|
||||||
|
version "0.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/asciidoctor-opal-runtime/-/asciidoctor-opal-runtime-0.3.0.tgz#df327a870ddd3cd5eb0e162d64ed4dcdd3fe3fee"
|
||||||
|
integrity sha512-YapVwl2qbbs6sIe1dvAlMpBzQksFVTSa2HOduOKFNhZlE9bNmn+moDgGVvjWPbzMPo/g8gItyTHfWB2u7bQxag==
|
||||||
|
dependencies:
|
||||||
|
glob "7.1.3"
|
||||||
|
unxhr "1.0.1"
|
||||||
|
|
||||||
|
asciidoctor@^2.0.3:
|
||||||
|
version "2.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/asciidoctor/-/asciidoctor-2.0.3.tgz#350a41d051c5f99d1d8fa4abdc7a9c80da48f90a"
|
||||||
|
integrity sha512-6UB/oB0jRQOy6/DoFBH1nUzAAGt7Aa6snfXKZyvaxKxQHcuvFuJcvS2zVjAH2eURQidkBLy+J0VHBVjRWs8CiQ==
|
||||||
|
dependencies:
|
||||||
|
"@asciidoctor/cli" "2.0.1"
|
||||||
|
"@asciidoctor/core" "2.0.3"
|
||||||
|
|
||||||
asn1.js@^4.0.0:
|
asn1.js@^4.0.0:
|
||||||
version "4.10.1"
|
version "4.10.1"
|
||||||
resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0"
|
resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0"
|
||||||
|
@ -1474,6 +1505,14 @@ aws4@^1.8.0:
|
||||||
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f"
|
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f"
|
||||||
integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==
|
integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==
|
||||||
|
|
||||||
|
axios@^0.19.0:
|
||||||
|
version "0.19.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.0.tgz#8e09bff3d9122e133f7b8101c8fbdd00ed3d2ab8"
|
||||||
|
integrity sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==
|
||||||
|
dependencies:
|
||||||
|
follow-redirects "1.5.10"
|
||||||
|
is-buffer "^2.0.2"
|
||||||
|
|
||||||
babel-eslint@^10.0.3:
|
babel-eslint@^10.0.3:
|
||||||
version "10.0.3"
|
version "10.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.3.tgz#81a2c669be0f205e19462fed2482d33e4687a88a"
|
resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.3.tgz#81a2c669be0f205e19462fed2482d33e4687a88a"
|
||||||
|
@ -2609,6 +2648,13 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
ms "2.0.0"
|
ms "2.0.0"
|
||||||
|
|
||||||
|
debug@=3.1.0:
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
|
||||||
|
integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
|
||||||
|
dependencies:
|
||||||
|
ms "2.0.0"
|
||||||
|
|
||||||
debug@^3.0.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6:
|
debug@^3.0.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6:
|
||||||
version "3.2.6"
|
version "3.2.6"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
|
||||||
|
@ -3517,6 +3563,13 @@ flush-write-stream@^1.0.0:
|
||||||
inherits "^2.0.3"
|
inherits "^2.0.3"
|
||||||
readable-stream "^2.3.6"
|
readable-stream "^2.3.6"
|
||||||
|
|
||||||
|
follow-redirects@1.5.10:
|
||||||
|
version "1.5.10"
|
||||||
|
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a"
|
||||||
|
integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==
|
||||||
|
dependencies:
|
||||||
|
debug "=3.1.0"
|
||||||
|
|
||||||
follow-redirects@^1.0.0:
|
follow-redirects@^1.0.0:
|
||||||
version "1.9.0"
|
version "1.9.0"
|
||||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.9.0.tgz#8d5bcdc65b7108fe1508649c79c12d732dcedb4f"
|
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.9.0.tgz#8d5bcdc65b7108fe1508649c79c12d732dcedb4f"
|
||||||
|
@ -3692,6 +3745,18 @@ glob-to-regexp@^0.3.0:
|
||||||
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
|
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
|
||||||
integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=
|
integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=
|
||||||
|
|
||||||
|
glob@7.1.3:
|
||||||
|
version "7.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
|
||||||
|
integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==
|
||||||
|
dependencies:
|
||||||
|
fs.realpath "^1.0.0"
|
||||||
|
inflight "^1.0.4"
|
||||||
|
inherits "2"
|
||||||
|
minimatch "^3.0.4"
|
||||||
|
once "^1.3.0"
|
||||||
|
path-is-absolute "^1.0.0"
|
||||||
|
|
||||||
glob@^7.0.3, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4:
|
glob@^7.0.3, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4:
|
||||||
version "7.1.5"
|
version "7.1.5"
|
||||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.5.tgz#6714c69bee20f3c3e64c4dd905553e532b40cdc0"
|
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.5.tgz#6714c69bee20f3c3e64c4dd905553e532b40cdc0"
|
||||||
|
@ -4282,6 +4347,11 @@ is-buffer@^1.1.5:
|
||||||
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
|
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
|
||||||
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
|
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
|
||||||
|
|
||||||
|
is-buffer@^2.0.2:
|
||||||
|
version "2.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623"
|
||||||
|
integrity sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==
|
||||||
|
|
||||||
is-callable@^1.1.4:
|
is-callable@^1.1.4:
|
||||||
version "1.1.4"
|
version "1.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75"
|
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75"
|
||||||
|
@ -5590,7 +5660,7 @@ os-homedir@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
|
resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
|
||||||
integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M=
|
integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M=
|
||||||
|
|
||||||
os-locale@^3.0.0:
|
os-locale@^3.0.0, os-locale@^3.1.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a"
|
resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-3.1.0.tgz#a802a6ee17f24c10483ab9935719cef4ed16bf1a"
|
||||||
integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==
|
integrity sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==
|
||||||
|
@ -7753,6 +7823,11 @@ unset-value@^1.0.0:
|
||||||
has-value "^0.3.1"
|
has-value "^0.3.1"
|
||||||
isobject "^3.0.0"
|
isobject "^3.0.0"
|
||||||
|
|
||||||
|
unxhr@1.0.1:
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/unxhr/-/unxhr-1.0.1.tgz#92200322d66c728993de771f9e01eeb21f41bc7b"
|
||||||
|
integrity sha512-MAhukhVHyaLGDjyDYhy8gVjWJyhTECCdNsLwlMoGFoNJ3o79fpQhtQuzmAE4IxCMDwraF4cW8ZjpAV0m9CRQbg==
|
||||||
|
|
||||||
upath@^1.1.1:
|
upath@^1.1.1:
|
||||||
version "1.2.0"
|
version "1.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894"
|
resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894"
|
||||||
|
@ -8228,7 +8303,7 @@ yargs-parser@^11.1.1:
|
||||||
camelcase "^5.0.0"
|
camelcase "^5.0.0"
|
||||||
decamelize "^1.2.0"
|
decamelize "^1.2.0"
|
||||||
|
|
||||||
yargs-parser@^13.1.1:
|
yargs-parser@^13.0.0, yargs-parser@^13.1.1:
|
||||||
version "13.1.1"
|
version "13.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0"
|
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0"
|
||||||
integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==
|
integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==
|
||||||
|
@ -8254,6 +8329,23 @@ yargs@12.0.5:
|
||||||
y18n "^3.2.1 || ^4.0.0"
|
y18n "^3.2.1 || ^4.0.0"
|
||||||
yargs-parser "^11.1.1"
|
yargs-parser "^11.1.1"
|
||||||
|
|
||||||
|
yargs@13.2.2:
|
||||||
|
version "13.2.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.2.2.tgz#0c101f580ae95cea7f39d927e7770e3fdc97f993"
|
||||||
|
integrity sha512-WyEoxgyTD3w5XRpAQNYUB9ycVH/PQrToaTXdYXRdOXvEy1l19br+VJsc0vcO8PTGg5ro/l/GY7F/JMEBmI0BxA==
|
||||||
|
dependencies:
|
||||||
|
cliui "^4.0.0"
|
||||||
|
find-up "^3.0.0"
|
||||||
|
get-caller-file "^2.0.1"
|
||||||
|
os-locale "^3.1.0"
|
||||||
|
require-directory "^2.1.1"
|
||||||
|
require-main-filename "^2.0.0"
|
||||||
|
set-blocking "^2.0.0"
|
||||||
|
string-width "^3.0.0"
|
||||||
|
which-module "^2.0.0"
|
||||||
|
y18n "^4.0.0"
|
||||||
|
yargs-parser "^13.0.0"
|
||||||
|
|
||||||
yargs@^13.0.0:
|
yargs@^13.0.0:
|
||||||
version "13.3.0"
|
version "13.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83"
|
resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.0.tgz#4c657a55e07e5f2cf947f8a366567c04a0dedc83"
|
||||||
|
|
|
@ -9,18 +9,18 @@
|
||||||
* Vue Frontend
|
* Vue Frontend
|
||||||
** [ ] spend some time learning about TypeScript/Vue.js style
|
** [ ] spend some time learning about TypeScript/Vue.js style
|
||||||
** Documents
|
** Documents
|
||||||
*** [ ] adoc editor widget
|
*** [x] adoc editor widget
|
||||||
** Authentication
|
** Authentication
|
||||||
*** [ ] some kind of user auth
|
*** [ ] some kind of user auth
|
||||||
** Views
|
** Views
|
||||||
*** [ ] editor view
|
*** [ ] editor view
|
||||||
*** [ ] public index/search view
|
*** [ ] public index/search view
|
||||||
* Backend
|
* Backend
|
||||||
** [ ] save endpoint
|
** [?] save endpoint
|
||||||
*** [ ] need to generate a slug for entries
|
*** [ ] need to generate a slug for entries
|
||||||
*** [ ] add authentication/authorization
|
*** [ ] add authentication/authorization
|
||||||
*** [ ] convert document to adoc (give format?)
|
*** [ ] convert document to adoc (give format?)
|
||||||
*** [ ] test in frontend
|
*** [x] test in frontend
|
||||||
*** [ ] check for unique tags
|
*** [ ] check for unique tags
|
||||||
*** [ ] set some db fields not null
|
*** [ ] set some db fields not null
|
||||||
** [ ] search endpoint
|
** [ ] search endpoint
|
||||||
|
|
19
web/entry.go
19
web/entry.go
|
@ -4,6 +4,9 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
|
||||||
"code.chrissexton.org/cws/cabinet/entry"
|
"code.chrissexton.org/cws/cabinet/entry"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
@ -28,19 +31,25 @@ func (web *Web) editEntry(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
newEntry.ID = oldEntry.ID
|
oldEntry.Content = newEntry.Content
|
||||||
err = newEntry.Update()
|
oldEntry.Tags = newEntry.Tags
|
||||||
|
oldEntry.Updated = time.Now()
|
||||||
|
|
||||||
|
err = oldEntry.Update()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
fmt.Fprint(w, err)
|
fmt.Fprint(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
resp, err := json.Marshal(newEntry)
|
resp, err := json.Marshal(oldEntry)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
fmt.Fprint(w, err)
|
fmt.Fprint(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Debug().Interface("oldEntry", oldEntry).Msg("Got a PUT")
|
||||||
|
|
||||||
w.Header().Set("content-type", "application/json")
|
w.Header().Set("content-type", "application/json")
|
||||||
fmt.Fprint(w, string(resp))
|
fmt.Fprint(w, string(resp))
|
||||||
}
|
}
|
||||||
|
@ -72,7 +81,9 @@ func (web *Web) newEntry(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (web *Web) allEntries(w http.ResponseWriter, r *http.Request) {
|
func (web *Web) allEntries(w http.ResponseWriter, r *http.Request) {
|
||||||
entries, err := entry.GetAll(web.db)
|
r.ParseForm()
|
||||||
|
query := r.Form.Get("query")
|
||||||
|
entries, err := entry.Search(web.db, query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
fmt.Fprint(w, err)
|
fmt.Fprint(w, err)
|
||||||
|
|
Loading…
Reference in New Issue