share with a png file
This commit is contained in:
@@ -15,16 +15,10 @@ const { duration } = useTaskRecordMetadata(taskRecord)
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="task-record-preview">
|
<div class="task-record-preview">
|
||||||
<router-link
|
<div v-if="taskRecord !== null">
|
||||||
:to="{
|
Last record took {{ duration }} minutes.
|
||||||
name: 'record-view',
|
</div>
|
||||||
params: { taskId }
|
<div v-else>No record yet.</div>
|
||||||
}"
|
|
||||||
class="button is-primary is-light"
|
|
||||||
>start session</router-link
|
|
||||||
>
|
|
||||||
<div v-if="duration !== null">Last record took {{ duration }} minutes</div>
|
|
||||||
<div v-else>No record yet</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -1,45 +1,46 @@
|
|||||||
import { useTaskRecordStore } from '@/modules/record/stores/useTaskRecordStore'
|
|
||||||
import type { Task } from '@/modules/task/models/task'
|
import type { Task } from '@/modules/task/models/task'
|
||||||
import { formatDiffInMinutes, formatToShortDate } from '@/shared/format-date'
|
import { computed, ref, type ComputedRef } from 'vue'
|
||||||
import { toValue } from '@vueuse/core'
|
import { toBlob } from 'html-to-image'
|
||||||
import { ref, type ComputedRef } from 'vue'
|
|
||||||
|
|
||||||
export const useCopyRecord = (task: ComputedRef<Task | null>) => {
|
export const useCopyRecord = (task: ComputedRef<Task | null>) => {
|
||||||
const recordStore = useTaskRecordStore()
|
const canShareTask =
|
||||||
|
!!navigator && !!navigator.clipboard && !!navigator.clipboard.writeText
|
||||||
const canShareTask = !!navigator.clipboard
|
|
||||||
const taskCopied = ref(false)
|
const taskCopied = ref(false)
|
||||||
|
const domId = computed(() => `task-${task.value?.id}`)
|
||||||
|
|
||||||
const shareTask = async () => {
|
const shareTask = async () => {
|
||||||
const t = toValue(task)
|
const node = document.getElementById(domId.value)
|
||||||
|
|
||||||
if (!t) {
|
if (!node) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const record = recordStore.getTaskRecord(t.id)
|
const blob = await toBlob(node, {
|
||||||
|
style: {
|
||||||
let clipboardText = `${t.title};${formatToShortDate(t.date)}\n
|
margin: 'inherit'
|
||||||
Step;Estimation;Duration\n`
|
}
|
||||||
|
|
||||||
t.steps.forEach((step) => {
|
|
||||||
const recordStep = record?.stepRecords[step.id]
|
|
||||||
const duration =
|
|
||||||
recordStep && recordStep.end
|
|
||||||
? formatDiffInMinutes(recordStep.start, recordStep.end)
|
|
||||||
: '-'
|
|
||||||
const analyze = duration <= step.estimation ? '✅' : '⚠️'
|
|
||||||
|
|
||||||
clipboardText += `"${analyze} ${step.title}";${step.estimation};${duration}\n`
|
|
||||||
})
|
})
|
||||||
|
|
||||||
await navigator.clipboard.writeText(clipboardText)
|
if (!blob) {
|
||||||
taskCopied.value = true
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.write([
|
||||||
|
new ClipboardItem({
|
||||||
|
[blob.type]: blob
|
||||||
|
})
|
||||||
|
])
|
||||||
|
|
||||||
|
taskCopied.value = true
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(error)
|
||||||
|
} finally {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
taskCopied.value = false
|
taskCopied.value = false
|
||||||
}, 2000)
|
}, 2000)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
canShareTask,
|
canShareTask,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { useCopyRecord } from '@/modules/record/hooks/useCopyRecord.hook'
|
|||||||
import { useTaskStore } from '@/modules/task/stores/useTask.store'
|
import { useTaskStore } from '@/modules/task/stores/useTask.store'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
import StepRecord from '@/modules/record/components/StepRecord.vue'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
id: string
|
id: string
|
||||||
@@ -31,47 +32,78 @@ const { canShareTask, taskCopied, shareTask } = useCopyRecord(task)
|
|||||||
<template>
|
<template>
|
||||||
<div class="task-view" v-if="task">
|
<div class="task-view" v-if="task">
|
||||||
<div class="buttons actions">
|
<div class="buttons actions">
|
||||||
<router-link :to="{
|
<router-link
|
||||||
|
:to="{
|
||||||
|
name: 'record-view',
|
||||||
|
params: { taskId: id }
|
||||||
|
}"
|
||||||
|
class="button is-primary is-light"
|
||||||
|
>start session</router-link
|
||||||
|
>
|
||||||
|
<router-link
|
||||||
|
:to="{
|
||||||
name: 'edit-task',
|
name: 'edit-task',
|
||||||
params: {
|
params: {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
}" class="button">
|
}"
|
||||||
|
class="button"
|
||||||
|
>
|
||||||
<img src="/icons/edit.svg" alt="edit task" />
|
<img src="/icons/edit.svg" alt="edit task" />
|
||||||
</router-link>
|
</router-link>
|
||||||
<button v-if="canShareTask" class="share-task button" @click="shareTask">
|
<button v-if="canShareTask" class="share-task button" @click="shareTask">
|
||||||
<img v-if="taskCopied" src="/icons/check.svg" alt="task copied!" />
|
<img v-if="taskCopied" src="/icons/check.svg" alt="task copied!" />
|
||||||
<img v-else src="/icons/share.svg" alt="share task" />
|
<img v-else src="/icons/share.svg" alt="share task" />
|
||||||
</button>
|
</button>
|
||||||
<router-link :to="{
|
<router-link
|
||||||
|
:to="{
|
||||||
name: 'duplicate-task',
|
name: 'duplicate-task',
|
||||||
params: {
|
params: {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
}" class="button">
|
}"
|
||||||
|
class="button"
|
||||||
|
>
|
||||||
<img src="/icons/copy.svg" alt="duplicate task" />
|
<img src="/icons/copy.svg" alt="duplicate task" />
|
||||||
</router-link>
|
</router-link>
|
||||||
<button class="delete-task button is-light is-danger" @click="deleteTask">
|
<button class="delete-task button is-light is-danger" @click="deleteTask">
|
||||||
<img src="/icons/trash.svg" alt="delete task" />
|
<img src="/icons/trash.svg" alt="delete task" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<a
|
||||||
|
v-if="task.link"
|
||||||
|
:href="task.link"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
class="button is-link"
|
||||||
|
>user story link</a
|
||||||
|
>
|
||||||
|
<div class="content" :id="`task-${id}`">
|
||||||
<h1 class="title">{{ task.title }}</h1>
|
<h1 class="title">{{ task.title }}</h1>
|
||||||
<h2 class="subtitle">
|
<h2 class="subtitle">
|
||||||
<estimation-time-arrival :estimation="task.totalEstimation" />
|
<estimation-time-arrival :estimation="task.totalEstimation" />
|
||||||
</h2>
|
</h2>
|
||||||
<a v-if="task.link" :href="task.link" target="_blank" rel="noopener noreferrer" class="button is-link">user story
|
|
||||||
link</a>
|
|
||||||
<task-record-preview :task-id="id" />
|
<task-record-preview :task-id="id" />
|
||||||
<hr />
|
<table class="table is-striped is-hoverable">
|
||||||
<div class="content">
|
<thead>
|
||||||
<ol>
|
<tr>
|
||||||
<li v-for="step in task.steps" :key="step.id">
|
<th>#</th>
|
||||||
<div class="step-item">
|
<th>status</th>
|
||||||
<span>{{ step.title }}</span>
|
<th>task</th>
|
||||||
<span class="tag">{{ step.estimation }} minutes</span>
|
<th>estimation</th>
|
||||||
</div>
|
<th>actual</th>
|
||||||
</li>
|
</tr>
|
||||||
</ol>
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<step-record
|
||||||
|
v-for="(step, key) in task.steps"
|
||||||
|
:task-id="id"
|
||||||
|
:key="step.id"
|
||||||
|
:step-id="step.id"
|
||||||
|
:step-number="key + 1"
|
||||||
|
/>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<task-not-found v-else />
|
<task-not-found v-else />
|
||||||
@@ -89,4 +121,11 @@ const { canShareTask, taskCopied, shareTask } = useCopyRecord(task)
|
|||||||
.actions {
|
.actions {
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
background-color: white;
|
||||||
|
max-width: 800px;
|
||||||
|
margin: auto;
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user