Merge branch 'feature/notes-table'

This commit is contained in:
Hubert Van De Walle 2020-04-25 12:42:41 +02:00
commit 503c80275e
6 changed files with 117 additions and 63 deletions

View File

@ -7,8 +7,6 @@ import io.ktor.jackson.*
fun Application.contentNegotiationFeature() { fun Application.contentNegotiationFeature() {
install(ContentNegotiation) { install(ContentNegotiation) {
jackson { jackson {}
enable(SerializationFeature.INDENT_OUTPUT)
}
} }
} }

View File

@ -8,15 +8,19 @@ import io.ktor.response.*
import io.ktor.routing.* import io.ktor.routing.*
import org.kodein.di.Kodein import org.kodein.di.Kodein
import org.kodein.di.generic.instance import org.kodein.di.generic.instance
import kotlin.system.measureTimeMillis
fun Routing.notes(kodein: Kodein) { fun Routing.notes(kodein: Kodein) {
val notesService by kodein.instance<NotesService>() val notesService by kodein.instance<NotesService>()
authenticate { authenticate {
get("/notes") { get("/notes") {
val time = measureTimeMillis {
val userId = call.userId() val userId = call.userId()
val notes = notesService.getNotes(userId) val notes = notesService.getNotes(userId)
call.respond(notes) call.respond(notes)
} }
application.log.info("Time taken: $time ms")
}
} }
} }

View File

@ -25,19 +25,14 @@ class NotesService(override val kodein: Kodein) : KodeinAware {
.where { Notes.userId eq userId } .where { Notes.userId eq userId }
.orderBy(Notes.updatedAt.desc()) .orderBy(Notes.updatedAt.desc())
.map { row -> .map { row ->
Notes.createEntity(row)
}
.toList()
.map { note ->
val tags = db.from(Tags) val tags = db.from(Tags)
.select(Tags.name) .select(Tags.name)
.where { Tags.noteId eq note.id } .where { Tags.noteId eq row[Notes.id]!! }
.map { it[Tags.name]!! } .map { it[Tags.name]!! }
.toList()
val updatedAt = DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(note.updatedAt) val updatedAt = DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(row[Notes.updatedAt]!!)
NotesDTO(note.title, tags, updatedAt) NotesDTO(row[Notes.title]!!, tags, updatedAt)
} }
fun getNoteIdFromUserIdAndTitle(userId: Int, noteTitle: String): Int? = db.from(Notes) fun getNoteIdFromUserIdAndTitle(userId: Int, noteTitle: String): Int? = db.from(Notes)

View File

@ -2,27 +2,34 @@
<v-card <v-card
class="d-flex flex-column" class="d-flex flex-column"
:class="{ hover: hover }" :class="{ hover: hover }"
d
height="100%" height="100%"
:color="hover ? 'blue lighten-3' : 'white'" :color="hover ? 'blue lighten-4' : ''"
:elevation="hover ? 12 : 2" :elevation="hover ? 12 : 2"
> >
<div class="mb-auto"> <div class="mb-auto">
<v-card-title>{{ title }}</v-card-title> <v-list-item three-line>
<v-list-item-content>
<div class="overline mb-4">
Last updated {{ updatedAt }}
</div>
<v-list-item-title class="headline mb-1">
<h2 class="title primary--text">{{ title }}</h2>
</v-list-item-title>
</v-list-item-content>
</v-list-item>
<v-card-actions>
<div class="tags"> <div class="tags">
<v-chip <v-chip
v-for="tag in tags" v-for="tag in tags"
:key="tag" :key="tag"
active active
color="secondary" color="secondary"
>{{ tag }}</v-chip
> >
{{ tag }}
</v-chip>
</div> </div>
</div> </v-card-actions>
<div v-if="updatedAt" class="mt-auto">
<v-divider />
<v-card-subtitle>Last updated {{ updatedAt }}</v-card-subtitle>
</div> </div>
</v-card> </v-card>
</template> </template>

View File

@ -4,7 +4,7 @@
<v-btn to="/" text rounded>Simple Notes</v-btn> <v-btn to="/" text rounded>Simple Notes</v-btn>
<v-spacer /> <v-spacer />
<v-btn to="/notes" class="mr-2" text rounded>My notes</v-btn> <v-btn to="/notes" class="mr-2" text rounded>My notes</v-btn>
<v-btn v-if="this.$store.state.auth.loggedIn" outline> <v-btn v-if="this.$store.state.auth.loggedIn" outlined>
Welcome {{ this.$store.state.auth.user.username }} Welcome {{ this.$store.state.auth.user.username }}
</v-btn> </v-btn>
<v-btn v-else to="/account" text rounded>Account</v-btn> <v-btn v-else to="/account" text rounded>Account</v-btn>

View File

@ -1,48 +1,98 @@
<template> <template>
<v-container fluid> <v-data-table
<v-row dense align-content="stretch" align="stretch"> :headers="headers"
<v-col :items="notes"
v-for="(note, index) in notes" :loading="loading"
:key="index" loading-text="Loading notes..."
cols="12" class="elevation-1 mt-10"
sm="6" disable-sort
md="4"
xl="3"
class="viewer"
> >
<v-hover v-slot:default="{ hover }"> <template v-slot:top>
<Note <v-toolbar flat color="secondary">
:hover="hover" <h2 class="title white--text">
:title="note.title" Notes
:tags="note.tags" </h2>
:updated-at="format(note.updatedAt)" <v-spacer></v-spacer>
> <v-btn to="/create" outlined dark>
{{ note.content }} New Note
</Note>
</v-hover>
</v-col>
</v-row>
<v-btn to="/create" fab fixed bottom right large color="accent">
<v-icon>mdi-plus</v-icon>
</v-btn> </v-btn>
</v-container> </v-toolbar>
</template>
<template v-slot:item.updatedAt="{ item }">
{{ format(item.updatedAt) }}
</template>
<template v-slot:item.tags="{ item }">
<div class="tags">
<v-chip
v-for="tag in item.tags"
:key="tag"
active
color="secondary"
>
{{ tag }}
</v-chip>
</div>
</template>
<template v-slot:item.actions="{ item }">
<v-icon
small
color="accent"
dark
class="mr-2"
@click="editItem(item)"
>
mdi-pencil
</v-icon>
<v-icon small color="accent" dark @click="deleteItem(item)">
mdi-delete
</v-icon>
</template>
<template v-slot:no-data>
<v-btn color="primary" @click="initialize">Reset</v-btn>
</template>
</v-data-table>
</template> </template>
<script> <script>
import { format } from 'timeago.js' import { format } from 'timeago.js'
import Note from '@/components/Note'
export default { export default {
name: 'Notes', name: 'Notes',
title: 'Notes', title: 'Notes',
components: { Note },
data: () => ({ data: () => ({
loading: true,
notes: [], notes: [],
headers: [
{ text: 'Title', align: 'start', value: 'title' },
{ text: 'Last updated', value: 'updatedAt', align: 'end' },
{ text: 'Tags', value: 'tags' },
{ text: 'Actions', value: 'actions' },
],
}), }),
mounted() { mounted() {
this.$axios.get('/notes').then((e) => (this.notes = e.data)) this.$axios.get('/notes').then((e) => {
this.notes = e.data
this.loading = false
})
},
methods: {
editItem(item) {},
deleteItem(item) {},
format,
}, },
methods: { format },
} }
</script> </script>
<style scoped>
.tags {
padding: 16px;
}
.tags .v-chip {
margin: 4px 8px 4px 0;
}
.v-card.hover {
cursor: pointer;
}
</style>