fix: added task listview and dialog
This commit is contained in:
parent
e06d110cbc
commit
531e5710b8
@ -6,4 +6,55 @@ from frappe.model.document import Document
|
|||||||
|
|
||||||
|
|
||||||
class CRMTask(Document):
|
class CRMTask(Document):
|
||||||
pass
|
@staticmethod
|
||||||
|
def default_list_data():
|
||||||
|
columns = [
|
||||||
|
{
|
||||||
|
'label': 'Title',
|
||||||
|
'type': 'Data',
|
||||||
|
'key': 'title',
|
||||||
|
'width': '12rem',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': 'Status',
|
||||||
|
'type': 'Select',
|
||||||
|
'key': 'status',
|
||||||
|
'width': '8rem',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': 'Priority',
|
||||||
|
'type': 'Select',
|
||||||
|
'key': 'priority',
|
||||||
|
'width': '8rem',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': 'Due Date',
|
||||||
|
'type': 'Date',
|
||||||
|
'key': 'due_date',
|
||||||
|
'width': '8rem',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': 'Assigned To',
|
||||||
|
'type': 'Link',
|
||||||
|
'key': 'assigned_to',
|
||||||
|
'width': '10rem',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': 'Last Modified',
|
||||||
|
'type': 'Datetime',
|
||||||
|
'key': 'modified',
|
||||||
|
'width': '8rem',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
rows = [
|
||||||
|
"name",
|
||||||
|
"title",
|
||||||
|
"description",
|
||||||
|
"assigned_to",
|
||||||
|
"due_date",
|
||||||
|
"status",
|
||||||
|
"priority",
|
||||||
|
"modified",
|
||||||
|
]
|
||||||
|
return {'columns': columns, 'rows': rows}
|
||||||
|
|||||||
164
frontend/src/components/ListViews/TasksListView.vue
Normal file
164
frontend/src/components/ListViews/TasksListView.vue
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
<template>
|
||||||
|
<ListView
|
||||||
|
:columns="columns"
|
||||||
|
:rows="rows"
|
||||||
|
:options="{
|
||||||
|
onRowClick: (row) => emit('showTask', row.name),
|
||||||
|
selectable: options.selectable,
|
||||||
|
}"
|
||||||
|
row-key="name"
|
||||||
|
>
|
||||||
|
<ListHeader class="mx-5" />
|
||||||
|
<ListRows id="list-rows">
|
||||||
|
<ListRow
|
||||||
|
class="mx-5"
|
||||||
|
v-for="row in rows"
|
||||||
|
:key="row.name"
|
||||||
|
v-slot="{ column, item }"
|
||||||
|
:row="row"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="column.key === 'due_date'"
|
||||||
|
class="flex items-center gap-2 text-base"
|
||||||
|
>
|
||||||
|
<CalendarIcon />
|
||||||
|
<div v-if="item">
|
||||||
|
<Tooltip :text="dateFormat(item, 'ddd, MMM D, YYYY')">
|
||||||
|
{{ dateFormat(item, 'D MMM') }}
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ListRowItem v-else :item="item">
|
||||||
|
<template #prefix>
|
||||||
|
<div v-if="column.key === 'status'">
|
||||||
|
<TaskStatusIcon :status="item" />
|
||||||
|
</div>
|
||||||
|
<div v-else-if="column.key === 'priority'">
|
||||||
|
<TaskPriorityIcon :priority="item" />
|
||||||
|
</div>
|
||||||
|
<div v-else-if="column.key === 'assigned_to'">
|
||||||
|
<Avatar
|
||||||
|
v-if="item.full_name"
|
||||||
|
class="flex items-center"
|
||||||
|
:image="item.user_image"
|
||||||
|
:label="item.full_name"
|
||||||
|
size="sm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div
|
||||||
|
v-if="['modified', 'creation'].includes(column.key)"
|
||||||
|
class="truncate text-base"
|
||||||
|
>
|
||||||
|
{{ item.timeAgo }}
|
||||||
|
</div>
|
||||||
|
<div v-else-if="column.type === 'Check'">
|
||||||
|
<FormControl
|
||||||
|
type="checkbox"
|
||||||
|
:modelValue="item"
|
||||||
|
:disabled="true"
|
||||||
|
class="text-gray-900"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</ListRowItem>
|
||||||
|
</ListRow>
|
||||||
|
</ListRows>
|
||||||
|
<ListSelectBanner>
|
||||||
|
<template #actions="{ selections, unselectAll }">
|
||||||
|
<Button
|
||||||
|
theme="red"
|
||||||
|
variant="subtle"
|
||||||
|
label="Delete"
|
||||||
|
@click="deleteTask(selections, unselectAll)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</ListSelectBanner>
|
||||||
|
</ListView>
|
||||||
|
<ListFooter
|
||||||
|
class="border-t px-5 py-2"
|
||||||
|
v-model="pageLengthCount"
|
||||||
|
:options="{
|
||||||
|
rowCount: options.rowCount,
|
||||||
|
totalCount: options.totalCount,
|
||||||
|
}"
|
||||||
|
@loadMore="emit('loadMore')"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<script setup>
|
||||||
|
import TaskStatusIcon from '@/components/Icons/TaskStatusIcon.vue'
|
||||||
|
import TaskPriorityIcon from '@/components/Icons/TaskPriorityIcon.vue'
|
||||||
|
import CalendarIcon from '@/components/Icons/CalendarIcon.vue'
|
||||||
|
import { dateFormat } from '@/utils'
|
||||||
|
import { globalStore } from '@/stores/global'
|
||||||
|
import {
|
||||||
|
Avatar,
|
||||||
|
ListView,
|
||||||
|
ListHeader,
|
||||||
|
ListRows,
|
||||||
|
ListRow,
|
||||||
|
ListSelectBanner,
|
||||||
|
ListRowItem,
|
||||||
|
ListFooter,
|
||||||
|
call,
|
||||||
|
Tooltip,
|
||||||
|
} from 'frappe-ui'
|
||||||
|
import { defineModel } from 'vue'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
rows: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
columns: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({
|
||||||
|
selectable: true,
|
||||||
|
totalCount: 0,
|
||||||
|
rowCount: 0,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits(['loadMore', 'showTask', 'reload'])
|
||||||
|
|
||||||
|
const pageLengthCount = defineModel()
|
||||||
|
|
||||||
|
const { $dialog } = globalStore()
|
||||||
|
|
||||||
|
function deleteTask(selections, unselectAll) {
|
||||||
|
let title = 'Delete task'
|
||||||
|
let message = 'Are you sure you want to delete this task?'
|
||||||
|
|
||||||
|
if (selections.size > 1) {
|
||||||
|
title = 'Delete tasks'
|
||||||
|
message = 'Are you sure you want to delete these tasks?'
|
||||||
|
}
|
||||||
|
|
||||||
|
$dialog({
|
||||||
|
title: title,
|
||||||
|
message: message,
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
label: 'Delete',
|
||||||
|
theme: 'red',
|
||||||
|
variant: 'solid',
|
||||||
|
async onClick(close) {
|
||||||
|
for (const selection of selections) {
|
||||||
|
await call('frappe.client.delete', {
|
||||||
|
doctype: 'CRM Task',
|
||||||
|
name: selection,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
close()
|
||||||
|
unselectAll()
|
||||||
|
emit('reload')
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
106
frontend/src/pages/Tasks.vue
Normal file
106
frontend/src/pages/Tasks.vue
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
<template>
|
||||||
|
<LayoutHeader>
|
||||||
|
<template #left-header>
|
||||||
|
<Breadcrumbs :items="breadcrumbs" />
|
||||||
|
</template>
|
||||||
|
<template #right-header>
|
||||||
|
<Button variant="solid" label="Create" @click="showTaskModal = true">
|
||||||
|
<template #prefix><FeatherIcon name="plus" class="h-4" /></template>
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
|
</LayoutHeader>
|
||||||
|
<ViewControls
|
||||||
|
v-model="tasks"
|
||||||
|
v-model:loadMore="loadMore"
|
||||||
|
doctype="CRM Task"
|
||||||
|
/>
|
||||||
|
<TasksListView
|
||||||
|
v-if="tasks.data && rows.length"
|
||||||
|
v-model="tasks.data.page_length_count"
|
||||||
|
:rows="rows"
|
||||||
|
:columns="tasks.data.columns"
|
||||||
|
:options="{
|
||||||
|
rowCount: tasks.data.row_count,
|
||||||
|
totalCount: tasks.data.total_count,
|
||||||
|
}"
|
||||||
|
@loadMore="() => loadMore++"
|
||||||
|
@showTask="showTask"
|
||||||
|
@reload="() => tasks.reload()"
|
||||||
|
/>
|
||||||
|
<div v-else-if="tasks.data" class="flex h-full items-center justify-center">
|
||||||
|
<div
|
||||||
|
class="flex flex-col items-center gap-3 text-xl font-medium text-gray-500"
|
||||||
|
>
|
||||||
|
<EmailIcon class="h-10 w-10" />
|
||||||
|
<span>No Tasks Found</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<TaskModal v-model="showTaskModal" v-model:reloadTasks="tasks" :task="task" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import EmailIcon from '@/components/Icons/EmailIcon.vue'
|
||||||
|
import LayoutHeader from '@/components/LayoutHeader.vue'
|
||||||
|
import ViewControls from '@/components/ViewControls.vue'
|
||||||
|
import TasksListView from '@/components/ListViews/TasksListView.vue'
|
||||||
|
import TaskModal from '@/components/Modals/TaskModal.vue'
|
||||||
|
import { usersStore } from '@/stores/users'
|
||||||
|
import { dateFormat, dateTooltipFormat, timeAgo } from '@/utils'
|
||||||
|
import { Breadcrumbs } from 'frappe-ui'
|
||||||
|
import { computed, ref } from 'vue'
|
||||||
|
|
||||||
|
const breadcrumbs = [{ label: 'Tasks', route: { name: 'Tasks' } }]
|
||||||
|
|
||||||
|
const { getUser } = usersStore()
|
||||||
|
|
||||||
|
// tasks data is loaded in the ViewControls component
|
||||||
|
const tasks = ref({})
|
||||||
|
const loadMore = ref(1)
|
||||||
|
|
||||||
|
const rows = computed(() => {
|
||||||
|
if (!tasks.value?.data?.data) return []
|
||||||
|
return tasks.value?.data.data.map((task) => {
|
||||||
|
let _rows = {}
|
||||||
|
tasks.value?.data.rows.forEach((row) => {
|
||||||
|
_rows[row] = task[row]
|
||||||
|
|
||||||
|
if (['modified', 'creation'].includes(row)) {
|
||||||
|
_rows[row] = {
|
||||||
|
label: dateFormat(task[row], dateTooltipFormat),
|
||||||
|
timeAgo: timeAgo(task[row]),
|
||||||
|
}
|
||||||
|
} else if (row == 'assigned_to') {
|
||||||
|
_rows[row] = {
|
||||||
|
label: task.assigned_to && getUser(task.assigned_to).full_name,
|
||||||
|
...(task.assigned_to && getUser(task.assigned_to)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return _rows
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const showTaskModal = ref(false)
|
||||||
|
|
||||||
|
const task = ref({
|
||||||
|
title: '',
|
||||||
|
description: '',
|
||||||
|
assigned_to: '',
|
||||||
|
due_date: '',
|
||||||
|
status: 'Backlog',
|
||||||
|
priority: 'Low',
|
||||||
|
})
|
||||||
|
|
||||||
|
function showTask(name) {
|
||||||
|
let t = rows.value?.find((row) => row.name === name)
|
||||||
|
task.value = {
|
||||||
|
title: t.title,
|
||||||
|
description: t.description,
|
||||||
|
assigned_to: t.assigned_to?.email || '',
|
||||||
|
due_date: t.due_date,
|
||||||
|
status: t.status,
|
||||||
|
priority: t.priority,
|
||||||
|
}
|
||||||
|
showTaskModal.value = true
|
||||||
|
}
|
||||||
|
</script>
|
||||||
Loading…
x
Reference in New Issue
Block a user