Add an initial Events Timeline for Stories

This doesn't cover all the possible events yet, but is a sufficient
starting point.
This commit is contained in:
Adam Coldrick 2020-05-06 01:36:48 +01:00
parent 2eb447ccb1
commit fa0bee29d1
9 changed files with 471 additions and 4 deletions

@ -0,0 +1,45 @@
<template>
<div class="event-details">
<p class="event-title">
<a href="">{{ author.full_name }}</a>
commented on {{ createdDate.toDateString() }}
at {{ createdDate.toLocaleTimeString() }}
</p>
<div class="event-body">
{{ event.comment.content }}
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'EventComment',
data () {
return {
author: {}
}
},
props: ['event'],
computed: {
createdDate () {
return new Date(this.event.created_at)
}
},
created () {
this.getAuthor(this.event.author_id)
},
methods: {
async getAuthor (authorId) {
const baseUrl = 'http://localhost:8080/v1'
const { data: author } = await axios.get(`${baseUrl}/users/${authorId}`)
this.author = author
}
}
}
</script>
<style lang="scss" scoped>
</style>

@ -0,0 +1,42 @@
<template>
<div class="event-details">
<p class="event-title">
<a href="">{{ author.full_name }}</a>
created this story on {{ createdDate.toDateString() }}
at {{ createdDate.toLocaleTimeString() }}
</p>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'EventStoryCreated',
data () {
return {
author: {}
}
},
props: ['event'],
computed: {
createdDate () {
return new Date(this.event.created_at)
}
},
created () {
this.getAuthor(this.event.author_id)
},
methods: {
async getAuthor (authorId) {
const baseUrl = 'http://localhost:8080/v1'
const { data: author } = await axios.get(`${baseUrl}/users/${authorId}`)
this.author = author
}
}
}
</script>
<style lang="scss" scoped>
</style>

@ -0,0 +1,42 @@
<template>
<div class="event-details">
<p class="event-title">
<a href="">{{ author.full_name }}</a>
updated this story on {{ createdDate.toDateString() }}
at {{ createdDate.toLocaleTimeString() }}
</p>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'EventStoryUpdated',
data () {
return {
author: {}
}
},
props: ['event'],
computed: {
createdDate () {
return new Date(this.event.created_at)
}
},
created () {
this.getAuthor(this.event.author_id)
},
methods: {
async getAuthor (authorId) {
const baseUrl = 'http://localhost:8080/v1'
const { data: author } = await axios.get(`${baseUrl}/users/${authorId}`)
this.author = author
}
}
}
</script>
<style lang="scss" scoped>
</style>

@ -0,0 +1,53 @@
<template>
<div class="event-details">
<p class="event-title">
<a href="">{{ author.full_name }}</a>
tagged this story as
<span class="tag" v-for="tag in info.tags" :key="tag">{{ tag }}</span>
on {{ createdDate.toDateString() }}
at {{ createdDate.toLocaleTimeString() }}
</p>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'EventTagsAdded',
data () {
return {
author: {}
}
},
props: ['event'],
computed: {
createdDate () {
return new Date(this.event.created_at)
},
info () {
return JSON.parse(this.event.event_info)
}
},
created () {
this.getAuthor(this.event.author_id)
},
methods: {
async getAuthor (authorId) {
const baseUrl = 'http://localhost:8080/v1'
const { data: author } = await axios.get(`${baseUrl}/users/${authorId}`)
this.author = author
}
}
}
</script>
<style lang="scss" scoped>
.tag {
padding: 6px 12px;
margin: 0 5px;
background-color: #f0ad4e;
color: #f7f6f4;
border-radius: 20px;
}
</style>

@ -0,0 +1,48 @@
<template>
<div class="event-details">
<p class="event-title">
<a href="">{{ author.full_name }}</a>
updated the asssignee of the task "{{ info.task_title }}"
from <a href="" v-if="info.old_assignee_id">{{ info.old_assignee_fullname }}</a> <span v-if="!info.old_assignee_id">{{ info.old_assignee_fullname }}</span>
to <a href="">{{ info.new_assignee_fullname }}</a>
on {{ createdDate.toDateString() }}
at {{ createdDate.toLocaleTimeString() }}
</p>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'EventTaskAssigneeUpdated',
data () {
return {
author: {}
}
},
props: ['event'],
computed: {
createdDate () {
return new Date(this.event.created_at)
},
info () {
return JSON.parse(this.event.event_info)
}
},
created () {
this.getAuthor(this.event.author_id)
},
methods: {
async getAuthor (authorId) {
const baseUrl = 'http://localhost:8080/v1'
const { data: author } = await axios.get(`${baseUrl}/users/${authorId}`)
this.author = author
}
}
}
</script>
<style lang="scss" scoped>
</style>

@ -0,0 +1,46 @@
<template>
<div class="event-details">
<p class="event-title">
<a href="">{{ author.full_name }}</a>
created the task "{{ info.task_title }}"
on {{ createdDate.toDateString() }}
at {{ createdDate.toLocaleTimeString() }}
</p>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'EventTaskCreated',
data () {
return {
author: {}
}
},
props: ['event'],
computed: {
createdDate () {
return new Date(this.event.created_at)
},
info () {
return JSON.parse(this.event.event_info)
}
},
created () {
this.getAuthor(this.event.author_id)
},
methods: {
async getAuthor (authorId) {
const baseUrl = 'http://localhost:8080/v1'
const { data: author } = await axios.get(`${baseUrl}/users/${authorId}`)
this.author = author
}
}
}
</script>
<style lang="scss" scoped>
</style>

