Files
mws.frontend.vue/src/views/tasks/TaskDetailDialog.vue
T

190 lines
5.2 KiB
Vue
Raw Normal View History

2026-08-11 23:17:04 +07:00
<template>
<Dialog
:visible="visible"
:header="task ? 'Edit Task' : 'New Task'"
:modal="true"
style="width: min(520px, 92vw)"
@update:visible="emit('update:visible', $event)"
>
<div class="field">
<label>Title</label>
<InputText v-model.trim="form.title" class="w-full" autofocus />
</div>
<div class="field">
<label>Description</label>
<Textarea v-model="form.description" rows="3" class="w-full" />
</div>
<div class="grid grid-cols-1 gap-3 sm:grid-cols-2">
<div class="field">
<label>Status</label>
<Select v-model="form.status" :options="statusOptions" optionLabel="label" optionValue="value" class="w-full" />
</div>
<div class="field">
<label>Priority</label>
<Select v-model="form.priority" :options="priorityOptions" optionLabel="label" optionValue="value" class="w-full" />
</div>
<div class="field">
<label>Assignee</label>
<Select
v-model="form.assigneeId"
:options="assigneeOptions"
optionLabel="label"
optionValue="value"
showClear
placeholder="Unassigned"
class="w-full"
/>
</div>
<div class="field">
<label>Due date</label>
<DatePicker v-model="form.dueDate" class="w-full" dateFormat="yy-mm-dd" showClear />
</div>
</div>
<div v-if="savingError" class="mb-2">
<Message severity="error" variant="simple">{{ savingError }}</Message>
</div>
<template #footer>
<div class="flex justify-between">
<Button
v-if="task && canDelete"
label="Delete"
icon="pi pi-trash"
severity="danger"
text
@click="onDelete"
/>
<div>
<Button label="Cancel" severity="secondary" text @click="emit('update:visible', false)" />
<Button v-if="canEdit" label="Save" :loading="saving" @click="onSave" />
</div>
</div>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { createTask, updateTask, deleteTask } from '../../services/modules'
import { errorMessage } from '../../services/api'
import type { Task, TaskPriority, TaskStatus } from '../../types'
const props = defineProps<{
visible: boolean
task: Task | null
projectId: string
members: { userId: string; displayName: string }[]
canEdit: boolean
canDelete: boolean
}>()
const emit = defineEmits<{
'update:visible': [value: boolean]
saved: []
deleted: []
}>()
const toast = useToast()
const saving = ref(false)
const savingError = ref('')
const statusOptions = [
{ label: 'To Do', value: 'Todo' as TaskStatus },
{ label: 'In Progress', value: 'InProgress' as TaskStatus },
{ label: 'Done', value: 'Done' as TaskStatus },
{ label: 'Cancelled', value: 'Cancelled' as TaskStatus },
]
const priorityOptions = [
{ label: 'Low', value: 'Low' as TaskPriority },
{ label: 'Medium', value: 'Medium' as TaskPriority },
{ label: 'High', value: 'High' as TaskPriority },
]
const assigneeOptions = computed(() => props.members.map((m) => ({ label: m.displayName, value: m.userId })))
const form = reactive({
title: '',
description: '',
status: 'Todo' as TaskStatus,
priority: 'Medium' as TaskPriority,
assigneeId: null as string | null,
dueDate: null as Date | null,
})
watch(
() => props.visible,
(visible) => {
if (visible) {
savingError.value = ''
if (props.task) {
form.title = props.task.title
form.description = props.task.description ?? ''
form.status = props.task.status
form.priority = props.task.priority
form.assigneeId = props.task.assigneeId
form.dueDate = props.task.dueDate ? new Date(props.task.dueDate) : null
} else {
form.title = ''
form.description = ''
form.status = 'Todo'
form.priority = 'Medium'
form.assigneeId = null
form.dueDate = null
}
}
},
)
async function onSave() {
if (!form.title.trim()) {
savingError.value = 'Title is required'
return
}
saving.value = true
savingError.value = ''
try {
const dueDate = form.dueDate ? form.dueDate.toISOString() : null
if (props.task) {
await updateTask(props.task.id, {
title: form.title.trim(),
description: form.description || null,
status: form.status,
priority: form.priority,
assigneeId: form.assigneeId,
dueDate,
})
} else {
await createTask(props.projectId, {
title: form.title.trim(),
description: form.description || null,
status: form.status,
priority: form.priority,
assigneeId: form.assigneeId,
dueDate,
})
}
toast.add({ severity: 'success', summary: 'Saved', life: 2000 })
emit('saved')
} catch (e) {
savingError.value = errorMessage(e)
} finally {
saving.value = false
}
}
async function onDelete() {
if (!props.task) return
saving.value = true
try {
await deleteTask(props.task.id)
toast.add({ severity: 'success', summary: 'Task deleted', life: 2000 })
emit('deleted')
} catch (e) {
savingError.value = errorMessage(e)
} finally {
saving.value = false
}
}
</script>