implement the start and next to a record
This commit is contained in:
@@ -1,5 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useTaskStore } from '@/modules/task/stores/useTask.store'
|
import { useTaskStore } from '@/modules/task/stores/useTask.store'
|
||||||
|
import { formatDate } from '@/shared/format-date'
|
||||||
|
import { toISODate } from '@/shared/types/date'
|
||||||
|
import { computed } from 'vue'
|
||||||
import { useTaskRecordStore } from '../stores/useTaskRecordStore'
|
import { useTaskRecordStore } from '../stores/useTaskRecordStore'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
@@ -10,23 +13,104 @@ const props = defineProps<{
|
|||||||
const taskStore = useTaskStore()
|
const taskStore = useTaskStore()
|
||||||
const recordStore = useTaskRecordStore()
|
const recordStore = useTaskRecordStore()
|
||||||
|
|
||||||
const task = taskStore.getTask(props.taskId)
|
const task = computed(() => taskStore.getTask(props.taskId))
|
||||||
|
|
||||||
const record = recordStore.createAndRetriveTaskRecord(
|
const record = computed(() =>
|
||||||
props.taskId,
|
recordStore.createAndRetrieveTaskRecord(props.taskId, props.recordId)
|
||||||
props.recordId
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const getNextStepId = () => {
|
||||||
|
if (!task.value) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!recordStore.currentStepId) {
|
||||||
|
return task.value.steps[0].id
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentStepIndex = task.value.steps.findIndex(
|
||||||
|
(step) => step.id === recordStore.currentStepId
|
||||||
|
)
|
||||||
|
|
||||||
|
const canHaveNextIndex =
|
||||||
|
currentStepIndex >= 0 && currentStepIndex < task.value.steps.length - 1
|
||||||
|
|
||||||
|
if (canHaveNextIndex) {
|
||||||
|
return task.value.steps[currentStepIndex + 1].id
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const startRecording = () => {
|
||||||
|
if (!task.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
recordStore.startStepRecord({
|
||||||
|
recordId: props.recordId,
|
||||||
|
stepId: task.value.steps[0].id,
|
||||||
|
start: toISODate(new Date())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const nextStep = () => {
|
||||||
|
if (!task.value || !recordStore.currentStepId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
recordStore.nextStepRecord({
|
||||||
|
recordId: record.value.id,
|
||||||
|
currentStepId: recordStore.currentStepId,
|
||||||
|
nextStepId: getNextStepId(),
|
||||||
|
tick: toISODate(new Date())
|
||||||
|
})
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="task-record">
|
<div class="task-record" v-if="task">
|
||||||
<pre>{{ task }}</pre>
|
<h1>Task: {{ task.title }}</h1>
|
||||||
<pre>{{ record }}</pre>
|
<h2>start time: {{ formatDate(record.start) }}</h2>
|
||||||
|
<button v-if="!record.hasStepRecords" @click="startRecording">start</button>
|
||||||
|
<button v-else @click="nextStep">next</button>
|
||||||
|
<button @click="recordStore.$reset">reset</button>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>#</th>
|
||||||
|
<th>Task</th>
|
||||||
|
<th>estimation</th>
|
||||||
|
<th>actual</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr
|
||||||
|
v-for="(step, key) in task.steps"
|
||||||
|
:key="step.id"
|
||||||
|
:class="{ current: recordStore.currentStepId === step.id }"
|
||||||
|
>
|
||||||
|
<td>{{ key + 1 }}</td>
|
||||||
|
<td>{{ step.title }}</td>
|
||||||
|
<td class="estimation">{{ step.estimation }} minutes</td>
|
||||||
|
<td v-if="record.stepRecords[step.id]">
|
||||||
|
{{ record.stepRecords[step.id] }} minutes
|
||||||
|
</td>
|
||||||
|
<td v-else>NA</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.task-record {
|
.task-record {
|
||||||
display: flex;
|
.current {
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.estimation {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ export class TaskRecord implements Recordable {
|
|||||||
return Math.round(durationMilliseconds / (1000 * 60))
|
return Math.round(durationMilliseconds / (1000 * 60))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get hasStepRecords() {
|
||||||
|
return Object.values(this.stepRecords).length > 0
|
||||||
|
}
|
||||||
|
|
||||||
public static fromRecordable(recordable: Recordable) {
|
public static fromRecordable(recordable: Recordable) {
|
||||||
const taskRecord = new TaskRecord(recordable.id, recordable.taskId)
|
const taskRecord = new TaskRecord(recordable.id, recordable.taskId)
|
||||||
|
|
||||||
|
|||||||
@@ -1,41 +1,25 @@
|
|||||||
import type { ISODate } from '@/shared/types/date'
|
import { toISODate, type ISODate } from '@/shared/types/date'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import type { Recordable } from '../interfaces/recordable'
|
import type { Recordable } from '../interfaces/recordable'
|
||||||
import type { StepRecordable } from '../interfaces/step-recordable'
|
import type { StepRecordable } from '../interfaces/step-recordable'
|
||||||
import { TaskRecord } from '../models/task-record'
|
import { TaskRecord } from '../models/task-record'
|
||||||
|
|
||||||
type RecordId = string
|
|
||||||
|
|
||||||
export interface TaskRecordStoreState {
|
export interface TaskRecordStoreState {
|
||||||
currentStepId: string | null
|
currentStepId: string | null
|
||||||
records: { [recordId: string]: Recordable }
|
records: { [recordId: string]: Recordable }
|
||||||
taskRecordMaps: { [taskId: string]: RecordId[] }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useTaskRecordStore = defineStore('task-record-store', {
|
export const useTaskRecordStore = defineStore('task-record-store', {
|
||||||
persist: true,
|
persist: true,
|
||||||
state: (): TaskRecordStoreState => ({
|
state: (): TaskRecordStoreState => ({
|
||||||
currentStepId: null,
|
currentStepId: null,
|
||||||
records: {},
|
records: {}
|
||||||
taskRecordMaps: {}
|
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
addRecord(taskRecord: TaskRecord) {
|
addRecord(taskRecord: TaskRecord) {
|
||||||
if (!this.taskRecordMaps[taskRecord.taskId]) {
|
|
||||||
this.taskRecordMaps[taskRecord.taskId] = []
|
|
||||||
}
|
|
||||||
|
|
||||||
this.taskRecordMaps[taskRecord.taskId].push(taskRecord.id)
|
|
||||||
|
|
||||||
this.records[taskRecord.id] = taskRecord
|
this.records[taskRecord.id] = taskRecord
|
||||||
},
|
},
|
||||||
removeRecord(recordId: string) {
|
removeRecord(recordId: string) {
|
||||||
for (const taskId in this.taskRecordMaps) {
|
|
||||||
this.taskRecordMaps[taskId] = this.taskRecordMaps[taskId].filter(
|
|
||||||
(rId) => rId !== recordId
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
delete this.records[recordId]
|
delete this.records[recordId]
|
||||||
},
|
},
|
||||||
startStepRecord(params: {
|
startStepRecord(params: {
|
||||||
@@ -49,11 +33,22 @@ export const useTaskRecordStore = defineStore('task-record-store', {
|
|||||||
record.start = params.start
|
record.start = params.start
|
||||||
}
|
}
|
||||||
|
|
||||||
this.records[params.recordId].stepRecords[params.stepId] = {
|
this.$patch({
|
||||||
problems: [],
|
records: {
|
||||||
start: params.start
|
...this.records,
|
||||||
}
|
[params.recordId]: {
|
||||||
this.currentStepId = params.stepId
|
...record,
|
||||||
|
stepRecords: {
|
||||||
|
...record.stepRecords,
|
||||||
|
[params.stepId]: {
|
||||||
|
problems: [],
|
||||||
|
start: params.start
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
currentStepId: params.stepId
|
||||||
|
})
|
||||||
},
|
},
|
||||||
endStepRecord(params: { recordId: string; stepId: string; end: ISODate }) {
|
endStepRecord(params: { recordId: string; stepId: string; end: ISODate }) {
|
||||||
const stepRecord =
|
const stepRecord =
|
||||||
@@ -68,7 +63,7 @@ export const useTaskRecordStore = defineStore('task-record-store', {
|
|||||||
nextStepRecord(params: {
|
nextStepRecord(params: {
|
||||||
recordId: string
|
recordId: string
|
||||||
currentStepId: string
|
currentStepId: string
|
||||||
nextStepId: string
|
nextStepId: string | null
|
||||||
tick: ISODate
|
tick: ISODate
|
||||||
}) {
|
}) {
|
||||||
this.endStepRecord({
|
this.endStepRecord({
|
||||||
@@ -76,13 +71,20 @@ export const useTaskRecordStore = defineStore('task-record-store', {
|
|||||||
stepId: params.currentStepId,
|
stepId: params.currentStepId,
|
||||||
end: params.tick
|
end: params.tick
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (!params.nextStepId) {
|
||||||
|
this.endRecord(params.recordId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
this.startStepRecord({
|
this.startStepRecord({
|
||||||
recordId: params.recordId,
|
recordId: params.recordId,
|
||||||
stepId: params.nextStepId,
|
stepId: params.nextStepId,
|
||||||
start: params.tick
|
start: params.tick
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
endRecord() {
|
endRecord(recordId: string) {
|
||||||
|
this.records[recordId].end = toISODate(new Date())
|
||||||
this.currentStepId = null
|
this.currentStepId = null
|
||||||
},
|
},
|
||||||
addProblemToStepRecord(recordId: string, stepId: string, problem: string) {
|
addProblemToStepRecord(recordId: string, stepId: string, problem: string) {
|
||||||
@@ -98,22 +100,19 @@ export const useTaskRecordStore = defineStore('task-record-store', {
|
|||||||
getters: {
|
getters: {
|
||||||
getTaskRecords() {
|
getTaskRecords() {
|
||||||
return (taskId: string): TaskRecord[] =>
|
return (taskId: string): TaskRecord[] =>
|
||||||
this.taskRecordMaps?.[taskId]?.map((recordId) =>
|
Object.values(this.records)
|
||||||
TaskRecord.fromRecordable(this.records[recordId])
|
.filter((record) => record.taskId === taskId)
|
||||||
) ?? []
|
.map((record) => TaskRecord.fromRecordable(record))
|
||||||
},
|
},
|
||||||
createAndRetriveTaskRecord() {
|
createAndRetrieveTaskRecord() {
|
||||||
return (taskId: string, recordId: string): TaskRecord => {
|
return (taskId: string, recordId: string): TaskRecord => {
|
||||||
const hasTaskRecord = this.taskRecordMaps[taskId]?.some(
|
const hasTaskRecord = !!this.records[recordId]
|
||||||
(rId) => rId === recordId
|
|
||||||
)
|
|
||||||
|
|
||||||
if (hasTaskRecord) {
|
if (hasTaskRecord) {
|
||||||
return TaskRecord.fromRecordable(this.records[recordId])
|
return TaskRecord.fromRecordable(this.records[recordId])
|
||||||
}
|
}
|
||||||
|
|
||||||
const newTaskRecord = new TaskRecord(recordId, taskId)
|
const newTaskRecord = new TaskRecord(recordId, taskId)
|
||||||
this.taskRecordMaps[taskId]?.push(recordId)
|
|
||||||
this.records[recordId] = newTaskRecord
|
this.records[recordId] = newTaskRecord
|
||||||
|
|
||||||
return newTaskRecord
|
return newTaskRecord
|
||||||
|
|||||||
Reference in New Issue
Block a user