@ -0,0 +1,46 @@
<template>
<div class="event-details">
<p class="event-title">
<a href="">{{ author.full_name }}</a>
updated the task "{{ info.task_title }}"
on {{ createdDate.toDateString() }}
at {{ createdDate.toLocaleTimeString() }}
</p>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'EventTaskUpdated',
data () {
return {
author: {}
}
},
props: ['event'],
computed: {
createdDate () {
return new Date(this.event.created_at)
},
info () {
return JSON.parse(this.event.event_info)
}
},
created () {
this.getAuthor(this.event.author_id)
},
methods: {
async getAuthor (authorId) {
const baseUrl = 'http://localhost:8080/v1'
const { data: author } = await axios.get(`${baseUrl}/users/${authorId}`)
this.author = author
}
}
}
</script>
<style lang="scss" scoped>
</style>

@ -0,0 +1,129 @@
<template>
<div class="timeline-event">
<p class="timeline-event-icon" :class="iconClass">
x
</p>
<EventStoryCreated :event="event" v-if="event.event_type === 'story_created'" />
<EventStoryUpdated :event="event" v-if="event.event_type === 'story_details_changed'" />
<EventTagsAdded :event="event" v-if="event.event_type === 'tags_added'" />
<EventTaskCreated :event="event" v-if="event.event_type === 'task_created'" />
<EventTaskUpdated :event="event" v-if="event.event_type === 'task_details_changed'" />
<EventTaskAssigneeUpdated :event="event" v-if="event.event_type === 'task_assignee_changed'" />
<EventComment :event="event" v-if="isComment" />
</div>
</template>
<script>
import axios from 'axios'
import EventStoryCreated from '@/components/EventStoryCreated'
import EventStoryUpdated from '@/components/EventStoryUpdated'
import EventTagsAdded from '@/components/EventTagsAdded'
import EventTaskCreated from '@/components/EventTaskCreated'
import EventTaskUpdated from '@/components/EventTaskUpdated'
import EventTaskAssigneeUpdated from '@/components/EventTaskAssigneeUpdated'
import EventComment from '@/components/EventComment'
const PRIMARY_EVENTS = [
'story_created',
'user_comment'
]
export default {
name: 'TimelineEvent',
data () {
return {
author: {}
}
},
props: ['event'],
components: {
EventComment,
EventStoryCreated,
EventStoryUpdated,
EventTagsAdded,
EventTaskCreated,
EventTaskUpdated,
EventTaskAssigneeUpdated
},
computed: {
createdDate () {
return new Date(this.event.created_at)
},
isComment () {
return this.event.event_type === 'user_comment'
},
iconClass () {
return {
primary: PRIMARY_EVENTS.includes(this.event.event_type)
}
}
},
created () {
this.getAuthor(this.event.author_id)
},
methods: {
async getAuthor (authorId) {
const baseUrl = 'http://localhost:8080/v1'
const { data: author } = await axios.get(`${baseUrl}/users/${authorId}`)
this.author = author
}
}
}
</script>
<style lang="scss" scoped>
.timeline-event {
display: flex;
align-items: flex-start;
margin-top: 20px;
.timeline-event-icon {
flex: 0 0 30px;
margin: 3px 5px;
background-color: #f7f6f4;
width: 30px;
height: 30px;
text-align: center;
line-height: 30px;
border-radius: 50px;
border: 3px solid #ddd;
&.primary {
flex-basis: 40px;
margin: 0;
width: 40px;
height: 40px;
line-height: 40px;
background-color: #c43422;
color: #f7f6f4;
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.12);
}
}
.event-details {
margin-left: 50px;
flex: 1 0 0;
::v-deep p {
margin: 0;
}
::v-deep .event-title {
padding: 5px 10px;
line-height: 30px;
background-color: #eee;
border: 1px solid #ddd;
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.12);
z-index: 10;
position: relative;
}
::v-deep .event-body {
background-color: #fff;
margin: 0 2px;
padding: 20px;
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.12);
}
}
}
</style>

@ -43,21 +43,21 @@
</div>
<h2>Events Timeline and Comments</h2>
<div class="events">
<div class="timeline-event" v-for="event in events" :key="event.id">
{{ event.event_type }}
</div>
<TimelineEvent v-for="event in events" :key="event.id" :event="event"/>
</div>
</div>
</template>
<script>
import TaskListEntry from '@/components/TaskListEntry.vue'
import TimelineEvent from '@/components/TimelineEvent.vue'
import axios from 'axios'
export default {
name: 'StoryDetailView',
components: {
TaskListEntry
TaskListEntry,
TimelineEvent
},
data () {
return {
@ -183,4 +183,20 @@ h2 {
}
}
}
.events {
position: relative;
margin-bottom: 100px;
&::before {
position: absolute;
content: " ";
width: 4px;
height: calc(100% - 20px);
left: 21px;
top: 20px;
background-color: #ddd;
z-index: -1;
}
}
</style>